<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MySQL &#8211; Roy Duineveld</title>
	<atom:link href="https://royduineveld.nl/tag/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>https://royduineveld.nl</link>
	<description>Waarom moeilijk doen als het makkelijk kan?</description>
	<lastBuildDate>Tue, 27 Jan 2026 07:48:00 +0000</lastBuildDate>
	<language>nl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.26</generator>
	<item>
		<title>[EN] Magento 1 cacheleak exploit</title>
		<link>https://royduineveld.nl/magento-cacheleak-exploit/</link>
		<comments>https://royduineveld.nl/magento-cacheleak-exploit/#respond</comments>
		<pubDate>Mon, 09 Jan 2017 17:06:45 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=998</guid>
		<description><![CDATA[<p>In the last two years Magento did release a lot of patches for (security) issues with Magento 1. If you apply those patches shortly after the release you&#8217;re pretty save. But...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/magento-cacheleak-exploit/">[EN] Magento 1 cacheleak exploit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>In the last two years Magento did release a lot of patches for (security) issues with Magento 1. If you apply those patches shortly after the release you&#8217;re pretty save. But your server configuration is important too! For example when you switch the Apache webserver for Nginx, <code>.htaccess</code> files don&#8217;t work anymore so you&#8217;ve to make sure you&#8217;ve configured it safely.</p>
<p>You can find <code>.htaccess</code> files across multiple folders with a default Magento 1 installation, not only in the root. For example the <code>.htaccess</code> file in the <code>/var</code> directory blocks access to all files in there. Don&#8217;t forget to block access to that directory in your Nginx configuration too! If you don&#8217;t block access people can access log files and even cached files. You may think that&#8217;s not dangerous but it is, with this exploit you can get the MySQL database credentials.</p>
<ol>
<li>First, check if a website is vulnerable at <a href="https://magereport.com" target="_blank">magereport.com</a></li>
<li>Try to access the <code>resource_config.json</code> file by visiting <code>http://website.com/var/resource_config.json</code></li>
<li>Copy and modify the media directory path from the <code>resource_config.json</code> file to something like: <code>/home/users/username/website.com/app/etc</code> (make sure it points to <code>/app/etc</code>)</li>
<li>Create a MD5 hash from that path, for example with <a href="http://www.md5.cz/" target="_blank">http://www.md5.cz/</a> or generate it from the terminal: <code>php -r "echo md5('PATH');"</code> en replace the path, that will generate something like: 68095313d2b99db25e7ebcd5bc8d9642</li>
<li>Use the first 3 characters from that hash, with my example that will be 680</li>
<li>Visit <code>http://website.com/var/cache/mage--2/mage---XXX_CONFIG_GLOBAL</code> and replace the XXX with those 3 characters</li>
<li>Now you can search in the global configuration, you can find among other things the MySQL credentials here</li>
<li>Try to connect to the database remotely or search for a PhpMyAdmin or Adminer script</li>
</ol>
<p>From that point the possibilities are endless as I&#8217;ve written before in my <a href="https://royduineveld.nl/hacking-public-git-repositories/">Hacking public GIT repositories</a> post, from defacing to change payment provider credentials and further.</p>
<p>Why now publish this exploit? I&#8217;ve &#8220;created&#8221; it in 2015 but back then a lot of webshops where vulnerable. Meanwhile the percentage is pretty low so I thought; let&#8217;s share it with the world.</p>
<blockquote><p>Do you need a security audit for your webshop? <a href="https://royduineveld.nl/contact/">Contact me</a>!</p></blockquote>
<p>&nbsp;</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/magento-cacheleak-exploit/">[EN] Magento 1 cacheleak exploit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/magento-cacheleak-exploit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[EN] Backup your server with AutoMySQLBackup and Duplicity</title>
		<link>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/</link>
		<comments>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/#comments</comments>
		<pubDate>Thu, 15 Dec 2016 08:38:31 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Serverpilot]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=950</guid>
		<description><![CDATA[<p>You&#8217;ve setup your server (for example with Serverpilot or Laravel Forge on Digital Ocean), but have you thought about backups? If you&#8217;re using MySQL it would be nice to have daily backups...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/">[EN] Backup your server with AutoMySQLBackup and Duplicity</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>You&#8217;ve setup your server (for example with <a href="https://royduineveld.nl/laravel-forge-vs-serverpilot/">Serverpilot or Laravel Forge</a> on <a href="https://m.do.co/c/7c917c6ea074" target="_blank" rel="noopener">Digital Ocean</a>), but have you thought about backups? If you&#8217;re using MySQL it would be nice to have daily backups right? And what if your server crashes? Maybe an off-site backup of everything? If you&#8217;re thinking &#8220;but I&#8217;ve checked the backup option with my droplet on <a href="https://m.do.co/c/7c917c6ea074" target="_blank" rel="noopener">Digital Ocean</a>&#8221; right now, don&#8217;t rely on that! There are some horror stories about corrupt droplets and those backups are created weekly!</p>
<h2>AutoMySQLBackup</h2>
<p>This tool is pretty simple and can be installed with: <code>sudo apt-get install automysqlbackup</code> You&#8217;re done! Backups will be made daily, automatically of all your databases and will be stored in <code>/var/lib/automysqlbackup</code> If you&#8217;d like to manually run it: <code>sudo automysqlbackup</code></p>
<p>Restoring from one of those backups is as easy as extracting and importing the <code>.sql</code> file with: <code>mysql -u username -p databasename &lt; databasefile.sql</code>, provide your password when asked and your done.</p>
<h2>Duplicity</h2>
<p>You could copy a lot of directories from your server daily with SCP to another server, or first zip them but then you&#8217;ve to create multiple cronjobs to handle all of this. And what about old backups and thought about disk space on the backup server? Duplicity can take care of all of this. Don&#8217;t have a backup server? Duplicity supports a lot of storage platforms and transfer protocols like: Amazon S3, Backblaze, Dropbox, Google Drive, SSH/SCP, WebDav, etc. and it&#8217;s creating diff files instead of doing complete backups everyday to save diskspace. Let&#8217;s install it with: <code>sudo apt-get install duplicity</code></p>
<h3>Full backups</h3>
<p>Let&#8217;s start with a full backup. I&#8217;ve got a <a href="https://www.transip.nl/stack/" target="_blank" rel="noopener">TransIP STACK</a> account, it&#8217;s a Dutch hosting provider which gives 1TB of free storage which you can access with WebDav so I store my server backups there. Also my webserver is managed by Serverpilot so my directories I&#8217;d like to backup are:</p>
<ul>
<li><code>/srv</code>, all my websites, applications and logs are stored here</li>
<li><code>/var/lib/automysqlbackup</code>, all daily, weekly and monthly backups of all my MySQL databases</li>
</ul>
<p>I&#8217;m not going to use any encryption in this example so to create a full backup with Duplicity from the above directories to my TransIP STACK storage:</p>
<pre class="brush: plain; title: ; notranslate">
sudo duplicity full 
--ssl-no-check-certificate 
--no-encryption 
--include /srv 
--include /var/lib/automysqlbackup 
--exclude '**' 
/ 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups
</pre>
<p>Create a monthly cronjob (as <code>root</code> user else you can&#8217;t access the AutoMySQLBackup files) from it and you&#8217;re done!</p>
<h3>Incremental backups</h3>
<p>When you&#8217;ve created your first full backup, Duplicity can create incremental backups with the full backup as reference. This means: when a full backup is ready, Duplicity will compare everything with that backup and creates diff files from the changes. And yes Duplicity is smart enough to restore backups from it, you don&#8217;t have to run multiple diff files over the full backups or something. Setting up a incremental backup is as easy as replacing <code>full</code> with <code>incremental</code> in the example above. Create a daily cronjob, done!</p>
<h3>Removing old backups</h3>
<p>Now you&#8217;ve setup 2 cronjobs, but it keeps running and won&#8217;t delete anything. I&#8217;d like to keep 3 months of backups, this can be done with this command:</p>
<pre class="brush: plain; title: ; notranslate">
duplicity remove-all-but-n-full 3 
--ssl-no-check-certificate 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups
--force
</pre>
<h3>Restoring backups</h3>
<p>But what if something goes wrong and you&#8217;d like to restore a backup? You can use the <code>restore</code> command:</p>
<pre class="brush: plain; title: ; notranslate">
duplicity restore 
--ssl-no-check-certificate 
--no-encryption 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups .
</pre>
<p>Which will restore the latest backup to the directory your currently in. You can even restore one file with the <code>--file-to-restore</code> parameter or choose with <code>--time</code> from which backup you&#8217;d like to restore. If you want to know which backups you can restore, run the <code>collection-status</code> command.</p>
<h2>How I&#8217;ve setup my backups</h2>
<p>AutoMySQLBackup is installed and running and with <code>crontab -e</code> as my <code>root</code> user I&#8217;ve setup my Duplicity cronjobs:</p>
<pre class="brush: plain; title: ; notranslate">
0 1 * * * duplicity --full-if-older-than 1M --ssl-no-check-certificate --no-encryption --include /srv --include /var/lib/automysqlbackup --exclude '**' / webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups &gt;&gt; /dev/null
0 5 1 * * duplicity remove-all-but-n-full 3 --ssl-no-check-certificate webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups --force &gt;&gt; /dev/null
</pre>
<p>A little bit different from what I&#8217;ve said earlier. By default the <code>duplicity</code> command creates a full backup and when there is one already it creates an incremental one. With <code>--full-if-older-than</code> we can specify when a new full backup should be made. This method is better than creating two cronjobs; one for a full backup the first of the month and the other one incremental the other days. What if the full backup fails? We&#8217;re getting two months of incremental backups, etc.</p>
<p>Do you want to know more? Run the <code>man duplicity</code> command for the manual or check the <a href="http://duplicity.nongnu.org/duplicity.1.html" target="_blank" rel="noopener">duplicity manual online</a>.</p>
<blockquote><p>And don&#8217;t forget: backup, backup, backup!!!</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/">[EN] Backup your server with AutoMySQLBackup and Duplicity</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>In MySQL sorteren op IN()</title>
		<link>https://royduineveld.nl/mysql-sorteren/</link>
		<comments>https://royduineveld.nl/mysql-sorteren/#respond</comments>
		<pubDate>Fri, 13 Sep 2013 18:36:28 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=523</guid>
		<description><![CDATA[<p>Recent had ik in PHP een array opgebouwd, aan de hand van deze array wilde ik gegevens ophalen uit de database, máár wél in de volgorde van mijn array. Een...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/mysql-sorteren/">In MySQL sorteren op IN()</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Recent had ik in PHP een array opgebouwd, aan de hand van deze array wilde ik gegevens ophalen uit de database, máár wél in de volgorde van mijn array. Een voorbeeld array:</p>
<pre class="brush: php; title: ; notranslate">
$array = [5,1,3,2,4];
</pre>
<p>Om uit een database gegevens te halen waarbij de gegevens in de array gelijk zijn aan het ID heb ik de <a title="Ga naar de MySQL documentatie over de IN functie" href="http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in" target="_blank">IN functie</a> gebruikt met een <a title="Ga naar de PHP documentatie over de implode functie" href="http://php.net/implode" target="_blank">implode</a>:</p>
<pre class="brush: php; title: ; notranslate">
$array = [5,1,3,2,4];
$query = 'SELECT * FROM tabel WHERE id IN('.implode(',',$array).')';
</pre>
<p>Maar de volgorde wordt op deze manier niet behouden, mocht ik dus door de resultaten &#8220;loopen&#8221; dan krijg ik niet eerst resultaat 5 gevolgd door 1, enzovoorts. Om dit voor elkaar te krijgen heb ik de <a title="Ga naar de MySQL documentatie over de FIELD functie" href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_field" target="_blank">FIELD functie</a> gebruikt met als resultaat:</p>
<pre class="brush: php; title: ; notranslate">
$array = [5,1,3,2,4];
$query = 'SELECT * FROM tabel WHERE id IN('.implode(',',$array).') ORDER BY FIELD (id,'.implode(',',$array).')';
</pre>
<p>Op deze manier blijft de volgorde bestaan en krijg ik dus wél 5 als eerste resultaat, gevolgd door 1, enzovoorts.</p>
<blockquote><p>MySQL heeft meer functies dan dat er vaak gedacht wordt, kijk bijvoorbeeld eens naar de <a title="Ga naar de MySQL documentatie over functies" href="http://dev.mysql.com/doc/refman/5.0/en/functions.html" target="_blank">documentatie over functies</a>.</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/mysql-sorteren/">In MySQL sorteren op IN()</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/mysql-sorteren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Klanten in kaart brengen met Google Geolocation en Maps API</title>
		<link>https://royduineveld.nl/klanten-in-kaart-brengen-met-google-geolocation-en-maps-api/</link>
		<comments>https://royduineveld.nl/klanten-in-kaart-brengen-met-google-geolocation-en-maps-api/#respond</comments>
		<pubDate>Thu, 20 Jun 2013 18:11:41 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Google Maps]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=485</guid>
		<description><![CDATA[<p>Wanneer je bijvoorbeeld een webshop hebt en je wilt weten waar je klanten wonen kan je natuurlijk door je klanten bestand bladeren en elke keer een streepje zetten bij een...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/klanten-in-kaart-brengen-met-google-geolocation-en-maps-api/">Klanten in kaart brengen met Google Geolocation en Maps API</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Wanneer je bijvoorbeeld een webshop hebt en je wilt weten waar je klanten wonen kan je natuurlijk door je klanten bestand bladeren en elke keer een streepje zetten bij een plaats. Leuker is om dit op de kaart te zien! Op die kaart kan uiteraard ingezoomd worden en staat er een icoontje op het adres van de klant met daarbij de naam.</p>
<p><a href="https://royduineveld.nl/wp-content/uploads/2013/06/google-maps-klanten-in-kaart-brengen.png"><img class="aligncenter size-medium wp-image-486" alt="Google Maps - Klanten in kaart brengen" src="https://royduineveld.nl/wp-content/uploads/2013/06/google-maps-klanten-in-kaart-brengen-300x291.png" width="300" height="291" srcset="https://royduineveld.nl/wp-content/uploads/2013/06/google-maps-klanten-in-kaart-brengen-300x291.png 300w, https://royduineveld.nl/wp-content/uploads/2013/06/google-maps-klanten-in-kaart-brengen.png 841w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2>Beginnen</h2>
<p>Om dit voor elkaar te krijgen kan je gebruik maken van Google API&#8217;s, waaronder Maps en Geolocation. Om hiervan gebruik te maken dien je eerst toegang te krijgen tot deze API&#8217;s. Ga hiervoor naar <a title="Ga naar Google API's Console" href="https://code.google.com/apis/console/">Google API&#8217;s Console</a>. Ga vervolgens naar &#8220;services&#8221; in het menu en zet de &#8220;Google Maps API v3&#8221; aan. Hierna ga je naar &#8220;API Access&#8221; in het menu en daar is de &#8220;API key&#8221; te vinden om toegang te krijgen. Eventueel kan je op &#8220;Edit allowed referers&#8221; klikken en hierna je domeinnaam invullen, op die manier is de betreffende API key enkel te gebruiken op die domeinnaam.</p>
<h2>Google Geolocation</h2>
<p>Voordat we alle klanten op de kaart kunnen zetten hebben we de coördinaten nodig van het adres. Hiervoor dienen we eerst verbinding te maken met de database waar de klanten in staan, vervolgens kunnen we één voor één de coördinaten opvragen. Maar zodra we deze hebben is het wel zo handig deze ook op te slaan! Maak twee nieuwe kolommen aan in het klanten tabel: &#8220;lat&#8221; en &#8220;lng&#8221;. Eventueel kan je als type een decimaal instellen. Ik heb voor simpelweg een &#8220;varchar&#8221; veld gekozen. Hierna kunnen we bijvoorbeeld het volgende stukje code gebruiken:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
// Hier uiteraard eerst een verbinding maken met de database

// Alle klanten ophalen welke nog géén coördinaten hebben
$query = mysqli_query('SELECT * FROM klanten WHERE lat=0 OR lng=0');

// Door alle opgehaalde klanten lopen
while($row = mysqli_fetch_assoc($query))
{
	// De URL maken met het adres, postcode en de woonplaats
	// van de klant om informatie over op te halen
	$url = 'http://maps.googleapis.com/maps/api/geocode/json?address='.urlencode($row['adres'].' '.$row['postcode'].' '.$row['plaats']).'&amp;sensor=false&amp;language=nl&amp;region=nl';

	// Het resultaat ophalen
	if($result = file_get_contents($url))
	{
		// Het resultaat is JSON, zodoende nog even decoderen
		$result = json_decode($result);

		// Kijken of het resultaat goed is
		if($result-&gt;status == 'OK')
		{
			// De locatie uit het resultaat ophalen
			$location = $result-&gt;results[0]-&gt;geometry-&gt;location;

			// De klant gegevens updaten met de coördinaten
			mysqli_query('UPDATE klanten SET lat=&quot;'.secure_in($location-&gt;lat).'&quot;, lng=&quot;'.secure_in($location-&gt;lng).'&quot; WHERE id='.$row['id']);
		}
	}
}
?&gt;
</pre>
<p>Door het draaien van dit stukje code worden dus de coördinaten aan de klant regels toegevoegd. In mijn geval moest ik dit script een aantal keren draaien voordat alle klanten de coördinaten hadden. Waarschijnlijk door limieten van deze API van Google. Eventueel kan je experimenteren met de <a title="Ga naar PHP's sleep functie" href="http://nl3.php.net/sleep" target="_blank">sleep functie</a>, wellicht dat er wat tijd tussen een verzoek moet zitten.</p>
<h2>Google Maps</h2>
<p>Nu de coördinaten bekend zijn kunnen deze geplaatst worden op een kaart! Hiervoor dient wél eerst de Maps API aangesproken te worden, zet hiervoor de volgende regel met je eigen API key in de &#8220;head&#8221;:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;https://maps.googleapis.com/maps/api/js?v=3&amp;key=HIER-JE-API-KEY&amp;sensor=false&amp;language=nl&amp;region=nl&quot;&gt;&lt;/script&gt;
</pre>
<p>En voeg vervolgens een div toe aan de &#8220;body&#8221; zoals ook gedaan wordt in de <a title="Ga naar de Google Maps API Documentatie" href="https://developers.google.com/maps/documentation/javascript/" target="_blank">Maps API documentatie</a>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;map-canvas&quot;&gt;&lt;/div&gt;
</pre>
<p>Hierna openen we een script tag en kunnen we aan de gang! Eerst maar de Nederlandse kaart voor ons krijgen:</p>
<pre class="brush: jscript; title: ; notranslate">
// Nieuwe cq mooiere visuele weergave gebruiken
google.maps.visualRefresh = true;

// Functie die aangesproken wordt zodra de pagina geladen is
function initialize()
{
	// Kaart opties
	var mapOptions = {
		center: new google.maps.LatLng(52.23, 4.55), // Coördinaten van centraal Nederland
		zoom: 8, // Inzoomen zodat Nederland goed zichtbaar is
		mapTypeId: google.maps.MapTypeId.ROADMAP // De wegenkaart gebruiken
	};

	// Kaart laden in div met het ID map-canvas
	var map = new google.maps.Map(document.getElementById(&quot;map-canvas&quot;),mapOptions);
}

// Zodra de pagina is geladen de initialize functie aanspreken
google.maps.event.addDomListener(window, 'load', initialize);
</pre>
<p>Om de klanten op de kaart weer te geven dienen we deze eerst op te halen:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;?php
// Alle klanten ophalen
$query = mysql_query('SELECT * FROM klanten');

// Door alle klanten lopen
while($row = mysql_fetch_assoc($query))
{
	// Een array opbouwen met de klant naam en coördinaten
	// (welke nog even van een komma naar een punt veranderd worden)
	$locations[] = '[&quot;'.$row['naam'].'&quot;,&quot;'.str_replace(',','.',$row['lat']).'&quot;,&quot;'.str_replace(',','.',$row['lng']).'&quot;]';
}
?&gt;

// De array met gegevens in Javascript
var locations = [
	&lt;?=implode(',',$locations); // De in PHP gemaakte array samenvoegen tot een regel gescheiden door komma's ?&gt;
];
</pre>
<p>Waarna we een nieuwe functie kunnen maken om de klanten op de kaart te zetten:</p>
<pre class="brush: jscript; title: ; notranslate">
function setMarkers(map,locations)
{
	// De afbeelding voor de klanten op de kaart
	var image = 'http://icons.iconarchive.com/icons/icons-land/vista-people/24/Office-Customer-Male-Light-icon.png';

	// Door alle klanten lopen
	for (var i = 0; i &lt; locations.length; i++)
	{
		// De coördinaten ophalen
		var loc = locations[i];
		var myLatLng = new google.maps.LatLng(loc[1], loc[2]);

		// De &quot;marker&quot; plaatsen
		var marker = new google.maps.Marker({
			position: myLatLng,
			map: map,
			icon: image,
			title: loc[0]
		});
	}
}
</pre>
<p>Het enige wat er nu nog moet gebeuren is deze functie aanspreken en het allemaal samenvoegen, met als resultaat:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;?php
// Alle klanten ophalen
$query = mysql_query('SELECT * FROM klanten');

// Door alle klanten lopen
while($row = mysql_fetch_assoc($query))
{
	// Een array opbouwen met de klant naam en coördinaten
	// (welke nog even van een komma naar een punt veranderd worden)
	$locations[] = '[&quot;'.$row['naam'].'&quot;,&quot;'.str_replace(',','.',$row['lat']).'&quot;,&quot;'.str_replace(',','.',$row['lng']).'&quot;]';
}
?&gt;

// De array met gegevens in Javascript
var locations = [
	&lt;?=implode(',',$locations); // De in PHP gemaakte array samenvoegen tot een regel gescheiden door komma's ?&gt;
];

// Nieuwe cq mooiere visuele weergave gebruiken
google.maps.visualRefresh = true;

// Functie die aangesproken wordt zodra de pagina geladen is
function initialize()
{
	// Kaart opties
	var mapOptions = {
		center: new google.maps.LatLng(52.23, 4.55), // Coördinaten van centraal Nederland
		zoom: 8, // Inzoomen zodat Nederland goed zichtbaar is
		mapTypeId: google.maps.MapTypeId.ROADMAP // De wegenkaart gebruiken
	};

	// Kaart laden in div met het ID map-canvas
	var map = new google.maps.Map(document.getElementById(&quot;map-canvas&quot;),mapOptions);

	// De &quot;markers&quot; plaatsen
	setMarkers(map,locations);
}

// Functie die de &quot;markers&quot; plaatst op de kaart
function setMarkers(map,locations)
{
	// De afbeelding voor de klanten op de kaart
	var image = 'http://icons.iconarchive.com/icons/icons-land/vista-people/24/Office-Customer-Male-Light-icon.png';

	// Door alle klanten lopen
	for (var i = 0; i &lt; locations.length; i++)
	{
		// De coördinaten ophalen
		var loc = locations[i];
		var myLatLng = new google.maps.LatLng(loc[1], loc[2]);

		// De &quot;marker&quot; plaatsen
		var marker = new google.maps.Marker({
			position: myLatLng,
			map: map,
			icon: image,
			title: loc[0]
		});
	}
}

// Zodra de pagina is geladen de initialize functie aanspreken
google.maps.event.addDomListener(window, 'load', initialize);
</pre>
<blockquote><p>Dat is toch stukken leuker dan een lijst met plaatsnamen! Uiteraard is het aan alle kanten uit te breiden, voor inspiratie: bekijk de <a title="Ga naar de Google Maps API Documentatie" href="https://developers.google.com/maps/documentation/javascript/" target="_blank">Maps API documentatie</a> eens!</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/klanten-in-kaart-brengen-met-google-geolocation-en-maps-api/">Klanten in kaart brengen met Google Geolocation en Maps API</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/klanten-in-kaart-brengen-met-google-geolocation-en-maps-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter Bootstrap en CodeIgniter&#8217;s form validation</title>
		<link>https://royduineveld.nl/twitter-bootstrap-en-codeigniters-form-validation/</link>
		<comments>https://royduineveld.nl/twitter-bootstrap-en-codeigniters-form-validation/#respond</comments>
		<pubDate>Thu, 16 May 2013 19:00:39 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[CodeIgniter]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Twitter Bootstrap]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=454</guid>
		<description><![CDATA[<p>Ik ben een grootte fan van Twitter Bootstrap en gebruik dit dan ook graag, het ziet er goed uit en ermee werken is kinderlijk eenvoudig. Voor het functionele gedeelte maak...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/twitter-bootstrap-en-codeigniters-form-validation/">Twitter Bootstrap en CodeIgniter&#8217;s form validation</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Ik ben een grootte fan van Twitter Bootstrap en gebruik dit dan ook graag, het ziet er goed uit en ermee werken is kinderlijk eenvoudig. Voor het functionele gedeelte maak ik graag gebruik van het CodeIgniter framework waarin allerlei handige functionaliteiten, waaronder <a title="Ga naar de Form Validation handleiding voor CodeIgniter" href="http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html" target="_blank">Form Validation</a>. Deze PHP class of &#8220;library&#8221; genoemd in CodeIgniter maakt het valideren van een formulier stukken makkelijker. Als voorbeeld:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

// De controller welke de controller van CodeIgniter uitbreid
class Formulier extends CI_Controller
{
	// De index functie welke als eerst aangesproken wordt
	// wanneer de controller geladen is
	public function index()
	{
		// De library laden zodat deze gebruikt kan worden
		// Eventueel ook automatisch te laden via autoload.php
		$this-&gt;load-&gt;library('form_validation');

		// De p-tags verwijderen rondom validatie foutmeldingen
		// Normaal is dit: &lt;p&gt;Foutmelding...&lt;/p&gt;
		$this-&gt;form_validation-&gt;set_error_delimiters('', '');

		// Regels instellen voor ingevulde velden
		// De eerste waarde is de &quot;name&quot; van de input
		// De tweede waarde is de volledige naam van het veld
		// De derde waarde zijn de validatie regels:
		// - required = Verplicht in te vullen
		// - max_length[255] = Maximaal 255 karakters lang
		// - valid_email = Een geldig email adres
		// - trim = De PHP trim functie
		$this-&gt;form_validation-&gt;set_rules('name', 'Naam', 'required|max_length[255]|trim');
		$this-&gt;form_validation-&gt;set_rules('email', 'Email', 'required|valid_email|max_length[255]|trim');

		// Is het formulier verstuurd en is de validatie geslaagd?
		if( $this-&gt;form_validation-&gt;run()  )
		{
			// Ja!
			// Doe iets met de waardes,
			// opslaan in een database bijvoorbeeld
		}
		else
		{
			// Nee!
			// Het formulier weergeven
			$this-&gt;load-&gt;view('header');
			$this-&gt;load-&gt;view('formulier');
			$this-&gt;load-&gt;view('footer');
		}
	}
}
</pre>
<p>De &#8220;header&#8221; en &#8220;footer&#8221; view laten we even achterwege, door naar het formulier! Om de eventuele foutmeldingen weer te geven is de <a title="Ga naar de Helper Reference van de Form Validation library" href="http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#helperreference" target="_blank">validation_errors</a> functie, maar deze set alle foutmeldingen onder elkaar. Bij een mooi formulier ziet het er stukken beter uit als achter het veld een eventuele foutmelding komt te staan. Zie ook <a title="Ga naar de voorbeelden van formulier met Twitter Bootstrap" href="http://twitter.github.io/bootstrap/base-css.html#forms" target="_blank">de voorbeelden</a> van Twitter Bootstrap (vrij onderaan, je kan beter eerst naar buttons gaan en dan naar boven scrollen). Om dit voor elkaar te krijgen is er de <a title="Ga naar de Helper Reference van de Form Validation library" href="http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#helperreference" target="_blank">form_error</a> functie. Deze in combinatie met de <a title="Ga naar de Helper Reference van de Form Validation library" href="http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#helperreference" target="_blank">set_value</a> functie maakt het af. Deze laatste zorgt er namelijk voor dat de ingevulde waardes blijven, wanneer de validatie mislukt is, na het versturen van het formulier. Dit bij elkaar gestopt resulteert in het volgende:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;h1&gt;Gebruiker &lt;?=(isset($id) ? 'bewerken' : 'toevoegen');?&gt;&lt;/h1&gt;
&lt;hr&gt;
&lt;?=form_open('',array('class' =&gt; 'form-horizontal'));?&gt;
	&lt;div class=&quot;control-group&lt;?=form_error('name') ? ' error' : '';?&gt;&quot;&gt;
		&lt;label class=&quot;control-label&quot; for=&quot;name&quot;&gt;Naam&lt;/label&gt;
		&lt;div class=&quot;controls&quot;&gt;
			&lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; placeholder=&quot;Naam&quot; value=&quot;&lt;?=set_value('name',(isset($name) ? $name : '')); ?&gt;&quot;&gt;
			&lt;span class=&quot;help-inline&quot;&gt;&lt;?=form_error('name'); ?&gt;&lt;/span&gt;
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div class=&quot;control-group&lt;?=form_error('email') ? ' error' : '';?&gt;&quot;&gt;
		&lt;label class=&quot;control-label&quot; for=&quot;email&quot;&gt;Email&lt;/label&gt;
		&lt;div class=&quot;controls&quot;&gt;
			&lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;email&quot; placeholder=&quot;Email adres&quot; value=&quot;&lt;?=set_value('email',(isset($email) ? $email : '')); ?&gt;&quot;&gt;
			&lt;span class=&quot;help-inline&quot;&gt;&lt;?=form_error('email'); ?&gt;&lt;/span&gt;
		&lt;/div&gt;
	&lt;/div&gt;
	&lt;div class=&quot;control-group&quot;&gt;
		&lt;div class=&quot;controls&quot;&gt;
			&lt;button type=&quot;submit&quot; class=&quot;btn btn-success&quot;&gt;&lt;i class=&quot;icon-ok icon-white&quot;&gt;&lt;/i&gt; Opslaan&lt;/button&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/form&gt;
</pre>
<p>Eén ding heb ik nog niet uitgelegd! Programmeren volgens <a title="Ga naar Wikipedia over DRY" href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" target="_blank">DRY</a> houd dus ook géén dubbele formulieren in. Vandaar de <a title="Ga naar de isset functie van PHP" href="http://php.net/isset" target="_blank">isset</a> functies. Het formulier om iets toe te voegen en iets te bewerken kan dus samengevoegd worden. Wanneer dit formulier vanuit de controller geladen wordt zijn er geen waardes ingevuld. Willen we deze wel ingevuld hebben; dienen we deze enkel door te geven. De aangevulde en iets bewerkte controller:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

// De controller welke de controller van CodeIgniter uitbreid
class Formulier extends CI_Controller
{
	// De &quot;constructor&quot;, deze wordt altijd voor de losse functies uitgevoerd
	public function __construct()
	{
		// Zorgen dat de &quot;constructor&quot; van de CI_Controller eerst uitgevoerd wordt
		parent::__construct();

		// De library laden zodat deze gebruikt kan worden
		// Eventueel ook automatisch te laden via autoload.php
		$this-&gt;load-&gt;library('form_validation');

		// De p-tags verwijderen rondom validatie foutmeldingen
		// Normaal is dit: &lt;p&gt;Foutmelding...&lt;/p&gt;
		$this-&gt;form_validation-&gt;set_error_delimiters('', '');

		// Regels instellen voor ingevulde velden
		// De eerste waarde is de &quot;name&quot; van de input
		// De tweede waarde is de volledige naam van het veld
		// De derde waarde zijn de validatie regels:
		// - required = Verplicht in te vullen
		// - max_length[255] = Maximaal 255 karakters lang
		// - valid_email = Een geldig email adres
		// - trim = De PHP trim functie
		$this-&gt;form_validation-&gt;set_rules('name', 'Naam', 'required|max_length[255]|trim');
		$this-&gt;form_validation-&gt;set_rules('email', 'Email', 'required|valid_email|max_length[255]|trim');
	}

	// De index functie welke als eerst aangesproken wordt
	// wanneer de controller geladen is, dus:
	// http://website.nl/formulier
	public function index()
	{
		// Is het formulier verstuurd en is de validatie geslaagd?
		if( $this-&gt;form_validation-&gt;run()  )
		{
			// Ja! Opslaan in de database
			$this-&gt;db-&gt;insert('gebruikers',$this-&gt;input-&gt;post());

			// Doorsturen naar de begin pagina
			redirect( site_url() );
		}
		else
		{
			// Nee! Het formulier weergeven
			$this-&gt;load-&gt;view('header');
			$this-&gt;load-&gt;view('formulier');
			$this-&gt;load-&gt;view('footer');
		}
	}

	// De bijwerken functie
	// http://website.nl/formulier/edit/1
	public function edit($id = 0)
	{
		// Gegevens uit de database halen
		$data = $this-&gt;db-&gt;get_where('gebruikers',array('id' =&gt; $id))-&gt;row();

		// Niets gevonden in de database?
		if(empty($data))
		{
			// Foutmelding weergeven
			show_404();
		}

		// Is het formulier verstuurd en is de validatie geslaagd?
		if( $this-&gt;form_validation-&gt;run()  )
		{
			// Ja! Bewerken in de database
			$this-&gt;db-&gt;where('id',$id);
			$this-&gt;db-&gt;update('gebruikers',$this-&gt;input-&gt;post());

			// Doorsturen naar de begin pagina
			redirect( site_url() );
		}
		else
		{
			// Nee! Het formulier weergeven
			// Deze keer de waardes uit de database meegeven
			$this-&gt;load-&gt;view('header');
			$this-&gt;load-&gt;view('formulier',$data);
			$this-&gt;load-&gt;view('footer');
		}
	}
}
</pre>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/twitter-bootstrap-en-codeigniters-form-validation/">Twitter Bootstrap en CodeIgniter&#8217;s form validation</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/twitter-bootstrap-en-codeigniters-form-validation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Altijd weten door wie je gebeld wordt</title>
		<link>https://royduineveld.nl/altijd-weten-door-wie-je-gebeld-wordt/</link>
		<comments>https://royduineveld.nl/altijd-weten-door-wie-je-gebeld-wordt/#comments</comments>
		<pubDate>Sat, 11 May 2013 14:37:40 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Ideeën & Concepten]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Cordova]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Phonegap]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=437</guid>
		<description><![CDATA[<p>Is het niet super handig als je gebeld wordt, je altijd weet door wie? Met uitzondering van echte onbekende nummers natuurlijk. Ofwel; je wordt gebeld en er staat een nummer...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/altijd-weten-door-wie-je-gebeld-wordt/">Altijd weten door wie je gebeld wordt</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Is het niet super handig als je gebeld wordt, je altijd weet door wie? Met uitzondering van echte onbekende nummers natuurlijk. Ofwel; je wordt gebeld en er staat een nummer in het scherm welke je niet kent. Vervolgens zorgt een app ervoor dat er een popupje komt met de naam van de persoon. Goed nieuws! Zo&#8217;n app is er al: <a title="Ga naar de Truecaller app in de Play Store" href="https://play.google.com/store/apps/details?id=com.truecaller&amp;hl=nl" target="_blank">Truecaller</a>. Maar, helaas herkent deze app heel erg weinig telefoon nummers en mocht het op 06 nummers aankomen kan je het helemaal vergeten. Dat kan beter!</p>
<h2>Het idee</h2>
<p>WhoApp! Na het downloaden en starten van de app wordt je gehele adressenboek (enkel namen en telefoon nummers) geüpload naar de server. Dit klinkt misschien gek of eng maar waarom niet? Het zijn maar telefoon nummers en namen en anders kan er nooit een (om te beginnen, landelijk) telefoonboek gemaakt worden met ook mobiele nummers. Mocht je dit niet willen eventueel een tweede betaalde app waarbij dit niet hoeft. Dit is dan gelijk een klein verdien patroon. In de app zelf kan er op telefoon nummer gezocht worden naar namen. Dit aan de hand van de gegevens in de database op de server. Mocht het betreffende telefoon nummer nog niet in de database staan eventueel een koppeling met bijvoorbeeld <a title="Ga naar nummerzoeker.com" href="http://nummerzoeker.com" target="_blank">nummerzoeker.com</a> waar het mogelijk is te zoeken op vaste telefoon nummers naar (bedrijfs)namen en adressen. Daarnaast kan de achtergrond taak aangezet worden welke zorgt voor de volgende functionaliteit:</p>
<p>Wanneer je gebeld wordt komt er bovenin het scherm een venstertje te staan met daarin de naam van diegene met dat telefoon nummer. Dit enkel in het geval dat het nummer niet in je eigen adressenboek staat en uiteraard enkel wanneer aanwezig in de database.</p>
<p><a href="https://royduineveld.nl/wp-content/uploads/2013/05/truecaller.jpg"><img class="aligncenter size-medium wp-image-439" src="https://royduineveld.nl/wp-content/uploads/2013/05/truecaller-179x300.jpg" alt="Truecaller" width="179" height="300" srcset="https://royduineveld.nl/wp-content/uploads/2013/05/truecaller-179x300.jpg 179w, https://royduineveld.nl/wp-content/uploads/2013/05/truecaller.jpg 307w" sizes="(max-width: 179px) 100vw, 179px" /></a></p>
<h2>Uitvoering</h2>
<p>Enige tijd geleden kwam dit idee en heb hiervoor de naam WhoApp bedacht. Deze naam zodoende geregistreerd, zowel bij de KvK als de domeinnaam (<a title="Ga naar WhoApp.nl" href="http://whoapp.nl" target="_blank">whoapp.nl</a>) en de naam als app voor de Google Play Store. Nu de app nog maken! Op het moment van schrijven ben ik nog niet zo ver met Java en dus native Android apps. Daarin tegen kan ik aardig wat maken met Phonegap waar ik eerder een artikel over heb geschreven: <a title="App maken met Phonegap en Phonegap Build" href="https://royduineveld.nl/overige/413/app-maken-met-phonegap-en-phonegap-build/" target="_blank">App maken met Phonegap en Phonegap Build</a>. De te ontwikkelen functionaliteiten:</p>
<ul>
<li><span style="line-height: 13px;">Contacten van de telefoon naar de server versturen. Met Phonegap werk je voornamelijk met Javascript, dat in combinatie met de <a title="Ga naar de Phonegap Contacts API documentatie" href="http://docs.phonegap.com/en/2.7.0/cordova_contacts_contacts.md.html#Contacts" target="_blank">Phonegap API</a> en vervolgens middels Ajax versturen. Dit heb ik inmiddels getest en functioneert.</span></li>
<li>Het telefoonboek. Op de server zal een PHP script in combinatie met een MySQL database alle telefoon nummers moeten verwerken. Deze dienen uiteraard gestandaardiseerd en gegroepeerd te worden. Dit wordt nog even uitzoeken maar is zeker te doen. Ook moet er een algoritme toegepast worden om de juiste naam te tonen. De één heeft in zijn adressenboek simpelweg &#8220;Roy&#8221; staan, de ander &#8220;Roy Duineveld&#8221; en weer iemand anders &#8220;Roy uit Heiloo&#8221;. Aan de hand van de meest voorkomende naam zal deze getoond worden. Dan hopen dat zoveel mogelijk mensen &#8220;Roy Duineveld&#8221; in hun telefoonboek hebben staan.</li>
<li>Van de app een service maken welke op de achtergrond draait. Dit is vanuit Phonegap nog niet mogelijk maar er is wel al een plugin voor gemaakt: <a title="Ga naar de background service plugin voor Phonegap" href="https://github.com/Red-Folder/Cordova-Plugin-BackgroundService" target="_blank">Cordova-Plugin-BackgroundService</a>. Ik heb hier nog niet mee geëxperimenteerd maar ik verwacht geen problemen.</li>
<li>De service automatisch starten wanneer de telefoon aan gezet wordt. Dit wordt waarschijnlijk in de native code duiken worden maar hoop in de &#8220;Background Service&#8221; plugin wat te vinden wat dit mogelijk maakt.</li>
<li>Wanneer gaat de telefoon over? Ook daar heeft iemand al een plugin voor Phonegap voor gemaakt! <a title="Ga naar de PhoneListener github pagina" href="https://github.com/devgeeks/PhoneListener" target="_blank">PhoneListener</a>. Helaas functioneert deze plugin niet meer met de nieuwere versies van Phonegap maar ook nu heeft een nieuwe plugin zich aangeboden die dit mogelijk maakt: <a title="Ga naar de PhoneStateChangeListener github pagina" href="https://github.com/madeinstefano/PhoneStateChangeListener" target="_blank">PhoneStateChangeListener</a>. Hierbij krijg je de status &#8220;idle&#8221;, &#8220;ringing&#8221; of &#8220;offhook&#8221; door. Getest en werkt perfect!</li>
<li>Wanneer de telefoon gaat; het telefoon nummer bemachtigen. In de extended versie van de verouderde PhoneListener plugin was deze optie beschikbaar: <a title="Ga naar de Extended PhoneListener github pagina" href="https://github.com/devgeeks/PhoneListener/tree/extended" target="_blank">PhoneListener Extended</a>. Na contact te hebben gehad met de maker van de PhoneStateChangeListener plugin is versie 1.1 uitgebracht waar deze functionaliteit is toegevoegd! Ook dit getest en werkt perfect.</li>
<li>Als laatste misschien wel een van de belangrijkste elementen, de popup op het scherm weergeven! Na veel zoek werk ben ik hier vast gelopen. Mede door mijn nihile kennis van Java. Momenteel heb ik op <a title="Ga naar Freelancer.com" href="http://freelancer.com" target="_blank">Freelancer.com</a> een project lopen en iemand gevonden in China die dit voor mij gaat realiseren voor weinig geld. De verwachtingen zijn laag, maar niet geschoten is altijd mis. Wordt vervolgd!</li>
</ul>
<h2>Vooruitgang</h2>
<p>Momenteel draait er een simpele website welke enkel gekoppeld is aan nummerzoeker.com. Wat de app betreft heb ik twee varianten, één op grafisch gebied compleet en met de functionaliteit om alle contactpersonen naar de server te versturen en nummers op te zoeken. De tweede app is enkel om te testen, hierin zit momenteel de PhoneStateChangeListener plugin verwerkt. Het is nog even verder experimenteren met de &#8220;Background Service&#8221;, het automatisch starten hiervan en de popup.</p>
<blockquote><p>Het project zit niet heel veel druk achter maar lijkt mij leuk om door te zetten. Mocht er nieuws zijn zal ik dit toevoegen aan dit artikel.</p></blockquote>
<p><em>Eventueel geïnteresseerden voor samenwerking is bespreekbaar. Ook aan- of opmerkingen zijn van harte welkom!</em></p>
<h2>Updates</h2>
<ul>
<li>13-05-2013 &#8211; Inmiddels is bekend dat het met de &#8220;Background Service&#8221; plugin het mogelijk is een automatisch startende service te maken. Tevens een aantal test apps mogen ontvangen van &#8220;de Chinees&#8221; met goede resultaten!</li>
<li>01-09-2013 &#8211; Ik heb het aardig druk op het moment, dus dit project staat even op een zijspoor. Inmiddels is wel alles in huis om het project te voltooien.</li>
<li>05-01-2014 &#8211; Google heeft een soortgelijke functionaliteit in de nieuwe Android versie 4.4 Kitkat gestopt (<a title="Ga naar Tweakers.net" href="http://tweakers.net/nieuws/92420/google-breidt-nummerherkenning-android-uit-met-google+-informatie.html" target="_blank">zie dit nieuwsbericht op Tweakers.net</a>), ik zal er dus niet meer mee verder gaan.</li>
<li>19-07-2016 &#8211; De domeinnaam wordt niet verlengt en komt weer vrij op de markt.</li>
</ul>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/altijd-weten-door-wie-je-gebeld-wordt/">Altijd weten door wie je gebeld wordt</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/altijd-weten-door-wie-je-gebeld-wordt/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Responsive slider in WordPress met backend</title>
		<link>https://royduineveld.nl/responsive-slider-in-wordpress-met-backend/</link>
		<comments>https://royduineveld.nl/responsive-slider-in-wordpress-met-backend/#respond</comments>
		<pubDate>Fri, 19 Apr 2013 12:17:28 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=390</guid>
		<description><![CDATA[<p>Voor WordPress zijn er gigantisch veel sliders te vinden als plugin. Ik was op zoek naar een responsive slider voor in de header met een gemakkelijke backend om foto&#8217;s toe...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/responsive-slider-in-wordpress-met-backend/">Responsive slider in WordPress met backend</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Voor WordPress zijn er <a title="Wordpress plugin pagina met sliders" href="http://wordpress.org/extend/plugins/search.php?q=slider" target="_blank">gigantisch veel sliders</a> te vinden als plugin. Ik was op zoek naar een responsive slider voor in de header met een gemakkelijke backend om foto&#8217;s toe te voegen. Na een lange speurtocht besloten om dit zelf te regelen. Uiteindelijk gekozen voor een populaire slider: <a title="Website van Nivo Slider" href="http://dev7studios.com/nivo-slider/" target="_blank">Nivo Slider</a>. De Javascript en CSS bestanden toegevoegd in &#8220;lib/scripts.php&#8221; en de nodige HTML in de header. In mijn geval in &#8220;header-top-navbar.php&#8221; gezien ik gebruik maak van het <a title="Website van het Roots starter theme" href="http://www.rootstheme.com/" target="_blank">Roots starter theme</a>. Top, maar nu de backend? Er zit al een mediabibliotheek in de WordPress backend, waarom hier niet gebruik van maken! Afbeeldingen kunnen geupload, geroteerd, gespiegeld, geschaald en bijgesneden worden. Helemaal compleet dus! Maar niet alle afbeeldingen in de mediabibliotheek moeten in de slider komen. Zodoende gekozen voor een &#8220;filter&#8221; op de &#8220;<a title="Informatie over alt-text op Wikipedia" href="http://en.wikipedia.org/wiki/Alt_attribute" target="_blank">alt-text</a>&#8221; ofwel alternatieve tekst mocht de afbeelding niet weergegeven kunnen worden. Wanneer die gelijk is aan &#8220;slider&#8221; moet deze in de slider komen. Na wat zoek werk in de database tot de volgende MySQL query gekomen:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
	second.meta_value
FROM
	wp_postmeta as first
JOIN
	wp_postmeta as second
ON
	first.post_id = second.post_id AND second.meta_key = &quot;_wp_attached_file&quot;
WHERE
	first.meta_value=&quot;slider&quot;
</pre>
<p>Hierbij wordt gezocht naar &#8220;meta_value&#8221;: &#8220;slider&#8221;. Aan de hand van het &#8220;post_id&#8221; wordt gezocht naar de afbeelding welke als &#8220;meta_key&#8221;: &#8220;_wp_attached_file&#8221; heeft. Dit verwerkt naar &#8220;WordPress taal&#8221; met daarbij de <a title="Multidimensionale array naar enkele array" href="https://royduineveld.nl/php/327/multidimensionale-array-naar-enkele-array/">multidimensionale array geconverteerd naar een enkele array</a> met als resultaat:</p>
<pre class="brush: php; title: ; notranslate">
$results = $wpdb-&gt;get_results('
	SELECT
		second.meta_value
	FROM
		'.$wpdb-&gt;postmeta.' as first
	JOIN
		'.$wpdb-&gt;postmeta.' as second
	ON
		first.post_id = second.post_id AND second.meta_key = &quot;_wp_attached_file&quot;
	WHERE
		first.meta_value=&quot;slider&quot;
	',ARRAY_N);
$results = array_map(function($array){return $array[0];},$results);
</pre>
<p>Nu het resultaat nog in de slider zetten:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;slider&quot; class=&quot;nivoSlider&quot;&gt;
	&lt;? foreach($results as $result): ?&gt;
	&lt;img src=&quot;assets/&lt;?=$result;?&gt;&quot; alt=&quot;&quot;&gt;
	&lt;? endforeach; ?&gt;
&lt;/div&gt;
</pre>
<blockquote><p>Typisch weer een oplossing op mijn &#8220;slogan&#8221; gebaseerd: Waarom moeilijk doen als het makkelijk kan!</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/responsive-slider-in-wordpress-met-backend/">Responsive slider in WordPress met backend</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/responsive-slider-in-wordpress-met-backend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Totaal ophalen bij het gebruik van limit</title>
		<link>https://royduineveld.nl/totaal-ophalen-bij-het-gebruik-van-limit/</link>
		<comments>https://royduineveld.nl/totaal-ophalen-bij-het-gebruik-van-limit/#respond</comments>
		<pubDate>Tue, 05 Feb 2013 11:18:24 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=311</guid>
		<description><![CDATA[<p>Wanneer je paginering maakt op je website zal je gebruik maken van &#8220;limit&#8221; om ervoor te zorgen dat de items van de eerdere pagina&#8217;s niet weergeven worden en de items van...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/totaal-ophalen-bij-het-gebruik-van-limit/">Totaal ophalen bij het gebruik van limit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Wanneer je paginering maakt op je website zal je gebruik maken van &#8220;limit&#8221; om ervoor te zorgen dat de items van de eerdere pagina&#8217;s niet weergeven worden en de items van de nog te komen pagina ook niet. Een stukje pseudo code:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT
	name,image,price
FROM
	products
WHERE
	category=6
LIMIT
	20,10
</pre>
<p>Dit zorgt ervoor dat de eerste 20 producten overgeslagen worden en de volgende 10 weergegeven worden. Maar hoe komen we er nu achter hoeveel producten er totaal zijn? Hiervoor kunnen we natuurlijk de query nogmaals uitvoeren en de limit weg laten. In dit geval is dat niet een probleem, maar wat als het een grote complexe query is? Een mooie standaard om aan te houden is natuurlijk <a title="Don't repeat yourself" href="http://en.wikipedia.org/wiki/Don%27t_Repeat_Yourself" target="_blank">DRY</a>, dan is dat geen optie. Gelukkig heeft MySQL ook hier wat moois voor <a title="Found_rows function" href="http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows" target="_blank">FOUND_ROWS()</a>! Wanneer we dit toepassen zal onze query er zo uit zien:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT SQL_CALC_FOUND_ROWS
	name,image,price
FROM
	products
WHERE
	category=6
LIMIT
	20,10
</pre>
<p>Maar het resultaat is het zelfde? Dat klopt want hierna dienen we een tweede query te versturen welke het totaal gaat ophalen:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT FOUND_ROWS()
</pre>
<p>Deze tweede query geeft het aantal gevonden items terug van de eerste query zonder de limit!</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/totaal-ophalen-bij-het-gebruik-van-limit/">Totaal ophalen bij het gebruik van limit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/totaal-ophalen-bij-het-gebruik-van-limit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tekst en nummers sorteren</title>
		<link>https://royduineveld.nl/tekst-en-nummers-sorteren/</link>
		<comments>https://royduineveld.nl/tekst-en-nummers-sorteren/#respond</comments>
		<pubDate>Fri, 04 Jan 2013 12:06:53 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=178</guid>
		<description><![CDATA[<p>Om te sorteren in een MySQL query dan gebruiken je &#8220;order by&#8221;. Bijvoorbeeld: Vervolgens krijg je netjes een gesorteerd rijtje, bijvoorbeeld: Android Android 2.2 Android 2.3 Android 3.1 Android 3.2...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/tekst-en-nummers-sorteren/">Tekst en nummers sorteren</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Om te sorteren in een MySQL query dan gebruiken je &#8220;order by&#8221;. Bijvoorbeeld:</p>
<pre class="brush: plain; title: ; notranslate">ORDER BY naam ASC</pre>
<p>Vervolgens krijg je netjes een gesorteerd rijtje, bijvoorbeeld:</p>
<ul>
<li>Android</li>
<li>Android 2.2</li>
<li>Android 2.3</li>
<li>Android 3.1</li>
<li>Android 3.2</li>
<li>Android 4.0</li>
<li>Android 4.0.3</li>
<li>BlackBerry Tablet OS</li>
<li>iOS 4</li>
<li>iOS 5</li>
<li>iOS 6</li>
<li>Windows 7 Professional</li>
<li>Windows 8</li>
<li>Windows 8 Pro</li>
</ul>
<p>Maar wanneer het gaat om nummers klopt de volgorde niet:</p>
<ul>
<li>1024 x 600</li>
<li>1024 x 768</li>
<li>1280 x 768</li>
<li>1280 x 800</li>
<li>1366 x 768</li>
<li>1920 x 1200</li>
<li>2048 x 1536</li>
<li>800 x 480</li>
</ul>
<p>De laatste hier genoemd dient bovenaan te staan. Om dit op te lossen kunnen de ABS functie van MySQL gebruiken:</p>
<pre class="brush: plain; title: ; notranslate">ORDER BY ABS(naam) ASC</pre>
<p>Wat het juiste resultaat geeft:</p>
<ul>
<li>800 x 480</li>
<li>1024 x 600</li>
<li>1024 x 768</li>
<li>1280 x 800</li>
<li>1280 x 768</li>
<li>1920 x 1200</li>
<li>2048 x 1536</li>
</ul>
<p>Maar! Het andere rijtje klopt nu niet meer:</p>
<ul>
<li>Android 3.1</li>
<li>Android</li>
<li>BlackBerry Tablet OS</li>
<li>Android 3.2</li>
<li>Android 2.3</li>
<li>Android 4.0</li>
<li>iOS 5</li>
<li>Android 4.0.3</li>
<li>iOS 4</li>
<li>Android 2.2</li>
<li>Windows 7 Professional</li>
<li>Windows 8</li>
<li>Windows 8 Pro</li>
<li>iOS 6</li>
</ul>
<p>Om dat op te lossen kan je een combinatie van beide gebruiken, ofwel:</p>
<pre class="brush: plain; title: ; notranslate">ORDER BY ABS(naam),naam ASC</pre>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/tekst-en-nummers-sorteren/">Tekst en nummers sorteren</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/tekst-en-nummers-sorteren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UTF-8 Karakterset</title>
		<link>https://royduineveld.nl/utf-8-karakterset/</link>
		<comments>https://royduineveld.nl/utf-8-karakterset/#respond</comments>
		<pubDate>Mon, 19 Nov 2012 18:43:47 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=103</guid>
		<description><![CDATA[<p>Problemen met rare karakters (bijvoorbeeld vierkantjes waar een é of € hoort te staan) worden meestal veroorzaakt door coderingsproblemen. Daarom is het belangrijk dat er overal dezelfde karakterset gebruikt wordt....</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/utf-8-karakterset/">UTF-8 Karakterset</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Problemen met rare karakters (bijvoorbeeld vierkantjes waar een é of € hoort te staan) worden meestal veroorzaakt door coderingsproblemen. Daarom is het belangrijk dat er overal dezelfde karakterset gebruikt wordt. Naar mijn weten is de meest gebruikte <a href="http://nl.wikipedia.org/wiki/UTF-8">UTF-8</a>. In dit artikel noem ik een aantal belangrijke punten bij het instellen van een codering.</p>
<p><strong>HTML</strong></p>
<p>Een meta tag in de head mee te geven met daarin de karakterset:</p>
<pre class="brush: xml; title: ; notranslate">&lt;meta charset=&quot;utf-8&quot;&gt;</pre>
<p><strong>HTACCESS</strong></p>
<p>Globaal een karakterset in te stellen:</p>
<pre class="brush: plain; title: ; notranslate">

AddDefaultCharset utf-8
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml

</pre>
<p><strong>PHP</strong></p>
<p>Met PHP is het belangrijk om bij alle functies waar een encoding opgegeven kan worden, dit op te geven. Bijvoorbeeld bij de functie htmlspecialchars:</p>
<pre class="brush: php; title: ; notranslate">htmlspecialchars($string,ENT_QUOTES,'UTF-8');</pre>
<p>Mocht je al op PHP 5.4 draaien is dit niet nodig. Vanaf 5.4 is UTF-8 de standaard encoding! Tevens voor de zekerheid default_charset even op utf-8 zetten:</p>
<pre class="brush: php; title: ; notranslate">ini_set('default_charset','UTF-8' );</pre>
<p><strong>MySQL</strong></p>
<p>Bij het verbinden met een database (in dit geval MySQL) dient de karakterset opgegeven te worden. Dit dient bij mysql en mysqli tussen de verbinding en het selecteren van een database te gebeuren:</p>
<pre class="brush: php; title: ; notranslate">

mysqli_connect($host,$username,$password);
mysqli_set_charset('utf8');
mysqli_select_db($database);

</pre>
<p>In het geval van PDO net even anders:</p>
<pre class="brush: php; title: ; notranslate">

$pdo = new PDO(
 'mysql:host='.$host.';dbname='.$database,
 $username,
 $password,
 array(PDO::MYSQL_ATTR_INIT_COMMAND =&gt; &quot;SET NAMES utf8&quot;)
);

</pre>
<p>Daarnaast bij het indelen van de database dient er overal als als charset; utf8 gekozen te worden en als collation: utf8_general_ci.</p>
<p><strong>Javascript</strong></p>
<p>Meestal niet nodig maar mochten er problemen voordoen, simpelweg charset=&#8221;utf-8&#8243; toevoegen, ofwel:</p>
<pre class="brush: xml; title: ; notranslate">

&lt;script charset=&quot;utf-8&quot;&gt;
//Javascript hier
&lt;/script&gt;

</pre>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/utf-8-karakterset/">UTF-8 Karakterset</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/utf-8-karakterset/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>