<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" version="2.0">

<channel>
	<title>Web development blog, news and tutorials - Developer Drive</title>
	
	<link>http://www.developerdrive.com</link>
	<description>Web development blog, news and tutorials</description>
	<lastBuildDate>Tue, 21 May 2013 01:39:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/DeveloperDrive" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="developerdrive" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">DeveloperDrive</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>Creating a jQuery gallery for Flickr</title>
		<link>http://www.developerdrive.com/2013/05/creating-a-jquery-gallery-for-flickr/</link>
		<comments>http://www.developerdrive.com/2013/05/creating-a-jquery-gallery-for-flickr/#comments</comments>
		<pubDate>Tue, 21 May 2013 01:39:06 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Web Services]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4126</guid>
		<description><![CDATA[Introduction This week, we&#8217;ll be creating a simple jQuery gallery that uses the Flickr API to retrieve a list of images from a photo set and display them. We&#8217;ll also be using the API to get some additional information about &#8230; <a href="http://www.developerdrive.com/2013/05/creating-a-jquery-gallery-for-flickr/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>This week, we&#8217;ll be creating a simple jQuery gallery that uses the <a href="http://www.flickr.com/services/api/" target="_blank">Flickr API</a> to retrieve a list of images from a photo set and display them. We&#8217;ll also be using the API to get some additional information about the images and storing it using the HTML5 <a href="http://www.w3.org/TR/html51/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes" target="_blank">data-* attributes</a>
</p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery.png"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery.png" alt="gallery" width="821" height="637" class="aligncenter size-full wp-image-4128" /></a></p>
<h2>The Flickr API</h2>
<p>To use the API you need to register for an API key, it doesn&#8217;t take long and you can start <a href="http://www.flickr.com/services/apps/create/apply/?" target="_blank">here</a>
</p>
<p>There are 2 methods that we will be using for this tutorial, first we&#8217;ll need to get name and description of the specified photo set. To do that we use the <a href="http://www.flickr.com/services/api/flickr.photosets.getInfo.html" target="_blank">flickr.photosets.getInfo</a> method, it has just 2 parameters, the API key and the photo set id. The photo set I am using is from the National Library of Ireland and is title <a href="http://www.flickr.com/photos/nlireland/sets/72157626766436507/" target="_blank">Working Life</a>, you can see the photo set ID (72157626766436507) is easily read from the URL. This set is available under a Commons license. Next up we&#8217;ll need to get the list of images in the photo set, to do this we use the <a href="http://www.flickr.com/services/api/flickr.photosets.getPhotos.html" target="_blank">flickr.photosets.getPhotos</a> method. This method takes a few extra parameters that allow you to control paging and what information comes back
</p>
<h2>jQuery</h2>
<p>Just like our last jQuery plug-in, we&#8217;ll start off with the basic plug-in template and move on from there.
</p>
<pre class="brush: js">
(function( $ ) {
    $.fn.flickr = function(options) {
        var url = 'http://api.flickr.com/services/rest/?jsoncallback=?';
        
        var settings = $.extend( {
            'api_key': 'API KEY GOES HERE',
        }, options);
        
        return this.each(function() {
        });

    };
})( jQuery );
</pre>
<p>We&#8217;re going to make this plug-in completely self contained (except for CSS), so all the user has to provide is an empty div. To enable this, when the plug-in is initialized we will have to add any HTML elements we need.
</p>
<pre class="brush: js">
var gallery = $(this);
gallery.addClass('flickr-gallery');
gallery.append('&lt;h2&gt;&lt;/h2&gt;&lt;h3&gt;&lt;/h3&gt;&lt;div class="viewport"&gt;&lt;/div&gt;&lt;div class="browser"&gt;&lt;ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="clear"&gt;&lt;/div&gt;');
</pre>
<p>Here you can see I am adding some header elements as well as two divs. The &#8220;viewport&#8221; div is where we will have our main image, and the &#8220;browser&#8221; div is where we will have our list of thumbnails.
</p>
<p>To populate the thumbnail list we&#8217;re going to use the jQuery <a href="http://api.jquery.com/jQuery.getJSON/" target="_blank">getJSON</a> function. To use the Flickr API all we need to do is specify the API key, the method we are using and the photo set id. We also set the format we want the response to be in.
</p>
<pre class="brush: js">
$.getJSON(url, {
    method: 'flickr.photosets.getInfo',
    api_key: settings.api_key,
    photoset_id: settings.photoset_id,
    format: 'json'
}).success(function(state) {
    gallery.children('h2').html(state.photoset.title._content);
    gallery.children('h3').html(state.photoset.description._content);
}).fail(function(state) { 
    alert("Unable to retrieve photo set information"); 
});
</pre>
<p>Because we specified the format as &#8216;json&#8217; we can easily access the values of the response. Here I&#8217;m setting the title and description of the photo set in the header tags we added earlier.
</p>
<p>Next we want to get the list of images, so this time we will nest the second getJSON function in the success function of our photo set info.
</p>
<pre class="brush: js">
$.getJSON(url, {
    method: 'flickr.photosets.getInfo',
    api_key: settings.api_key,
    photoset_id: settings.photoset_id,
    format: 'json'
}).success(function(state) {
    gallery.children('h2').html(state.photoset.title._content);
    gallery.children('h3').html(state.photoset.description._content);
    
    $.getJSON(url, {
        method: 'flickr.photosets.getPhotos',
        api_key: settings.api_key,
        media: 'photos',
        photoset_id: settings.photoset_id,
        format: 'json',
        extras: 'url_sq,url_m,date_taken,tags'
    }).success(function(state) {
        var list = gallery.find('ul:first');
        list.html('');
        
        $.each(state.photoset.photo, function(){
            if(this.isprimary == "1") {
                var viewport = gallery.children('div.viewport');
                viewport.html('');
                viewport.html('&lt;img src="' + this.url_m + '" /&gt;&lt;div class="image-info"&gt;' + this.title + '&lt;br /&gt;' + this.tags + '&lt;/div&gt;');
            }
            list.append('&lt;li&gt;&lt;img src="' + this.url_sq + '" ' +
                'data-title="' + this.title + '" ' +
                'data-url="' + this.url_m + '" ' +
                ( this.date_taken ? 'data-date="' + this.date_taken + '" ' : '' ) +
                'data-tags="' + this.tags + '" ' +
                '/&gt;&lt;/li&gt;');
        });
        
    }).fail(function(state) { 
        alert("Unable to retrieve photo set"); 
    });
}).fail(function(state) { 
    alert("Unable to retrieve photo set information"); 
});
</pre>
<p>As you can see the getJSON parameters are very similar and when the function returns successfully we use the <a href="http://api.jquery.com/jQuery.each/" target="_blank">jQuery each</a> function to loop through all of the images. I am also appending the additional information we want to the img element using the data-* attributes. This allows me to keep all of the data I need next to the correct thumbnail. It also makes it very easy to parse it back out.
</p>
<p>The Flickr API also tells me what the primary image for the set is, so I can automatically display this image in our viewport div.
</p>
<p>To actually change the image in the viewport, we&#8217;ll need to add a click event to the list. We add this to the list and not each list item or image as it creates only one event and is more efficient.
</p>
<pre class="brush: js">
list.on('click', view_image);
</pre>
<p>The view_image function we are referencing here will determine what element was clicked and then if it&#8217;s the thumbnail image, it will parse out the data-* attributes and update the image and headings as appropriate.
</p>
<pre class="brush: js">
function view_image(event) {
    var target = $(event.target);
    
    if(target.is('img')) {
        var url = target.attr('data-url');
        var cache = new Image();
        cache.src = url;
        var gallery = target.parents('.flickr-gallery:first').children('div.viewport');
        var info = gallery.children('div.image-info');
        var image = gallery.children('img');
        image.fadeOut('slow', function () {
            image.attr('src', url);
            image.fadeIn('slow');
            info.html(target.attr('data-title') + '&lt;br /&gt;' + target.attr('data-tags'));
        });
    }
}
</pre>
<p>There is an additional pieces of logic in the above JavaScript, it&#8217;s a quick hack to start the download of the image so that it is available for the animation. This, combined with the fade delay provides a smoother transition for the effect.
</p>
<pre class="brush: js">
var cache = new Image();
cache.src = url;
</pre>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery-no-css.png"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery-no-css.png" alt="gallery-no-css" width="1232" height="841" class="aligncenter size-full wp-image-4129" /></a></p>
<h2>CSS</h2>
<p>For this gallery I have decided on a side by side view. To achieve this, I set the widths on my gallery viewport and browser div elements. I then use the CSS property &#8216;float&#8217; with the value &#8216;left&#8217; to move the 2 elements next to each other. It&#8217;s always a good idea to reset the float after wards. I have done this by appending an empty div with the class &#8220;clear&#8221; on it. The &#8220;clear&#8221; class uses the &#8216;clear&#8217; property to ensure that the float does not cause any unwanted behavior.
</p>
<pre class="brush: js">
div.flickr-gallery {
    width: 800px;
    height: 550px;
    overflow: hidden;
}

div.flickr-gallery .clear{
    clear: both;
}

div.flickr-gallery &gt; div{
    float: left;
    max-height: 100%;
}

div.flickr-gallery div.viewport{
    width: 500px;
    position: relative;
}

div.flickr-gallery div.browser{
    width: 300px;
    overflow-y: auto;
}

div.flickr-gallery ul{
    list-style: none;
    padding: 0px;
    margin: 0px;
}

div.flickr-gallery ul li{
    float: left;
    width: 75px;
    height: 75px;
    margin-left: 8px;
    margin-bottom: 8px;
}
</pre>
<p>I also want to have the image title and the tags display over the image. To do this I ensure that the viewport is has a position value of relative, and the image-info div is positioned absolutely. I also adjust the opacity level so that it&#8217;s slightly transparent.
</p>
<pre class="brush: js">div.flickr-gallery div.viewport .image-info {
    position: absolute;
    width: 500px;    
    top: 0px;
    text-align: center;
    background-color: #ffffff;
    -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";
    filter: alpha(opacity=75);
    -moz-opacity: 0.75;
    opacity: 0.75;
}
</pre>
<p>I have also set the overflow properties on main div to hidden and the overflow-y property on the browser div to auto so that the thumbnail list stays with in the bounds of the gallery.
</p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery.png"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/gallery.png" alt="gallery" width="821" height="637" class="aligncenter size-full wp-image-4128" /></a></p>
<h2>Using the plug-in</h2>
<p>To use the plug-in all you need is an empty div and the following jQuery.</p>
<pre class="brush: js">
&lt;div id="gallery"&gt;
&lt;/div&gt;
&lt;script type="text/javascript"&gt;
$(document).on('ready', function(){
    $('div#gallery').flickr({ photoset_id: '72157626766436507'});
});
&lt;/script&gt;
</pre>
<p>Depending on your Internet connection and the performance of the Flickr API, you may want to add a loading animation to the gallery. It&#8217;s as simple as adding an animated gif and then when the final getJSON method returns and is finished parsing hiding it.</p>
<p>
<a href='http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/index2.html' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/index.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/creating-a-jquery-gallery-for-flickr/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a Google Maps shortcode for WordPress</title>
		<link>http://www.developerdrive.com/2013/05/creating-a-google-maps-shortcode-for-wordpress/</link>
		<comments>http://www.developerdrive.com/2013/05/creating-a-google-maps-shortcode-for-wordpress/#comments</comments>
		<pubDate>Thu, 16 May 2013 22:15:44 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[Web Services]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4112</guid>
		<description><![CDATA[Introduction This week we&#8217;ll be extending WordPress by adding a custom shortcode. This shortcode will allow us to add a Google Map to a post using a simple piece of text in a post or page. Shortcodes are a quick &#8230; <a href="http://www.developerdrive.com/2013/05/creating-a-google-maps-shortcode-for-wordpress/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>This week we&#8217;ll be extending <a href="http://wordpress.org/" target="_blank">WordPress</a> by adding a custom <a href="http://codex.wordpress.org/Shortcode_API" target="_blank">shortcode</a>. This shortcode will allow us to add a <a href="https://developers.google.com/maps/documentation/javascript/reference" target="_blank">Google Map</a> to a post using a simple piece of text in a post or page. Shortcodes are a quick and easy way to add functionality to a post in WordPress. They look something like this:
</p>
<pre class="brush: js">
[ gallery ids="1,2,3,4"]
</pre>
<p>We&#8217;ll also using PHP based <a href="http://php.net/manual/en/language.oop5.php" target="_blank">Object Orientated Programming</a> (OOP) to create our shortcode. We&#8217;ll create a basic class, that has some static functions with in. We&#8217;ll be using static methods because we want some level of state to be kept.
</p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/map.png"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/map.png" alt="map" width="527" height="521" class="aligncenter size-full wp-image-4116" /></a></p>
<h2>Getting started</h2>
<p>Our basic class will be pretty simple, it needs to register the shortcode and provide functions to render the shortcode and add the API JavaScript file.
</p>
<pre class="brush: js">
class GoogleMap_Shortcode {
    static function init() {
    }

    static function render_shortcode($atts) {      
    }
	
    static function enqueue_map_javascript() {
    }
}
</pre>
<p>To create our shortcode we&#8217;ll need to first register it and then define it how it works and what HTML it creates. We&#8217;ll also need to figure out a way to add in the Google Maps API JavaScript. To add a shortcode to WordPress we use the <a href="http://codex.wordpress.org/Function_Reference/add_shortcode" target="_blank">add_shortcode</a> function. This function takes 2 parameters, the name of the shortcode and the function to call when we want to render the shortcode. We&#8217;re going to call our shortcode &#8220;map&#8221; and our render function &#8220;render_shortcode&#8221;. Since we&#8217;re using a class, we&#8217;re going to take advantage of the PHP <a href="http://php.net/manual/en/language.constants.predefined.php" target="_blank">Magic Constant</a> &#8220;__CLASS__&#8221;, this will maintain a reference to our class and ensure our function can be called.
</p>
<pre class="brush: js">
add_shortcode('map', array(__CLASS__, 'render_shortcode'));
</pre>
<p>
The add_shortcode needs to be called only once, so we&#8217;re going to call it from our &#8220;init&#8221; function, and then immediately after our class definition call it.
</p>
<pre class="brush: js">
class GoogleMap_Shortcode {
    static function init() {
        wp_enqueue_script( 'jquery' );
        add_shortcode('map', array(__CLASS__, 'render_shortcode'));
    }

    static function render_shortcode($atts) {
    }
    static function enqueue_map_javascript() {
    }
}

GoogleMap_Shortcode::init();
</pre>
<p>Now we have to start getting our shortcode to render. First up we need to define the attributes of the shortcode. One of these will be the Google Maps API key, you can sign up for one <a href="https://developers.google.com/maps/signup" target="_blank">here</a>, we&#8217;ll also want to be able to specify map related settings like the <a href="https://developers.google.com/maps/documentation/javascript/maptypes" target="_blank">map type</a>, zoom and naturally the co-ordinates we want to center on. We can define their default values using the <a href="http://codex.wordpress.org/Function_Reference/shortcode_atts" target="_blank">shortcode_atts</a> and <a href="http://php.net/manual/en/function.extract.php" target="_blank">extract</a> functions.
</p>
<pre class="brush: js">
extract( shortcode_atts( array(
    'api_key' =&gt; false,
    'id' =&gt; 'map-canvas-1',
    'class' =&gt; '',
    'zoom' =&gt; '18',
    'coords' =&gt; '53.339381, -6.260405',
    'type' =&gt; 'roadmap',
    'width' =&gt; '480px',
    'height' =&gt; '480px'
), $atts ) );
</pre>
<p>Here you can see I&#8217;m defaulting all the possible attributes, this means the user doesn&#8217;t have to specify them all in the shortcode if they don&#8217;t need to. The API key is needed for all instances of the shortcode, but we can cache the key from the 1st call so it does not need to be specified on all shortcodes on a page. If we declare a static variable inside the class and use it where ever we are in the class it will only need to be set once.
</p>
<pre class="brush: js">
class GoogleMap_Shortcode {
    static $api_key = false;

    static function init() {
        wp_enqueue_script( 'jquery' );
        add_shortcode('map', array(__CLASS__, 'render_shortcode'));
    }

    static function render_shortcode($atts) {
        extract( shortcode_atts( array(
            'api_key' =&gt; false,
            'id' =&gt; 'map-canvas-1',
            'class' =&gt; '',
            'zoom' =&gt; '18',
            'coords' =&gt; '53.339381, -6.260405',
            'type' =&gt; 'roadmap',
            'width' =&gt; '480px',
            'height' =&gt; '480px'
        ), $atts ) );
        
        if(!self::$api_key &amp;&amp; $api_key) {
            self::$api_key = $api_key;
        }      
    }
    static function enqueue_map_javascript() {
    }
}

GoogleMap_Shortcode::init();
</pre>
<p>There are four default <a href="https://developers.google.com/maps/documentation/javascript/maptypes" target="_blank">map types</a>, to simplify the specification of which one we want to use we can use a <a href="http://ie1.php.net/manual/en/control-structures.switch.php" target="_blank">switch</a> statement
</p>
<pre class="brush: js">
$map_type_id = "google.maps.MapTypeId.ROADMAP";
switch ($type) {
    case "roadmap":
        $map_type_id = "google.maps.MapTypeId.ROADMAP";
        break;
    case "satellite":
        $map_type_id = "google.maps.MapTypeId.SATELLITE";
        break;
    case "hybrid":
        $map_type_id = "google.maps.MapTypeId.HYBRID";
        break;
    case "terrain":
        $map_type_id = "google.maps.MapTypeId.TERRAIN";
        break;
}
</pre>
<p>Now we have everything we need to actually render our shortcode. First we&#8217;ll need to specify the div we are going to wrap the map in, and then we&#8217;ll need some JavaScript to initialize the map.
</p>
<pre class="brush: js">
class GoogleMap_Shortcode {
    static $api_key = false;

    static function init() {
        wp_enqueue_script( 'jquery' );
        add_shortcode('map', array(__CLASS__, 'render_shortcode'));
    }

    static function render_shortcode($atts) {
        extract( shortcode_atts( array(
            'api_key' =&gt; false,
            'id' =&gt; 'map-canvas-1',
            'class' =&gt; '',
            'zoom' =&gt; '18',
            'coords' =&gt; '53.339381, -6.260405',
            'type' =&gt; 'roadmap',
            'width' =&gt; '480px',
            'height' =&gt; '480px'
        ), $atts ) );
        
        if(!self::$api_key &amp;&amp; $api_key) {
            self::$api_key = $api_key;
        }
        
        $return = "";
        
        $map_type_id = "google.maps.MapTypeId.ROADMAP";
        
        switch ($type) {
            case "roadmap":
                $map_type_id = "google.maps.MapTypeId.ROADMAP";
                break;
            case "satellite":
                $map_type_id = "google.maps.MapTypeId.SATELLITE";
                break;
            case "hybrid":
                $map_type_id = "google.maps.MapTypeId.HYBRID";
                break;
            case "terrain":
                $map_type_id = "google.maps.MapTypeId.TERRAIN";
                break;
        }
        
        if(self::$api_key) {
            $return = '&lt;div id="'.$id.'" class="map-canvas '.$class.'" style="width:'.$width.';height:'.$height.';" &gt;&lt;/div&gt;';
            
            $return .= '&lt;script type="text/javascript"&gt;';
            $return .= 'jQuery(document).on("ready", function(){ ';
            $return .= 'var options = { center: new google.maps.LatLng('.$coords.'),';
            $return .= 'zoom: ' . $zoom . ', mapTypeId: ' . $map_type_id . ' };';
            $return .= 'var map = new google.maps.Map(document.getElementById("'.$id.'"), options);';
            $return .= 'var marker = new google.maps.Marker({ position: new google.maps.LatLng('.$coords.'), map: map });';
            $return .= '});&lt;/script&gt;';
        } else {
            $return = "&lt;div&gt;&lt;p&gt;Please specify your Google Maps API key&lt;/p&gt;&lt;/div&gt;";
        }
        
        return $return;        
    }
    static function enqueue_map_javascript() {
    }
}

GoogleMap_Shortcode::init();
</pre>
<p>I&#8217;ve also added a check here to see if the api key is set, and if not display a message to that effect. Next up we need to include the actual Map API JavaScript, to do that we&#8217;re going to use the <a href="http://codex.wordpress.org/Function_Reference/wp_enqueue_script" target="_blank">wp_enqueue_script</a> function. Because we&#8217;re checking that the api key is we are by default preventing the JavaScript from being loaded when the shortcode is not being used.
</p>
<pre class="brush: js">
static function enqueue_map_javascript() {
    if ( ! self::$api_key )
        return;

    wp_enqueue_script( 'map-js', 
        "https://maps.googleapis.com/maps/api/js?key=" . self::$api_key . "&amp;sensor=true"
    );
}
</pre>
<p>To make sure this script is included in the right place, we&#8217;re going to add an action to the wp_footer hook. We&#8217;ll add this to our init function
</p>
<pre class="brush: js">
add_action('wp_footer', array(__CLASS__, 'enqueue_map_javascript'));
</pre>
<p>And that&#8217;s it, add this to your functions.php or use an include/require and you&#8217;ll be able to add Google Maps to your posts. To actually add it to a post, you can use the following:</p>
<pre class="brush: js">
[map api_key="INSERT YOUR API KEY"]

[map api_key="INSERT YOUR API KEY" id="map-2" coords="52.339381, -4.260405"]

[map api_key="INSERT YOUR API KEY" id="map-2" coords="52.339381, -4.260405" zoom="5" type="satellite"]
</pre>
</p>
<p>It was pointed out to me that I forgot to include the pointer for the map. I&#8217;ve updated the sample above and the download. This is the new line to add a marker.
</p>
<pre class="brush: js">
$return .= 'var marker = new google.maps.Marker({ position: new google.maps.LatLng('.$coords.'), map: map });';
</pre>
<p>Here&#8217;s the updated full class.<br />
<a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/map_shortcode1.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/creating-a-google-maps-shortcode-for-wordpress/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Creating a simple to-do application – Part 4</title>
		<link>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-part-4/</link>
		<comments>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-part-4/#comments</comments>
		<pubDate>Mon, 13 May 2013 23:17:50 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4101</guid>
		<description><![CDATA[Introduction This week in part 4 of creating our simple to-do application we&#8217;ll be learning how to send email notifications/reminders. To do this we&#8217;ll be using the PHP mail method and learning how to schedule repeatable tasks on Linux using &#8230; <a href="http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-part-4/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>This week in part 4 of creating our simple to-do application we&#8217;ll be learning how to send email notifications/reminders. To do this we&#8217;ll be using the PHP <a href="http://php.net/manual/en/function.mail.php" target="_blank">mail</a> method and learning how to schedule repeatable tasks on Linux using <a href="http://en.wikipedia.org/wiki/Cron" target="_blank">cron</a>. The equivalent process for Windows is the task scheduler and it is pretty self explanatory. WordPress has what it calls the wp-cron alternative, but it&#8217;s use is limited as it requires someone to actually use the site.
</p>
<h2>Cron</h2>
<p>Cron is the Unix/Linux schedule process, it allows users to configure tasks that are executed at defined intervals. It&#8217;s not exactly considered to be particularly user friendly, so many hosting providers have a simplified UI to help set it up. In any event, it is still beneficial to know the basics.
</p>
<p>
Cron jobs are specified in the &#8216;crontab&#8217; file (typically in the /etc folder) Crontab is short for cron table, and as the name suggests it is a series of columns that determine when and what is run, split up in to tasks on each new line of the file. Each column has a specific time related function, with the last being the actual file that is executed. The column order is as follows:
</p>
<ol>
<li>Minutes (0 &#8211; 59)</li>
<li>Hours (0 &#8211; 23)</li>
<li>Day of month (1 &#8211; 31)</li>
<li>Month (1 &#8211; 12)</li>
<li>Day of week (0 &#8211; 6)</li>
<li>File or command</li>
</ol>
<p>You can specify any combination of the above columns, disabling columns is by way of the * character. This configuration method can lead to some confusing schedules being created. You can specify that any Monday, that is the 1st of the month at 11am execute some task. It would look something like this:
</p>
<pre class="brush: js">
* 11 1 * 1 task.php
</pre>
<p>Overall it&#8217;s quiet a powerful system that you can configure to do almost anything. There are a few things to note, don&#8217;t schedule a task that takes 5 minutes to execute every minute. Cron will execute this and you will end up with duplicate scripts taking up resources and potentially causing issues.
</p>
<h2>PHP mail</h2>
<p>PHP has a built in email function that is incredible easy to use. It uses the default SMTP server details in your php.ini configuration to send emails. Generally if you&#8217;re running in a hosted server this will be configured, if it&#8217;s not or if you want to use a different server you can use the <a href="http://php.net/manual/en/function.ini-set.php" target="_blank">ini_set</a> function to update your configuration. The ini_set command would look something like this:
</p>
<pre class="brush: js">
ini_set("SMTP", "smtpserver.yourdomain.com");
</pre>
<p>To actually send an email, you have to provide the following information:</p>
<ul>
<li>The &#8220;to&#8221; address</li>
<li>The subject</li>
<li>The message</li>
<li>Additional headers</li>
<li>Additional parameters</li>
</ul>
<p>The &#8220;to&#8221; address can be a simple one line &#8220;you@domain.com&#8221; or either a csv of email addresses. You can also specify the name of the person in this field as follows:
</p>
<pre class="brush: js">
Bob Bobberson &lt;bobby@bobberson.com&gt;
</pre>
<p>Another quirk about the mail function is that the message body must be split on to multiple lines of no more than 70 characters. To indicate a new line you need to use the \r\n (CRLF) characters.
</p>
<p>There is only one header that must be sent and that is the &#8220;from&#8221; header. You can also specify the &#8220;reply-to&#8221; or CC and BCC headers. The CC and BCC values have the same format as the &#8220;to&#8221; field. A full email would look something like this:
</p>
<pre class="brush: js">
$from = "To Do Application &lt;to-do@domain.com&gt;";
$to = "Bob Bobberson &lt;bobby@bobberson.com&gt;";
$subject = "How to send an email in PHP";
$message = "Read this! Sending an email in PHP is really easy";
$headers = "From: To Do Application ";
$headers .= "Bcc: Another attendee ";
$headers .= 'Reply-To: ' . $from;
$headers .= 'Return-Path: ' . $from;
mail($to, $subject, $message, $headers);
</pre>
<p>I should also note, that if you are planning on sending lots of emails there are better alternatives out there that will queue emails and perform better in general. The mail function is not designed to scale quickly or well, however for once off emails it is sufficient
</p>
<h2>Building our script</h2>
<p>So now we know how we&#8217;re going to schedule our emails and we also know how we&#8217;re going to send them, next up is what are we going to send? To do this it&#8217;s back to our MySQL database and a simple query. Here we&#8217;re going to do a simple select from our &#8216;tasks&#8217; table where the &#8216;task_date&#8217; is between 15 and 20 minutes from now. To do that we use the MySQL <a href="https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_adddate" target="_blank">ADDDATE</a> function.
</p>
<pre class="brush: js">
SELECT `task_id`, `user_firstname`, `user_surname`, `user_email`, `task_name`, 
    `task_priority`, `task_color`, `task_description`, `task_attendees`, `task_date` 
FROM `tasks` AS t 
INNER JOIN `users` AS u 
    ON u.user_id = t.user_id 
WHERE `task_date` &gt;= ADDDATE(NOW(), INTERVAL 15 MINUTE) 
    AND `task_date` &lt;= ADDDATE(NOW(), INTERVAL 20 MINUTE)
</pre>
<p>The above SQL combined with a cron job executing every 5 minutes should give us pretty reliable email notifications without putting load on a server. You may also notice the &#8220;INNER JOIN&#8221; syntax. This allows me to query 2 tables where both tables have the column &#8216;user_id&#8217;. In this way I can get the users email, firstname and surname values as well as all of their scheduled tasks in one go. The INNER JOIN also dictates that both tables must have matching rows to return, if only one has to have a matching row I could use what is called a &#8220;LEFT JOIN&#8221;.
</p>
<p>Our full email.php script looks something like this:</p>
<pre class="brush: js">
&lt;?php
require_once('database.php');

$from = "To Do Application &lt;to-do@domain.com&gt;";
$default_headers = "From: $from\r\n";
$default_headers .= "Reply-To: $from\r\n";
$default_headers .= "Return-Path: $from\r\n";

$query = $connection-&gt;prepare("SELECT `task_id`, `user_firstname`, `user_surname`, `user_email`, `task_name`, `task_priority`, `task_color`, `task_description`, `task_attendees`, `task_date` FROM `tasks` AS t INNER JOIN `users` AS u ON u.user_id = t.user_id WHERE `task_date` &gt;= ADDDATE(NOW(), INTERVAL 15 MINUTE) AND `task_date` &lt;= ADDDATE(NOW(), INTERVAL 20 MINUTE)");
$query-&gt;execute();

$query-&gt;bind_result($id, $firstname, $surname, $email, $name, $priority, $color, $description, $attendees, $date);

while ($query-&gt;fetch()) {
    $to = "$firstname $surname &lt;$email&gt;";
    $headers = $default_headers;
    if(!empty($attendees)){
        $headers .= $headers . "\nBcc: $attendees";
    }
    
    $subject = "Upcoming priority $priority task - $name";
    $message = "Don't forget $name is at $date!\r\n";
    
    if(!empty($description)){
        $message .= "$description\r\n";
    }
    
    mail($to, $subject, $message, $headers);
}

$query-&gt;close();

?&gt;
</pre>
<p>It&#8217;s pretty basic at the moment, but you can easily update the above to use HTML to send much prettier email alerts. To do that you need to set 2 additional email headers:
</p>
<pre class="brush: js">
$default_headers .= "MIME-Version: 1.0\r\n";
$default_headers .= "Content-type: text/html; charset=iso-8859-1\r\n";
</pre>
<p>Once you&#8217;ve got that done you can update the $message variable to read in a HTML template from file or just manually write the HTML as you need it.
</p>
<pre class="brush: js">
$message = "&lt;html&gt;\r\n" .
"&lt;head&gt;&lt;title&gt;Upcoming priority $priority task - $name&lt;/title&gt;&lt;/head&gt;\r\n" .
"&lt;body&gt;\r\n" . 
"&lt;p&gt;Upcoming priority $priority task - $name&lt;/p&gt;\r\n" .
"&lt;p&gt;Don't forget $name is at $date!&lt;br/&gt;\r\n" .
"$description\r\n" .
"&lt;/p&gt;\r\n" .
"&lt;/body&gt;&lt;/html&gt;\r\n";
</pre>
<h2>Putting it all together</h2>
<p>Now that we have our PHP email script and we know how to set up our cron, there is just a few more things we need to do. At the top of the we need to prep-end the following check
</p>
<pre class="brush: js">
if( php_sapi_name() != 'cli' )
    die("You don't have permissions to run this file");
</pre>
<p>This check ensures that the file can only be run from the command line. This means that even if the file is in your web folder (it probably shouldn&#8217;t) only someone at the command line or the cron job can execute it.
</p>
<pre class="brush: js">
&lt;?php
if( php_sapi_name() != 'cli' )
    die("You don't have permissions to run this file");
    
require_once('database.php');

$from = "To Do Application &lt;to-do@domain.com&gt;";
$default_headers = "From: $from\r\n";
$default_headers .= "Reply-To: $from\r\n";
$default_headers .= "Return-Path: $from\r\n";
$default_headers .= "MIME-Version: 1.0\r\n";
$default_headers .= "Content-type: text/html; charset=iso-8859-1\r\n";

$query = $connection-&gt;prepare("SELECT `task_id`, `user_firstname`, `user_surname`, `user_email`, `task_name`, `task_priority`, `task_color`, `task_description`, `task_attendees`, `task_date` FROM `tasks` AS t INNER JOIN `users` AS u ON u.user_id = t.user_id WHERE `task_date` &gt;= ADDDATE(NOW(), INTERVAL 15 MINUTE) AND `task_date` &lt;= ADDDATE(NOW(), INTERVAL 20 MINUTE)");
$query-&gt;execute();

$query-&gt;bind_result($id, $firstname, $surname, $email, $name, $priority, $color, $description, $attendees, $date);

while ($query-&gt;fetch()) {
    $to = "$firstname $surname &lt;$email&gt;";
    $headers = $default_headers;
    if(!empty($attendees)){
        $headers .= $headers . "\nBcc: $attendees";
    }
    
    $subject = "Upcoming priority $priority task - $name";
    $message = "Don't forget $name is at $date!\r\n";
    $message = "&lt;html&gt;\r\n" .
        "&lt;head&gt;&lt;title&gt;Upcoming priority $priority task - $name&lt;/title&gt;&lt;/head&gt;\r\n" .
        "&lt;body&gt;\r\n" . 
        "&lt;p&gt;Upcoming priority $priority task - $name&lt;/p&gt;\r\n" .
        "&lt;p&gt;Don't forget $name is at $date!&lt;br/&gt;\r\n" .
        "$description\r\n" .
        "&lt;/p&gt;\r\n" .
        "&lt;/body&gt;&lt;/html&gt;\r\n";    
    
    mail($to, $subject, $message, $headers);
}

$query-&gt;close();

?&gt;
</pre>
<p>We also need to configure our cron to execute our email task every 5 minutes</p>
<pre class="brush: js">
*/5 * * * * email.php
</pre>
<p>As always I have the full demo hosted <a href="http://schnittger.me/tutorials/to-do/part6/" target="_blank">here</a>, I have however disabled the sending of email. I don&#8217;t want to be caught sending Spam.
</p>
<p>
<a href='http://schnittger.me/tutorials/to-do/part6/' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/part6.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-part-4/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Creating a simple jQuery plugin for Pinterest</title>
		<link>http://www.developerdrive.com/2013/05/creating-a-simple-jquery-plugin-for-pinterest/</link>
		<comments>http://www.developerdrive.com/2013/05/creating-a-simple-jquery-plugin-for-pinterest/#comments</comments>
		<pubDate>Thu, 09 May 2013 22:51:34 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Social Apps]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4088</guid>
		<description><![CDATA[Introduction Plugins play a great part in the success of jQuery. There are hundreds of them out there, and having the ability to create your own is a great skill to have. With all of the interest in Pinterest (no &#8230; <a href="http://www.developerdrive.com/2013/05/creating-a-simple-jquery-plugin-for-pinterest/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>Plugins play a great part in the success of jQuery. There are hundreds of them out there, and having the ability to create your own is a great skill to have. With all of the interest in <a href="http://pinterest.com/" target="_blank">Pinterest</a> (no pun intended), I thought it would be a good idea to do up a quick and simple Pinterest sharing plugin for jQuery.
</p>
<h2>Getting started</h2>
<p>First we need to grab the jQuery plugin <a href="http://docs.jquery.com/Plugins/Authoring" target="_blank">boilerplate</a>
</p>
<pre class="brush: js">
(function( $ ) {
    $.fn.pinterest = function(options) {
        
        var settings = $.extend( {                    
        }, options);
        
        return this.each(function() {    
        
        });

    };
})( jQuery );
</pre>
<p>This boilerplate provides us with the ability to specify default configuration values (settings) and ensures that the infamous jQuery chaining ability is maintained (the return this.each). I&#8217;m going to take advantage of the default configuration values by specifying the default Pinterest image we&#8217;ll be using. This can be overridden when initializing the plugin by specifying the url to the preferred image.
</p>
<h3>Default image</h3>
<p><img src="http://business.pinterest.com/assets/img/builder/builder_opt_1.png" width="40" height="20" class="alignnone" /><br />
</p>
<h3>Custom image</h3>
<p><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/pinterest-alt.png" alt="pinterest-alt" width="52" height="25" class="aligncenter size-full wp-image-4090" /></p>
<pre class="brush: js">
(function( $ ) {
    $.fn.pinterest = function(options) {
        
        var settings = $.extend( {
            'button' : 'http://business.pinterest.com/assets/img/builder/builder_opt_1.png'
        }, options);
            
        return this.each(function() {    
        
        });

    };
})( jQuery );

$(document).on('ready', function(){
    $('img#share').pinterest({ button: 'pinterest-alt.png'});
});
</pre>
<p>Now that we have a basic framework done, let&#8217;s start adding some functionality. We need to update the this.each function to initialize each image and add some events for us. I&#8217;ve chosen to have the icon hover over the image being shared. To do this, I need to <a href="http://docs.jquery.com/Manipulation/wrap" target="_blank">wrap</a> the image in a span element that positioned &#8216;relative&#8217;, I then need to <a href="http://docs.jquery.com/Manipulation/append" target="_blank">append</a> the share image and position it absolutely. I&#8217;m going with the bottom right, but feel free to have a play
</p>
<pre class="brush: js">(function( $ ) {
    $.fn.pinterest = function(options) {
        
        var settings = $.extend( {
            'button' : 'http://business.pinterest.com/assets/img/builder/builder_opt_1.png'
        }, options);
                    
        return this.each(function() {    
            img = $(this);
            img.wrap('&lt;span class="pin-it" style="position: relative;" /&gt;');
            img.parent('span.pin-it').append('&lt;img src="' + settings.button + '" style="display: none;position: absolute; bottom: 20px; right: 20px;cursor: hand; cursor: pointer;" /&gt;');
        });
    };
})( jQuery );

$(document).on('ready', function(){
    $('img#share').pinterest({ button: 'pinterest-alt.png'});
});
</pre>
<p>Now that the elements that we need are there, we can start attaching our events. I&#8217;m going to use the hover and click events. When the user hovers over the main image, the share button will appear.</p>
<pre class="brush: js">
(function( $ ) {
    $.fn.pinterest = function(options) {
        
        var settings = $.extend( {
            'button' : 'http://business.pinterest.com/assets/img/builder/builder_opt_1.png'
        }, options);
        
        function on_click () {         
        };
        
        function on_hover_in() {
            $(this).siblings('img:first').show(500);
        };    
        
        return this.each(function() {    
            img = $(this);
            img.wrap('&lt;span class="pin-it" style="position: relative;" /&gt;');
            img.parent('span.pin-it').append('&lt;img src="' + settings.button + '" style="display: none;position: absolute; bottom: 20px; right: 20px;cursor: hand; cursor: pointer;" /&gt;');
            img.hover(on_hover_in);
            img.siblings('img:first').on('click', on_click);
        });

    };
})( jQuery );

$(document).on('ready', function(){
    $('img#share').pinterest({ button: 'pinterest-small.png'});
});
</pre>
<p>The on_click event looks like the following</p>
<pre class="brush: js">
function on_click () {
    img = $(this).siblings('img:first');
    description = img.attr('title');
    url = document.location;
    media = img.attr('src');

    var pin_url = 'http://pinterest.com/pin/create/button/?url=' + encodeURIComponent( url ) +
        '&amp;media=' + encodeURIComponent( media ) +
        '&amp;description=' + encodeURIComponent( description );
    
    window.open(pin_url, 'Pin - ' + description, 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');
    $(this).hide(1000);
};
</pre>
<p>It&#8217;s grabbing the image we want to share, and then reading it&#8217;s title and src attributes. It&#8217;s also getting the current pages url using document.location. The Pinterest share url has 3 parameters that we are interested in:</p>
<ul>
<li>url &#8211; the page we are sharing from</li>
<li>media &#8211; the image we want to share</li>
<li>description &#8211; the text we want to share with the image</li>
</ul>
<p>When I&#8217;m creating the share url, I&#8217;m also using the JavaScript <a href="https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/encodeURIComponent" target="_blank">encodeURIComponent</a> function to make sure the values are properly escaped.
</p>
<p>One final thing we need to allow for is if the image src url is relative or not. If it&#8217;s relative we&#8217;ll need to update the media url to be fully qualified.</p>
<pre class="brush: js">
function getUrl(src){
    var url = document.location.toString();
    var http = /^https?:\/\//i;
    if (!http.test(src)) {
        if(src[0] == '/'){
            url = url.substring(0, url.lastIndexOf('/')) + src;
        } else {
            url = url.substring(0, url.lastIndexOf('/')) + '/' + src;
        }
    } else {
        url = src;
    }
    
    return url;
};
</pre>
<p>This function is checking to see if the provided src URL starts with http or not. If it doesn&#8217;t it then creates an absolute url and returns it.</p>
<p>And that&#8217;s it, we now have a fully functional jQuery plugin for sharing images on Pinterest. You can see the full HTML and JavaScript or download it below. The image I&#8217;m sharing by the way is <a href="http://en.wikipedia.org/wiki/Three_Musicians" target="_blank">Picasso&#8217;s Three Musicians</a>
</p>
<pre class="brush: js">
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;
    &lt;title&gt;Creating a simple Pinterest! jQuery&lt;/title&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
    &lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"&gt;&lt;/script&gt;
        
    &lt;script type="text/javascript"&gt;    
        
        (function( $ ) {
            $.fn.pinterest = function(options) {
                
                var settings = $.extend( {
                    'button' : 'http://business.pinterest.com/assets/img/builder/builder_opt_1.png'
                }, options);
                
                function getUrl(src){
                    var url = document.location.toString();
                    var http = /^https?:\/\//i;
                    if (!http.test(src)) {
                        if(src[0] == '/'){
                            url = url.substring(0, url.lastIndexOf('/')) + src;
                        } else {
                            url = url.substring(0, url.lastIndexOf('/')) + '/' + src;
                        }
                    } else {
                        url = src;
                    }
                    
                    return url;
                };
                
                function on_click () {
                    img = $(this).siblings('img:first');
                    description = img.attr('title');
                    url = document.location;
                    media = getUrl( img.attr('src') );
    
                    var pin_url = 'http://pinterest.com/pin/create/button/?url=' + url +
                        '&amp;media=' + media +
                        '&amp;description=' + description;
                    
                    window.open(pin_url, 'Pin - ' + description, 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');
                    $(this).hide(1000);
                };
                
                function on_hover_in() {
                    $(this).siblings('img:first').show(500);
                };
                    
                return this.each(function() {    
                    img = $(this);
                    img.wrap('&lt;span class="pin-it" style="position: relative;" /&gt;');
                    img.parent('span.pin-it').append('&lt;img src="' + settings.button + '" style="display: none;position: absolute; bottom: 20px; right: 20px;cursor: hand; cursor: pointer;" /&gt;');
                    img.hover(on_hover_in);
                    img.siblings('img:first').on('click', on_click);
                });

            };
        })( jQuery );
    &lt;/script&gt;    
&lt;/head&gt;
&lt;body&gt;
&lt;div id="page"&gt;
    &lt;!-- [banner] --&gt;
    &lt;header id="banner"&gt;
        &lt;hgroup&gt;
            &lt;h1&gt;Creating a simple Pinterest! jQuery&lt;/h1&gt;
        &lt;/hgroup&gt;        
    &lt;/header&gt;
    &lt;!-- [content] --&gt;
    &lt;section id="content"&gt;
        &lt;img id="share" src="picasso.jpg" title="Three Musicians" /&gt;
    &lt;/section&gt;
    
    &lt;script&gt;
        $(document).on('ready', function(){
            $('img#share').pinterest({ button: 'pinterest-small.png'});
        });
    &lt;/script&gt;
&lt;/div&gt;
&lt;!-- [/page] --&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>
<a href='http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/index1.html' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/jquery-pinterest1.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/creating-a-simple-jquery-plugin-for-pinterest/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Creating a simple to-do application – Part 3</title>
		<link>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-%e2%80%93-part-3/</link>
		<comments>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-%e2%80%93-part-3/#comments</comments>
		<pubDate>Mon, 06 May 2013 22:17:02 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4067</guid>
		<description><![CDATA[Introduction So far we&#8217;ve created some basic PHP pages and added some simple authentication. Today we&#8217;re going to going to build on that by adding database support. This will allow us to add proper authentication to our application and start &#8230; <a href="http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-%e2%80%93-part-3/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>So far we&#8217;ve created some basic PHP pages and added some simple authentication. Today we&#8217;re going to going to build on that by adding database support. This will allow us to add proper authentication to our application and start saving tasks. I should also note that I am currently writing PHP in-line and not using functions (or object orientated PHP) I will tidy this up in the next tutorial and spend more time explaining it and what it&#8217;s benefits are.
</p>
<h2>Last week</h2>
<p>Last <a href="http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-–-part-2/" target="_blank">week</a> we installed <a href="http://www.apachefriends.org/en/xampp.html">XAMPP</a>, so you should already have MySQL installed and ready to go. If not, please check back on the previous tutorial <a href="http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-–-part-2/" target="_blank">here</a>. If you decided to use IIS last week, you can download MySQL using the <a href="http://www.microsoft.com/web/downloads/platform.aspx" target="_blank">Microsoft Web Platform Installer</a>, you can either install phpMyAdmin or download the <a href="http://dev.mysql.com/downloads/tools/workbench/" target="_blank">MySQL Workbench tool</a>
</p>
<p>If you have any trouble installing or configuring any of the above, please leave a comment below and I will try and help you get sorted.
</p>
<h2>Creating our schema</h2>
<p>Assuming you&#8217;ve got MySQL and phpMyAdmin installed, we can start creating our schema. So far we know that we will need at least two tables. Our user table for logging in and our tasks table. We also know that a user owns a specific task, so that tells us we&#8217;ll also need to use a <a href="http://en.wikipedia.org/wiki/Foreign_key" target="_blank">Foreign Key</a> to define the relationship.
</p>
<p>First we will need a database to create our tables. A basic script to get us going would look something like this. It creates the database &#8216;todo&#8217; and creates a &#8216;users&#8217; and a &#8216;tasks&#8217; table. It also defines the foreign key in the &#8216;tasks&#8217; table for the &#8216;user_id&#8217; column.</p>
<pre class="brush: js">
CREATE DATABASE todo;

USE todo;

CREATE TABLE IF NOT EXISTS `users` (
  `user_id` bigint(20) unsigned NOT NULL auto_increment,
  `user_login` varchar(100) NOT NULL,
  `user_password` varchar(64) NOT NULL,
  `user_firstname` varchar(50) NOT NULL,
  `user_surname` varchar(50) NOT NULL,
  `user_email` varchar(100) NOT NULL,
  `user_registered` datetime NOT NULL default '0000-00-00 00:00:00',  
  PRIMARY KEY (`user_id`),
  KEY `idx_user_login_key` (`user_login`)
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

CREATE TABLE IF NOT EXISTS `tasks` (
  `task_id` bigint(20) unsigned NOT NULL auto_increment,
  `user_id` bigint(20) NOT NULL REFERENCES `users`(`user_id`),
  `task_name` varchar(60) NOT NULL,
  `task_priority` tinyint(2) NOT NULL default '2',
  `task_color` varchar(7) NOT NULL default '#ffffff',
  `task_description` varchar(150) NULL,
  `task_attendees` varchar(4000) NULL,
  `task_date` datetime NOT NULL default '0000-00-00 00:00:00',  
  PRIMARY KEY (`task_id`),
  KEY `idx_task_name_key` (`task_name`)
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
</pre>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/phpmyadmin.png" target="_blank"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/phpmyadmin.png" alt="" width="993" height="415" class="aligncenter size-full wp-image-4075" /></a></p>
<p>To create our first user we need to insert a row into the &#8216;users&#8217; table. The sql below will define a user with the login &#8216;developerdrive&#8217;. If you have a look at the SQL you can also see it is calling the <a href="http://dev.mysql.com/doc/refman/5.1/en/password-hashing.html" target="_blank">PASSWORD</a> function. This is a built in function for MySQL that uses the the <a href="http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html#function_sha1" target="_blank">SHA1</a> function to hash a string. It&#8217;s recommended that you also <a href="http://en.wikipedia.org/wiki/Salt_(cryptography)" target="_blank">salt</a> a password before hashing it. Salting is the appending (or prepending) of a piece of randomly generated data to the original password (like &#8216;password&#8217; + &#8216;dhskhfkjhsdkfhskad&#8217;), this increases the randomization of the password and making it more difficult to break. If you are salting the password, you will also need to store the salt separately to the password.
</p>
<p>Feel free to update the details below to your own, don&#8217;t forget your password though!</p>
<pre class="brush: js">
INSERT INTO `users` ( `user_login`, `user_password`, `user_firstname`, 
	`user_surname`, `user_email`, `user_registered` )
SELECT 'developerdrive', PASSWORD('to-do-password'), 'developer',
	'drive', 'developer@email.com', NOW();
</pre>
<p>The entire script (schema.sql) including any SQL examples is included in the tutorial download at the end</p>
<h2>Authentication</h2>
<p>Now that we have our users table and our own login, we need to update our login.php to connect to the database and check the provided login against the &#8216;users&#8217; table. To do that we are going to use the <a href="http://php.net/manual/en/book.mysqli.php" target="_blank">MySQLi</a> library. To create a database connection using MySQLi, you need to provide the library the MySQL database details. The default XAMPP mysql credentials are a username of &#8220;root&#8221; and a blank password. Since we&#8217;re using the local database, you specify &#8220;localhost&#8221; as the server and &#8220;todo&#8221; as the name of the database.
</p>
<pre class="brush: js">
$connection = new mysqli("localhost", "root", "", "todo");

if (mysqli_connect_errno()) {
    die(sprintf("Connect failed: %s\n", mysqli_connect_error()));
}
</pre>
<p>We want to be able to use this connection all over our application, so we&#8217;re going to create a new file called database.php and put the following PHP in it.</p>
<pre class="brush: js">&lt;?php
global $connection;

if ( isset( $connection ) )
    return;

$connection = new mysqli("localhost", "root", "", "todo");

if (mysqli_connect_errno()) {        
    die(sprintf("Connect failed: %s\n", mysqli_connect_error()));
}
?&gt;
</pre>
<p>We can now update out login.php file to require &#8216;database.php&#8217; and we can also add our first SQL query. We&#8217;re going to use parameterized SQL and pass in our $username and $password variables from before. Parameterized SQL helps prevent attackers overriding your SQL using what is called a <a href="http://en.wikipedia.org/wiki/SQL_injection" target="_blank">SQL Injection</a> attack. A SQL injection attack can be quiet serious and can result in the complete loss of your data.
</p>
<p>The SQL statement queries the &#8216;users&#8217; table for a &#8216;user_id&#8217; that has a &#8216;user_login&#8217; and &#8216;user_password&#8217; that matches the provided values. We wrap the password parameter in the PASSWORD function to make sure we only compare the hash values. If the query returns a user_id we know that the correct username and password have been provided.</p>
<pre class="brush: js">
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    
    require_once('database.php');
    
    if(!empty($_POST["username"]) &amp;&amp; !empty($_POST["password"])) {
        $username = $_POST["username"];
        $password = $_POST["password"];
    
        $query = $connection-&gt;prepare("SELECT `user_id` FROM `users` WHERE `user_login` = ? and `user_password` = PASSWORD(?)");
        $query-&gt;bind_param("ss", $username, $password);
        $query-&gt;execute();
        $query-&gt;bind_result($userid);
        $query-&gt;fetch();
        $query-&gt;close();
        
        if(!empty($userid)) {
            session_start();
            $_SESSION["authenticated"] = 'true';
            header('Location: index.php');
        }
        else {
            header('Location: login.php');
        }
        
    } else {
        header('Location: login.php');
    }
}
</pre>
<p>The function that actually sets the parameters in the query is the <a href="http://php.net/manual/en/mysqli-stmt.bind-param.php" target="_blank">bind_param</a> function. The first parameter (&#8220;ss&#8221; above) defines the number of parameters and their data types:</p>
<ul>
<li>i for integer numbers e.g. 9999</li>
<li>d for double or decimal numbers e.g. 10.99</li>
<li>s for string e.g. &#8220;testing&#8221;</li>
<li>b for binary or blob values</li>
</ul>
<p>The <a href="http://www.php.net/manual/en/mysqli-stmt.bind-result.php" target="_blank">bind_result</a> function binds a variable to the result column in the same ordinal position. In the above example I am selecting only the &#8216;user_id&#8217; column, if I was selecting col1, col2, col3 I could use bind_result($val1, $val2, $val3) to get their values.
</p>
<p>While this has improved security, we still suffer from the fact that the authenticated flag is stored in session. It would be a much better idea to store the session in the database along with the expiry date (in one hour) and the IP address the user logged in from. To do this, all we have to do is create a new &#8216;sessions&#8217; table and insert a row that can be checked by the authenticate.php file</p>
<pre class="brush: js">
CREATE TABLE IF NOT EXISTS `sessions` (
  `session_id` bigint(20) unsigned NOT NULL auto_increment,
  `user_id` bigint(20) NOT NULL REFERENCES `users`(`user_id`),
  `session_key` varchar(60) NOT NULL,
  `session_address` varchar(100) NOT NULL,
  `session_useragent` varchar(200) NOT NULL,
  `session_expires` datetime NOT NULL default '0000-00-00 00:00:00',  
  PRIMARY KEY (`session_id`),
  KEY `idx_session_key` (`session_key`)
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
</pre>
<p>Here&#8217;s the full login.php content</p>
<pre class="brush: js">
&lt;?php
$username = null;
$password = null;

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    
    require_once('database.php');
    
    if(!empty($_POST["username"]) &amp;&amp; !empty($_POST["password"])) {
        $username = $_POST["username"];
        $password = $_POST["password"];
    
        $query = $connection-&gt;prepare("SELECT `user_id` FROM `users` WHERE `user_login` = ? and `user_password` = PASSWORD(?)");
        $query-&gt;bind_param("ss", $username, $password);
        $query-&gt;execute();
        $query-&gt;bind_result($userid);
        $query-&gt;fetch();
        $query-&gt;close();
        
        if(!empty($userid)) {
            session_start();
            $session_key = session_id();
            
            $query = $connection-&gt;prepare("INSERT INTO `sessions` ( `user_id`, `session_key`, `session_address`, `session_useragent`, `session_expires`) VALUES ( ?, ?, ?, ?, DATE_ADD(NOW(),INTERVAL 1 HOUR) );");
            $query-&gt;bind_param("isss", $userid, $session_key, $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'] );
            $query-&gt;execute();
            $query-&gt;close();
            
            header('Location: index.php');
        }
        else {
            header('Location: login.php');
        }
        
    } else {
        header('Location: login.php');
    }
} else {
?&gt;
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;
    &lt;title&gt;Creating a simple to-do application - Part 1&lt;/title&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="page"&gt;
    &lt;!-- [banner] --&gt;
    &lt;header id="banner"&gt;
        &lt;hgroup&gt;
            &lt;h1&gt;Login&lt;/h1&gt;
        &lt;/hgroup&gt;        
    &lt;/header&gt;
    &lt;!-- [content] --&gt;
    &lt;section id="content"&gt;
        &lt;form id="login" method="post"&gt;
            &lt;label for="username"&gt;Username:&lt;/label&gt;
            &lt;input id="username" name="username" type="text" required&gt;
            &lt;label for="password"&gt;Password:&lt;/label&gt;
            &lt;input id="password" name="password" type="password" required&gt;                    
            &lt;br /&gt;
            &lt;input type="submit" value="Login"&gt;
        &lt;/form&gt;
    &lt;/section&gt;
    &lt;!-- [/content] --&gt;
    
    &lt;footer id="footer"&gt;
        &lt;details&gt;
            &lt;summary&gt;Copyright 2013&lt;/summary&gt;
            &lt;p&gt;Jonathan Schnittger. All Rights Reserved.&lt;/p&gt;
        &lt;/details&gt;
    &lt;/footer&gt;
&lt;/div&gt;
&lt;!-- [/page] --&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;?php } ?&gt;
</pre>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/sessions.png" target="_blank"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/sessions.png" alt="" width="1221" height="541" class="aligncenter size-full wp-image-4076" /></a></p>
<p>Now we can update authenticate.php to check for the correct session. We do this by querying for the &#8216;session_key&#8217;, &#8216;session_address&#8217; and the &#8216;session_agent&#8217;. This way any changes to the session will be detected and the user will be asked to re-authenticate. We also confirm that the session has not expired.
</p>
<pre class="brush: js">
&lt;?php
session_start();
$session_key = session_id();

require_once('database.php');

$query = $connection-&gt;prepare("SELECT `session_id`, `user_id` FROM `sessions` WHERE `session_key` = ? AND `session_address` = ? AND `session_useragent` = ? AND `session_expires` &gt; NOW();");
$query-&gt;bind_param("sss", $session_key, $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']);
$query-&gt;execute();
$query-&gt;bind_result($session_id, $user_id);
$query-&gt;fetch();
$query-&gt;close();

if(empty($session_id)) {
    header('Location: login.php');
}

?&gt;
</pre>
<p>It&#8217;s probably a good idea to update the session expiry date every time the user does something, this way we&#8217;re not forcing them to login every hour. If you add the following php to the end of authenticate.php the session expiry will be extended for another hour.</p>
<pre class="brush: js">
$query = $connection-&gt;prepare("UPDATE `sessions` SET `session_expires` = DATE_ADD(NOW(),INTERVAL 1 HOUR) WHERE `session_id` = ?;");
$query-&gt;bind_param("i", $session_id );
$query-&gt;execute();
$query-&gt;close();
</pre>
<p>It&#8217;s also important to note that certain browsers share the session_id across tabs. This means that because we&#8217;re storing the session in the database, if the user opens up a new tab they will automatically be logged in. If this is not the behavior you want, you can use the <a href="http://php.net/manual/en/function.session-regenerate-id.php" target="_blank">session_regenerate_id</a> function to generate a new session_id</p>
<h2>Listing and saving tasks</h2>
<p>Now that we have authentication out of the way, we can save our first task and view a list of existing tasks. First up we&#8217;ll update index.php to be able to list any existing tutorials. We need to ensure that only the tasks for the logged in user are shown. To do this we&#8217;ll use the $user_id variable we set in authentication.php. The following code is from between the table tbody tags in index.php
</p>
<pre class="brush: js">
&lt;?php
global $user_id;
$query = $connection-&gt;prepare("SELECT `task_id`, `task_name`, `task_priority`, `task_color`, `task_description`, `task_attendees`, `task_date` FROM `tasks` WHERE `user_id` = ?");
$query-&gt;bind_param("i", $user_id);
$query-&gt;execute();

$query-&gt;bind_result($id, $name, $priority, $color, $description, $attendees, $date);
while ($query-&gt;fetch()) {
    echo '&lt;tr id="task-' . $id . '"&gt;&lt;th scope="row" style="background-color:' . $color . '"&gt;&lt;input type="checkbox" /&gt;&lt;/th&gt;&lt;td&gt;' . $date . '&lt;/td&gt;&lt;td&gt;' . $priority . '&lt;/td&gt;&lt;td&gt;' . $name . '&lt;/td&gt;&lt;td&gt;' . $description . '&lt;/td&gt;&lt;td&gt;' . $attendees . '&lt;/td&gt;&lt;/tr&gt;';
}

$query-&gt;close();
?&gt;    
</pre>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/view-tasks.png" target="_blank"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/view-tasks.png" alt="" width="1669" height="449" class="aligncenter size-full wp-image-4077" /></a></p>
<p>Next up is actually saving our first task. To do this we need to update submit.php. Here we&#8217;re going to add an insert statement once we&#8217;ve validated the data submitted.
</p>
<pre class="brush: js">
&lt;?php
require_once('authenticate.php');

$name = null;
$date = date('c');
$desc = '';
$email = '';
$priority = 2;
$color = '#ffffff';

$result = array();
$result['error'] = array();

if (!empty($_POST["new-task-name"]))
    $name = $_POST["new-task-name"];
else 
    array_push($result['error'], 'Please specify a name for your task');

if (!empty($_POST["new-task-date"]))
    $date = new DateTime($_POST["new-task-date"]);
else 
    array_push($result['error'], 'Please specify a date for your task');

if (!empty($_POST["new-task-desc"]))
    $desc = $_POST["new-task-desc"];

if (!empty($_POST["new-task-email"]))
    $email = explode(',', $_POST["new-task-email"]);

if (!empty($_POST["new-task-priority"]))
    $priority = intval($_POST["new-task-priority"]);
else 
    array_push($result['error'], 'Please specify a valid priority for your task');

if (!empty($_POST["new-task-color"]))
    $color = $_POST["new-task-color"];

if(isset($result['error']) &amp;&amp; count($result['error']) &gt; 0){
    $result['success'] = false;
} else {
    if(!empty($email)){
        $email = implode(',', $email);
    }
    $date = $date-&gt;format('c');
    
    $query = $connection-&gt;prepare("INSERT INTO `tasks` ( `user_id`, `task_name`, `task_priority`, `task_color`, `task_description`, `task_attendees`, `task_date` ) VALUES ( ?, ?, ?, ?, ?, ?, ? );");
    $query-&gt;bind_param("isissss", $user_id, $name, $priority, $color, $desc, $email, $date );
    $query-&gt;execute();
    $result['id'] = $query-&gt;insert_id;
    $query-&gt;close();

    $result['success'] = true;
    $result['name'] = $name;
    $result['date'] = $date;
    $result['desc'] = $desc;
    $result['email'] = $email;
    $result['priority'] = $priority;
    $result['color'] = $color;
}

echo json_encode($result);
?&gt;
</pre>
<p>Here you can see that I&#8217;m returning the $query-&gt;insert_id in the $result array. This is the &#8216;task_id&#8217; created by when we inserted the new task</p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/tasks.png" target="_blank"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/tasks.png" alt="" width="895" height="368" class="aligncenter size-full wp-image-4078" /></a></p>
<p>And there you have it, we&#8217;ve got some of the basic functionality done. We can now log in and create or view existing tasks. Next up I&#8217;ll be refactoring the code to use functions and adding better error handling. We&#8217;ll also be looking at adding a <a href="http://en.wikipedia.org/wiki/Cron" target="_blank">cron</a> job to tidy up expired sessions and perhaps we can add in some statistics to see what features or pages our users are looking at</p>
<p>
<a href='http://schnittger.me/tutorials/to-do/part5/' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/part5.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-%e2%80%93-part-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adding a simple authentication using PHP require and includes</title>
		<link>http://www.developerdrive.com/2013/05/adding-a-simple-authentication-using-php-require-and-includes/</link>
		<comments>http://www.developerdrive.com/2013/05/adding-a-simple-authentication-using-php-require-and-includes/#comments</comments>
		<pubDate>Fri, 03 May 2013 22:58:20 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Snippet]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4056</guid>
		<description><![CDATA[Introduction Please note that this tutorial has now been superseded by a later, more in-depth tutorial available here Continuing on with our to-do application, in this weeks snippet we&#8217;re going to be using PHP&#8217;s require_once function. The require_once function is &#8230; <a href="http://www.developerdrive.com/2013/05/adding-a-simple-authentication-using-php-require-and-includes/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>Please note that this tutorial has now been superseded by a later, more in-depth tutorial available <a href="http://www.developerdrive.com/2013/05/creating-a-simple-to-do-application-–-part-3">here</a></p>
<p>
Continuing on with our to-do application, in this weeks snippet we&#8217;re going to be using PHP&#8217;s <a href="http://php.net/manual/en/function.require-once.php" target="_blank">require_once</a> function. The require_once function is similar to the <a href="http://www.php.net/manual/en/function.require.php" target="_blank">require</a> function, in that it will execute and include any php code with the calling file but with one vital difference. It will only execute once. This is important for us in this tutorial as we are going to use it to validate our user.</p>
<p><p>There are a few things to note first, since this is a short tutorial I will be doing something that is not a good idea in a production application. I will be storing the logged in state in the <a href="http://php.net/manual/en/reserved.variables.session.php" target="_blank">$_SESSION</a> variable, this is only temporary as next week we will be updating it to store it in a MySQL database. We will also be storing in the database additional information such as the source IP address and a timestamp to mitigate against <a href="http://en.wikipedia.org/wiki/Session_hijacking" target="_blank">session hi-jacking</a>.
</p>
<h2>Getting started</h2>
<p>
First we will need to convert our existing index.html file to a php file. To do this, simply change the extension to &#8216;.php&#8217; and open the file in your text editor. We will now also create a new php file called &#8216;authenticate.php&#8217; and save it in the same folder as our new index.php.
</p>
<p>Now inside our index file, we are going to add the following php code to the very top of the file.
</p>
<pre class="brush: js">
&lt;?php
require_once('authenticate.php');
?&gt;
</pre>
<p>This means that as soon as the file is executed, it will read and execute our authenticate.php file. The contents of the authenticate file is also pretty straight forward for now. We&#8217;re going to ensure our session has been started (<a href="http://www.php.net/manual/en/function.session-start.php" target="_blank">session_start</a>), and then check to see if the $_SESSION variable contains our authenticated flag. If it doesn&#8217;t exist or is not set to &#8216;true&#8217; we will re-direct the user to our 3rd new page login.php. To perform the re-direct we are going to update the header Location value to point to our login.php file.
</p>
<p>
 By also adding the above code to our submit.php, we can also ensure that only authenticated users can add tasks.
</p>
<pre class="brush: js">
&lt;?php
session_start();
if(empty($_SESSION["authenticated"]) || $_SESSION["authenticated"] != 'true') {
    header('Location: login.php');
}
?&gt;
</pre>
<p>
In the scenario where we have already logged in, the index.php file will continue to execute as normal and we shall see our form and table as usual.
</p>
<h2>Logging in</h2>
<p>Next up, we are going to create our login page. For now, it will be just a simple username and password form that uses POST to send the provided details to itself.
</p>
<pre class="brush: js">
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;
    &lt;title&gt;Creating a simple to-do application - Part 1&lt;/title&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="page"&gt;
    &lt;!-- [banner] --&gt;
    &lt;header id="banner"&gt;
        &lt;hgroup&gt;
            &lt;h1&gt;Login&lt;/h1&gt;
        &lt;/hgroup&gt;        
    &lt;/header&gt;
    &lt;!-- [content] --&gt;
    &lt;section id="content"&gt;
        &lt;form id="login" method="post"&gt;
            &lt;label for="username"&gt;Username:&lt;/label&gt;
            &lt;input id="username" name="username" type="text" required&gt;
            &lt;label for="password"&gt;Password:&lt;/label&gt;
            &lt;input id="password" name="password" type="password" required&gt;                    
            &lt;br /&gt;
            &lt;input type="submit" value="Login"&gt;
        &lt;/form&gt;
    &lt;/section&gt;
    &lt;!-- [/content] --&gt;
    
    &lt;footer id="footer"&gt;
        &lt;details&gt;
            &lt;summary&gt;Copyright 2013&lt;/summary&gt;
            &lt;p&gt;Jonathan Schnittger. All Rights Reserved.&lt;/p&gt;
        &lt;/details&gt;
    &lt;/footer&gt;
&lt;/div&gt;
&lt;!-- [/page] --&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>
At the top of this file we&#8217;re going to add our POST logic.
</p>
<pre class="brush: js">
&lt;?php
$username = null;
$password = null;

if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    if(!empty($_POST["username"]) &amp;&amp; !empty($_POST["password"])) {
        $username = $_POST["username"];
        $password = $_POST["password"];
        
        if($username == 'user' &amp;&amp; $password == 'password') {
            session_start();
            $_SESSION["authenticated"] = 'true';
            header('Location: index.php');
        }
        else {
            header('Location: login.php');
        }
        
    } else {
        header('Location: login.php');
    }
} else {
?&gt;
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;
    &lt;title&gt;Creating a simple to-do application - Part 1&lt;/title&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="page"&gt;
    &lt;!-- [banner] --&gt;
    &lt;header id="banner"&gt;
        &lt;hgroup&gt;
            &lt;h1&gt;Login&lt;/h1&gt;
        &lt;/hgroup&gt;        
    &lt;/header&gt;
    &lt;!-- [content] --&gt;
    &lt;section id="content"&gt;
        &lt;form id="login" method="post"&gt;
            &lt;label for="username"&gt;Username:&lt;/label&gt;
            &lt;input id="username" name="username" type="text" required&gt;
            &lt;label for="password"&gt;Password:&lt;/label&gt;
            &lt;input id="password" name="password" type="password" required&gt;                    
            &lt;br /&gt;
            &lt;input type="submit" value="Login"&gt;
        &lt;/form&gt;
    &lt;/section&gt;
    &lt;!-- [/content] --&gt;
    
    &lt;footer id="footer"&gt;
        &lt;details&gt;
            &lt;summary&gt;Copyright 2013&lt;/summary&gt;
            &lt;p&gt;Jonathan Schnittger. All Rights Reserved.&lt;/p&gt;
        &lt;/details&gt;
    &lt;/footer&gt;
&lt;/div&gt;
&lt;!-- [/page] --&gt;
&lt;/body&gt;
&lt;/html&gt;
&lt;?php } ?&gt;

</pre>
<p>Here you can see that if the POST variable contains the keys username and password it will attempt to confirm that their values are equal to user and password. If they are not, it will redirect the user back to itself. If the values are correct, we ensure our session is started and set our &#8216;authenticated&#8217; session value to true and then re-direct to our index.php file. I&#8217;ve also wrapped the HTML tags in a conditional statement, so that if the page is requested using a GET it will show the log in form as normal.
</p>
<p>
Since we have now setup our session and the authenticated flag has been set, the index page will render as normal.
</p>
<h2>A final note</h2>
<p>As I have previously mentioned, this is not secure and should not be used in a production environment. It does however introduce us to session variables and allows us a stepping stone on to improving security and MySql database connectivity next week. In a later tutorial we will also be revisiting the require_once function to give us basic templating abilities for common HTML elements such as the header and footer of our application.
</p>
<p>
<a href='http://schnittger.me/tutorials/to-do/part4/' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/05/authenticate.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/05/adding-a-simple-authentication-using-php-require-and-includes/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Creating a simple to-do application – Part 2</title>
		<link>http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-%e2%80%93-part-2/</link>
		<comments>http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-%e2%80%93-part-2/#comments</comments>
		<pubDate>Tue, 30 Apr 2013 00:09:41 +0000</pubDate>
		<dc:creator>Jonathan Schnittger</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.developerdrive.com/?p=4033</guid>
		<description><![CDATA[Introduction Continuing on from last weeks tutorial (Creating a simple to-do application – Part 1) and the short snippet (Turning a form element into JSON and submiting it via jQuery ), this week we&#8217;ll be writing a PHP page to &#8230; <a href="http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-%e2%80%93-part-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<h2>Introduction</h2>
<p>
Continuing on from last weeks tutorial (<a href="http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-part-1/">Creating a simple to-do application – Part 1</a>) and the short snippet (<a href="http://www.developerdrive.com/2013/04/turning-a-form-element-into-json-and-submiting-it-via-jquery/">Turning a form element into JSON and submiting it via jQuery</a> ), this week we&#8217;ll be writing a <a href="http://php.net/">PHP</a> page to accept and process the form submit and return a JSON response.
</p>
<h2>Installing PHP on your web server</h2>
<p>
There are several ways to install PHP to your web server, but each is dependent on what web server you are running. The two main ones I will deal with are Microsoft&#8217;s <a href="http://www.iis.net/">Internet Information Services (IIS)</a> and <a href="http://www.apache.org/">Apache</a>. IIS will only run on Windows, but Apache will run on either Windows, OSX or your favorite Linux Distro (e.g Ubuntu). For each operating system there are several easy to use packages that will install and configure both the web server and php.
</p>
<h3>Apache</h3>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/feather-small.gif"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/feather-small.gif" alt="" width="203" height="61" class="aligncenter size-full wp-image-4036" /></a></p>
<p>
The easiest package for Apache I find is <a href="http://www.apachefriends.org/en/xampp.html" target="_blank">XAMPP</a>, it provides installers for all of the major platforms and is very easy to use. You can download it directly from the site based on your operating system and follow their install guides.
</p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/xampp.gif"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/xampp.gif" alt="" width="200" height="59" class="aligncenter size-full wp-image-4038" /></a></p>
<ul>
<li><a href="http://www.apachefriends.org/en/xampp-linux.html" target="_blank">Linux</a></li>
<li><a href="http://www.apachefriends.org/en/xampp-windows.html" target="_blank">Windows</a></li>
<li><a href="http://www.apachefriends.org/en/xampp-macosx.html" target="_blank">OSX</a></li>
</ul>
<p>
The XAMPP site includes details on where to put your files and how to create alias folders. These topics and more are covered in the install guides above.
</p>
<h3>IIS</h3>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/iis.jpeg"><img src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/iis.jpeg" alt="" width="72" height="72" class="aligncenter size-full wp-image-4037" /></a></p>
<p>IIS actually comes free with Windows, it generally isn&#8217;t installed by default but you can add it in the &#8220;Add/Remove Programs&#8221; section of the Control Panel under &#8220;Turn On/Off Windows Features&#8221; (For a detailed step by step guide, click <a href="http://www.iis.net/learn/install/installing-iis-7/installing-iis-on-windows-vista-and-windows-7" target="_blank">here</a>). The easiest way to install PHP is to download the Microsoft <a href="http://www.microsoft.com/web/downloads/platform.aspx" target="_blank">Web Platform Installer</a>. It will offer you a range of programs to install, everything from PHP and WordPress to various e-commerce platforms. All you do is pick the applications you want, and they will automatically be installed!</p>
<p>By default web files go in the following folder C:\inetpub\wwwroot, you can however create &#8220;Virtual Folders&#8221; that point anywhere you want. To continue this tutorial, please save the index.html page in to wwwroot or <a href="http://msdn.microsoft.com/en-ie/library/zwk103ab(v=vs.100).aspx">create a virtual folder</a>
</p>
<h2>Getting started</h2>
<p>Assuming you&#8217;ve gotten your web server running and PHP configured, let&#8217;s get started! (If you are having trouble, please feel free to ask in the comments section below and I will help as best I can).
</p>
<p>
First we&#8217;ll create our submit.php file (in the same folder as our index.html file) and then using the PHP global variable <a href="http://php.net/manual/en/reserved.variables.post.php">$_POST</a> we will retrieve the form values we have submitted using jQuery.<br />
PHP code is always wrapped in &#8220;&lt;?php ?&gt;&#8221; tags, anything outside of these tags is rendered as normal (like HTML).
</p>
<pre class="brush: js">
&lt;?php
$name = $_POST["new-task-name"];
$date = $_POST["new-task-date"];
$desc = $_POST["new-task-desc"];
$email = $_POST["new-task-email"];
$priority = $_POST["new-task-priority"];
$color = $_POST["new-task-color"];
?&gt;
</pre>
<p>
We are using the $_POST variable instead of the <a href="http://php.net/manual/en/reserved.variables.get.php" target="_blank">$_GET</a> because we specified our jQuery submit method as &#8216;POST&#8217;, this can be easily changed if you prefer to use &#8216;GET&#8217;
</p>
<p>Now that we have our form values, we&#8217;ll have to provide some basic validation on them. We know that we need a name, the date and priority values, but we also know that we can specify optional values and in the case of the email field a comma separated list. First we&#8217;ll make sure our required values, do in fact have values and that they are formatted correctly.
</p>
<p>Firstly, we&#8217;re going to specify some default values and then update the variables after we have confirmed that each of the form values has been provided. We can do this using the <a href="http://php.net/manual/en/function.empty.php" target="_blank">empty</a> function.
</p>
<pre class="brush: js">
&lt;?php
$name = null;
$date = date('c');
$desc = null;
$email = null;
$priority = 2;
$color = '#ffffff';

if (!empty($_POST["new-task-name"]))
    $name = $_POST["new-task-name"];
if (!empty($_POST["new-task-date"]))
    $date = $_POST["new-task-date"];
if (!empty($_POST["new-task-desc"]))
    $desc = $_POST["new-task-desc"];
if (!empty($_POST["new-task-email"]))
    $email = $_POST["new-task-email"];
if (!empty($_POST["new-task-priority"]))
    $priority = $_POST["new-task-priority"];
if (!empty($_POST["new-task-color"]))
    $color = $_POST["new-task-color"];
?&gt;
</pre>
<p>
We&#8217;re also going to confirm that the date value is in the correct format and that the priority is an integer value. We do this using the <a href="http://www.php.net/manual/en/class.datetime.php" target="_blank">DateTime</a> and <a href="http://php.net/manual/en/function.intval.php" target="_blank">intval</a> functions. Using the <a href="http://php.net/manual/en/function.explode.php" target="_blank">explode</a> and <a href="http://php.net/manual/en/function.implode.php" target="_blank">implode</a> functions we can also convert the comma separated email list into an array and back to a string.
</p>
<pre class="brush: js">
&lt;?php
$name = null;
$date = date('c');
$desc = null;
$email = null;
$priority = 2;
$color = '#ffffff';

if (!empty($_POST["new-task-name"]))
    $name = $_POST["new-task-name"];
if (!empty($_POST["new-task-date"]))
    $date = new DateTime($_POST["new-task-date"]);
if (!empty($_POST["new-task-desc"]))
    $desc = $_POST["new-task-desc"];
if (!empty($_POST["new-task-email"]))
    $email = explode(',', $_POST["new-task-email"]);
if (!empty($_POST["new-task-priority"]))
    $priority = intval($_POST["new-task-priority"]);
if (!empty($_POST["new-task-color"]))
    $color = $_POST["new-task-color"];
?&gt;
</pre>
<p>We must also provide feedback to the user if the required values are not set. To do this we will return a JSON object with a success value of false, we&#8217;ll also return a list of messages informing the user of what they need to do. Using the <a href="http://php.net/manual/en/function.json-encode.php" target="_blank">json_encode</a> function we can convert our $result array to JSON very easily
</p>
<pre class="brush: js">
&lt;?php
$name = null;
$date = date('c');
$desc = null;
$email = null;
$priority = 2;
$color = '#ffffff';

$result = array();
$result['error'] = array();

if (!empty($_POST["new-task-name"]))
    $name = $_POST["new-task-name"];
else 
    array_push($result['error'], 'Please specify a name for your task');

if (!empty($_POST["new-task-date"]))
    $date = new DateTime($_POST["new-task-date"]);
else 
    array_push($result['error'], 'Please specify a date for your task');

if (!empty($_POST["new-task-desc"]))
    $desc = $_POST["new-task-desc"];

if (!empty($_POST["new-task-email"]))
    $email = explode(',', $_POST["new-task-email"]);

if (!empty($_POST["new-task-priority"]))
    $priority = intval($_POST["new-task-priority"]);
else 
    array_push($result['error'], 'Please specify a valid priority for your task');

if (!empty($_POST["new-task-color"]))
    $color = $_POST["new-task-color"];

if(isset($result['error']) &amp;&amp; count($result['error']) &gt; 0){
    $result['success'] = false;
} else {
    $result['success'] = true;
    $result['name'] = $name;
    $result['date'] = $date-&gt;format('c');
    $result['desc'] = $desc;
    $result['email'] = implode(',', $email);
    $result['priority'] = $priority;
    $result['color'] = $color;
}

echo json_encode($result);
?&gt;
</pre>
<p>We now have to update our jQuery submit method to handle the response and either show an error message or add the task row to the table.
</p>
<pre class="brush: js">
$.ajax({
    type: "POST",
    url: "submit.php",
    data: json,
    dataType: "json"
}).success(function(state) { 
    if(state.success === true) {
        tbody.append('&lt;tr&gt;&lt;th scope="row" style="background-color:' + state['color'] + 
            '"&gt;&lt;input type="checkbox" /&gt;&lt;/th&gt;&lt;td&gt;' + state['date'] +
            '&lt;/td&gt;&lt;td&gt;' + state['priority'] + '&lt;/td&gt;&lt;td&gt;' + state['name'] + 
            '&lt;/td&gt;&lt;td&gt;' + state['desc'] + '&lt;/td&gt;&lt;td&gt;' + state['email'] + '&lt;/td&gt;&lt;/tr&gt;');    
    } else {
        alert(state.error.join());
    }
}).fail(function(state) { 
    alert("Failed to add to-do"); 
});
</pre>
<p>As you can see, if the success value of the state returned by out PHP file is true we add the row from the result. If it is false we append all of the error messages from the error array in to one string using the <a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join" target="_blank">join</a> function and display it using the alert function.
</p>
<p>We have now validated and cleansed our form values, we have also started to add some more complex logic to our application (in the form of error messages).
</p>
<h2>Next week</h2>
<p>Next week we&#8217;ll cover setting up a MySql database and creating some basic tables. We&#8217;ll also update our submit.php file to save the task to the database and add some more functionality to our application by converting out static HTML page to PHP and adding in some basic authentication. Security and file/database permissions deserve their own tutorial, so I will be covering them in depth very soon.</p>
<p>
<a href='http://schnittger.me/tutorials/to-do/part3/' target="_blank"><img class="aligncenter size-full wp-image-3371" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/02/view-demo-button.jpg" alt="" width="232" height="60" /></a></p>
<p><a href="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/04/submit.zip"><img class="aligncenter size-full wp-image-3629" src="http://developerdrive.developerdrive.netdna-cdn.com/wp-content/uploads/2013/03/download.jpg" alt="" width="300" height="53" /></a></p>
<div class="post-author-bio">
<div class="asection-title">ABOUT THE AUTHOR</div>
<div class="clr">
<div class="author-bio-text">A battle hardened software developer with a mixed and colorful background, who can&#8217;t come up with a decent author bio <a href="http://schnittger.me">http://schnittger.me</a></div>
<div class="clr"></div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.developerdrive.com/2013/04/creating-a-simple-to-do-application-%e2%80%93-part-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss><!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced
Content Delivery Network via developerdrive.developerdrive.netdna-cdn.com

 Served from: www.developerdrive.com @ 2013-05-21 11:47:42 by W3 Total Cache -->
