<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
> <channel><title>WP Theming &#187; Devin</title> <atom:link href="http://wptheming.com/author/devin/feed/" rel="self" type="application/rss+xml" /><link>http://wptheming.com</link> <description>Tutorials, Themes and Plugins</description> <lastBuildDate>Tue, 31 Jan 2012 20:52:10 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Options Framework 1.0</title><link>http://wptheming.com/2012/01/options-framework-1-0/</link> <comments>http://wptheming.com/2012/01/options-framework-1-0/#comments</comments> <pubDate>Mon, 16 Jan 2012 03:06:33 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Plug-ins]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2216</guid> <description><![CDATA[Version 1.0 of the Options Framework is on its way.  Here's a rundown of the new features.  If you're a developer, please test the beta version before a wider release is done. <a
href="http://wptheming.com/2012/01/options-framework-1-0/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Although this is the &#8220;milestone&#8221; 1.0 release for the Options Framework there aren&#8217;t any major changes for end users in this version.  It&#8217;s more of a developer release, providing additional filters and hooks.</p><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/optionsheader.jpg" alt="Options" title="optionsheader" width="590" height="210" class="alignnone size-full wp-image-2227" /><br
/> <small
style="font-size:12px">Image from <a
href="http://www.flickr.com/photos/verbaljam/36824715/">Creative Commons</a></small></p><p>If your theme currently works, it should be fine after this release too.  But I would love for any developers who use it to test it out and make sure.</p><p>Below is a little rundown of the new features.  All these updates were instigated via pull requests on GitHub.  The full discussions can be found on <a
href="https://github.com/devinsays/options-framework-plugin/pull/78">Issue #78</a> by <a
href="https://twitter.com/#!/inxilpro">@inxilpro</a>, and <a
href="https://github.com/devinsays/options-framework-plugin/pull/75">Issue #75</a> by <a
href="https://twitter.com/#!/https://twitter.com/#!/mattwiebe">@mattwiebe</a>.  Many thanks to all the contributors and testers.</p><h3>Option ID Not Required</h3><p>In the past theme authors were required to have the &#8220;optionsframework_option_name&#8221; function in their theme to declare the option ID.  In this new version it&#8217;s not required.  If the option ID is not set, it will the simply use theme name with the the &#8220;optionsframework&#8221; as a prefix.</p><p>I&#8217;d recommend leaving it in if it&#8217;s already part of your theme, but on future projects it&#8217;s your choice.</p><h3>Location of Options.php Filterable</h3><p>Not everyone likes to store all their files in the base theme directory.  With this filter you can rename options.php to whatever you like, or move it to whichever directory you like.  Here&#8217;s an example:</p><pre class="brush: php; title: ; notranslate">
add_filter('options_framework_location','options_framework_location_override');
function options_framework_location_override() {
	return array('/extensions/options-renamed.php');
}
</pre><h3>Options Array Filterable</h3><p>The entire options array is also now filterable.  This might be useful if you want to make a minor alteration to the options of a parent theme from a child theme, or if you just want to avoid using optionsframework_options in your theme.</p><p>Here&#8217;s an example that appends an additional option using the filter:</p><pre class="brush: php; title: ; notranslate">
add_filter('of_options', function($options) {
	$options[] = array(
	'name' =&gt; 'New Option Added Via Filter',
	'id' =&gt; 'example_filtered_option',
	'std' =&gt; 'Default',
	'type' =&gt; 'text'
	);
  return $options;
});
</pre><h3>A Fix for User Roles</h3><p>The option panel has always displayed for users who had the edit_theme_options capability, but because of a quirk in the Settings API users actually need the manage_options capability in order to save them.  Now the options should save for anyone with the edit_theme_options capability.</p><h3>Info Option</h3><p>You can now put html into the info option by default- which should make it easier to drop in images or other customizations.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2012/01/options-framework-1-0/feed/</wfw:commentRss> <slash:comments>5</slash:comments> </item> <item><title>Theming the Image Post Format</title><link>http://wptheming.com/2012/01/theming-the-image-post-format/</link> <comments>http://wptheming.com/2012/01/theming-the-image-post-format/#comments</comments> <pubDate>Fri, 13 Jan 2012 22:35:03 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[WordPress Tips]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2190</guid> <description><![CDATA[Working with post formats in WordPress can be challenging because of the lack of structured data, but jQuery can actually help out quite a bit. <a
href="http://wptheming.com/2012/01/theming-the-image-post-format/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Working with post formats in WordPress can be challenging because of the lack of structured data.  For instance, just because a user selects an &#8220;image&#8221; post format, there&#8217;s no guarantee that an image was actually attached to the post.</p><p>Alex King has <a
href="http://alexking.org/blog/2011/10/25/wordpress-post-formats-admin-ui">created a plugin</a> and submitted a couple patches, but until the WordPress UI catches up we as theme developers need to be a little creative.</p><p>In the most recent version of <a
href="http://wordpress.org/extend/themes/portfolio-press">Portfolio Press</a>, I ended up styling the image format to look like this:</p><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/post-format-590x548.jpg" alt="" title="post-format" width="590" height="548" class="alignnone size-large wp-image-2191" /></p><p>(You can also <a
href="http://themes.wptheming.com/portfolio/blog/">view it</a> on the demo site)</p><h3>Post Format Styling</h3><p>Instead of putting a title above the post like the standard format, I decided to hide it.  In my opinion the focus should be on the image.</p><p>Instead of displaying the the_excerpt like I do for other posts in the archive view, I display the_content so that full images are able to be displayed.</p><p>Then, using jQuery, I built a rollover effect that displays the post title when someone hovers over the image.  If you click on the image, it will take you to the full post.</p><h3>jQuery With Post Formats</h3><p>Using jQuery makes it much easier to work with the post formats because you don&#8217;t need to guess what&#8217;s in the content.  It also fails gracefully if it doesn&#8217;t find the markup it&#8217;s looking for.</p><p>For instance, the rollover effect in Portfolio Press is applied only on the first image detected in the post format- even if the author dropped 12 in there.  If there&#8217;s no image, no additional markup is added.</p><p>The script checks to make sure the image is at least 200px wide.  The rollover title still might not fit in a larger image, but at least it has a chance.</p><p>Authors sometimes link their images to the fullsize version.  However, in the archive view I want the image to only link to the full post.  The script I wrote will unwrap any link around the original image, and replace it with a link to the post in the archive view.  This might not be what some users in intend, but I think the majority will.</p><h3>Enough Talk</h3><p>Here&#8217;s the script I used to create the rollover effect on post format images:</p><pre class="brush: jscript; title: ; notranslate">
    // Image Post Format
    $('#content .format-image').each( function() {
    	var image = $(this).find('img:first');
    	if (image.width() &gt; 200 ) {
	    	var link = $(this).find('.entry-title').children();
	    	var title = link.text();
	    	image.unwrap('a');
	    	image.wrap('&lt;div class=&quot;image-wrap&quot; /&gt;');
	    	image.wrap(link.text(''));
	    	image.parent().append('&lt;h3/&gt;');
	    	$(this).find('h3').text(title).width(image.width() - 20);
    	}
    });
    $('.format-image .image-wrap a').hover( function() {
    	$(this).children('h3').slideDown(100);
    }, function(){
    	$(this).children('h3').slideUp(200);
    });
</pre><h3>View It</h3><p>You can view the <a
href="http://themes.wptheming.com/portfolio/blog/">post formats here</a>.  Portfolio Press 0.9 can be <a
href="https://github.com/devinsays/portfolio-press">downloaded from GitHub</a> (the latest isn&#8217;t in the .org repository yet).</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2012/01/theming-the-image-post-format/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Custom Design on WordPress.com</title><link>http://wptheming.com/2012/01/custom-design-on-wordpress-com/</link> <comments>http://wptheming.com/2012/01/custom-design-on-wordpress-com/#comments</comments> <pubDate>Thu, 12 Jan 2012 04:08:28 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[WordPress Tips]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2154</guid> <description><![CDATA[If you want a highly customized theme for your WordPress site the only option is to self-host, right?  Not so fast... <a
href="http://wptheming.com/2012/01/custom-design-on-wordpress-com/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>If you want a highly customized theme for your WordPress site the only option is to self-host, right?  Not so fast&#8230;</p><p>I just finished a small project for Bluefin Software <a
href="http://blog.bluefinapps.com/">redeveloping their blog</a>.  They wanted to stay with WordPress.com hosting because of its ability to scale and low maintenance.  And incredibly, by using just the <a
href="http://en.support.wordpress.com/custom-design/">custom design upgrade</a> and widget areas in the <a
href="http://en.blog.wordpress.com/2010/08/09/new-theme-coraline/">Coraline Theme</a>, it was possible to build exactly what their designer had envisioned.</p><h3>Bluefin Blog After Custom Design</h3><p><a
href="http://blog.bluefinapps.com/"><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/bluefin-590x510.jpg" alt="" title="Bluefin Blog" width="590" height="510" class="size-large wp-image-2157" /></a></p><h3>Bluefin Blog Original</h3><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/bluefin-original-590x419.jpg" alt="" title="bluefin-original" width="590" height="419" class="size-large wp-image-2161" /></p><h3>Benefits of Using WordPress.com</h3><p>Using WordPress.com has a few benefits:</p><ul><li>It&#8217;s completely free to have a basic blog.  The <a
href="http://en.support.wordpress.com/custom-design/">custom design option</a> is only $30/yr, and <a
href="http://en.support.wordpress.com/domain-mapping/map-existing-domain/">custom domains</a> are only $12.  Together, it&#8217;s still about half as much as using a shared host like HostGator or BlueHost.</li><li>It scales up incredibly well.  WordPress.com serves up <a
href="http://en.wordpress.com/stats/">2.5 billion pageviews a month</a>.  Your site will run fast, content is served off a global CDN, and you never have to worry about upgrading a plugin.</li><li>It handles everything most users need.  Contact forms, polls, stats, twitter widgets, code highlighters, and flickr widgets are all available.</li></ul><h3>Disadvantages of Using WordPress.com</h3><p>If you use WordPress.com you won&#8217;t be able to add any plugins.  You&#8217;re stuck with what they got.  No bbPress or Gravity Forms for you, sorry.</p><p>You can&#8217;t alter the theme markup.  Although it&#8217;s possible to do a lot with CSS, you won&#8217;t be able to do everything- especially in the javascript department.  You&#8217;re also limited to the selection of themes on WordPress.com.</p><h3>Tips for Custom Design on WordPress.com</h3><p>If that doesn&#8217;t deter you, here&#8217;s some tips for working with the custom design option:</p><h4>Pick the Right Theme</h4><p>Before you get heavily involved in the design, try to pick out the theme you want to use and work within it&#8217;s limitations.</p><p>I originally chose <a
href="http://en.blog.wordpress.com/2010/12/10/new-theme-toolbox/">Toolbox</a> to build the site off of since it&#8217;s a blank canvas with good markup, but to make the custom footer work I needed widget areas at the bottom.  That&#8217;s why I switched to <a
href="http://en.blog.wordpress.com/2010/08/09/new-theme-coraline/">Coraline</a>, which has an incredibly flexible layout.</p><h4>Use the Widget Areas</h4><p>Widget areas are the one part (besides the actual post content) where you can control the markup.  Just paste in the HTML you need (no scripts or iframes though).</p><h4>Build the Site Locally</h4><p>I built out the Bluefin design locally using MAMP.  All the free themes on WordPress.com are available on <a
href="http://wordpress.org/extend/themes/">WordPress extend</a> or directly <a
href="https://wpcom-themes.svn.automattic.com/">through SVN</a> from Automattic.</p><h4>Make a Child Theme</h4><p>Once you have the theme you plan to customization, make a child theme, import the WordPress.com content (if you have it), and start styling.  The other advantage of building a child theme is that it will be very easy to switch to self-hosted if you need to in the future.</p><h4>Upload All Your Assets to WordPress.com</h3><p>When you&#8217;re ready to move the design over, simply paste in the contents of your child theme&#8217;s CSS in the custom design editor.  If you&#8217;re using any images, those should be uploaded into your media assets on WordPress.com and replaced in the stylesheet so they can be served from WordPress.com&#8217;s CDN.</p><h3>In Conclusion</h3><p>WordPress.com won&#8217;t be for everyone- but it&#8217;s definitely a great option for some.  It&#8217;s low (or free) price and quality support make it a great option for folks who aren&#8217;t ready to make the plunge on self-hosted, and also for companies and organizations that just need a basic website and fairly unique look.</p><p>Also, as a developer it&#8217;s quite nice.  On a .org site you can do anything with enough time and work, but on .com the scope is inherently limited.  In this case, to 400 lines of CSS.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2012/01/custom-design-on-wordpress-com/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Tracking Outbound Links with Google Analytics</title><link>http://wptheming.com/2012/01/tracking-outbound-links-with-google-analytics/</link> <comments>http://wptheming.com/2012/01/tracking-outbound-links-with-google-analytics/#comments</comments> <pubDate>Mon, 09 Jan 2012 17:28:23 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Miscellaneous]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2110</guid> <description><![CDATA[I've recently been working on a website that needs to track external link clicks.  This is something that the WordPress.com stats does by default, but not Google Analytics.  If you want to set it up, you'll need to set up a custom tracker event. <a
href="http://wptheming.com/2012/01/tracking-outbound-links-with-google-analytics/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>I&#8217;ve recently been working on a website that needs to track external link clicks in Google Analytics.  This is something that the <a
href="http://wordpress.org/extend/plugins/jetpack/">WordPress.com stats</a> plugin does by default, but not Google.  In order to track external links with Google Analytics, you&#8217;ll need to set up a <a
href="http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html">custom tracker event</a>.</p><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/events-590x248.gif" alt="" title="events" width="590" height="248" class="alignnone size-large wp-image-2130" /></p><p>The method <a
href="http://support.google.com/googleanalytics/bin/answer.py?hl=en&#038;answer=55527">suggested by Google</a> involves manually applying code to each link.  This is fine if you want to just track a few specific links, but jQuery is more useful if you want to track every external link on the page.</p><h3>Custom Tracking Events</h3><p>The meat of the custom event is this line (which should be fired when an external link is clicked) is:</p><pre class="brush: jscript; gutter: false; title: ; notranslate">
_gat._getTrackerByName()._trackEvent(&quot;Outbound Links&quot;, e.currentTarget.host, url, 0);
</pre><ul><li>&#8220;Outbound Links&#8221; is the category of events to track</li><li>&#8220;e.currentTarget.host&#8221; is the &#8220;action&#8221;, in this case, the domain the user is clicking to.</li><li>&#8220;url&#8221; is the &#8220;label&#8221;, which I use to send the full url of the external link.</li></ul><p>If you&#8217;re unfamiliar with custom events, here&#8217;s the <a
href="http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html">docs page at Google</a>.</p><h3>Code for Detecting and Tracking Outbound Link Clicks</h3><p>This code snippet is highly commented and peppered with console.logs so that you can verify it&#8217;s working correctly and see how it works.  It should only be used in development environments- there&#8217;s a compressed version in the next section for use on live sites.</p><pre class="brush: jscript; title: ; notranslate">
// Outbound Link Tracking with Google Analytics
// Requires jQuery 1.7 or higher (use .live if using a lower version)
// For more info see: http://support.google.com/googleanalytics/bin/answer.py?hl=en&amp;answer=55527
$(&quot;a&quot;).on('click',function(e){
		var url = $(this).attr(&quot;href&quot;);
		// Console logs shows the domain name of the link being clicked and the current window
		console.log('e.currentTarget.host: ' + e.currentTarget.host);
		console.log('window.location.host: ' + window.location.host);
		// If the domains names are different, it assumes it is an external link
		// Be careful with this if you use subdomains
		if (e.currentTarget.host != window.location.host) {
			console.log('external link click');
			// Outbound link!  Fires the Google tracker code.
			_gat._getTrackerByName()._trackEvent(&quot;Outbound Links&quot;, e.currentTarget.host, url, 0);
  		// Checks to see if the ctrl or command key is held down
		// which could indicate the link is being opened in a new tab
		if (e.metaKey || e.ctrlKey) {
			console.log('ctrl or meta key pressed');
			var newtab = true;
		}
		// If it is not a new tab, we need to delay the loading
		// of the new link for a just a second in order to give the
		// Google track event time to fully fire
		if (!newtab) {
			console.log('default prevented');
			e.preventDefault();
                        console.log('loading link after brief timeout');
			//setTimeout('document.location = &quot;' + url + '&quot;', 100);
		}
	}
	else {
		console.log('internal link click');
	}
});
</pre><h3>Compressed Version</h3><p>This is the same code as above, but with comments and console.logs stripped out:</p><pre class="brush: jscript; title: ; notranslate">
// Outbound Link Tracking with Google Analytics
// Requires jQuery 1.7 or higher (use .live if using a lower version)
$(&quot;a&quot;).on('click',function(e){
	var url = $(this).attr(&quot;href&quot;);
	if (e.currentTarget.host != window.location.host) {
		_gat._getTrackerByName('demand')._trackEvent(&quot;Outbound Links&quot;, e.currentTarget.host, url, 0);
		if (e.metaKey || e.ctrlKey) {
		     var newtab = true;
		}
		if (!newtab) {
		     e.preventDefault();
		     setTimeout('document.location = &quot;' + url + '&quot;', 100);
		}
	}
});
</pre><h3>Viewing the Results</h3><p>To see if your custom tracking events are firing correctly, check your Google Analytics dashboard under &#8220;Content > Events > Overview&#8221;.  It may take a couple hours before you start to see the results.  In this screenshot you can clearly see when the tracking began:</p><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2012/01/analytics-events-590x398.gif" alt="" title="analytics-events" width="590" height="398" class="alignnone size-large wp-image-2123" /></p><h3>In WordPress</h3><p>If you are using a plugin for Google Analytics, like <a
href="http://wordpress.org/extend/plugins/google-analyticator/">Google Analyticator</a>, there is a settings fields where you can add javascript to be included with the tracker.  You can also just include it with your other scripts.</p><p>Another option to track links easily in WordPress is to use the stats from <a
href="http://wordpress.org/extend/plugins/jetpack/">Jetpack</a>, which give you outbound click data by default.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2012/01/tracking-outbound-links-with-google-analytics/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Using Multiple Google Analytics Trackers</title><link>http://wptheming.com/2011/12/using-multiple-google-analytics-trackers/</link> <comments>http://wptheming.com/2011/12/using-multiple-google-analytics-trackers/#comments</comments> <pubDate>Thu, 29 Dec 2011 23:05:41 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Miscellaneous]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2093</guid> <description><![CDATA[Sometimes it's necessary to use multiple Google Analytics trackers on the same site.  If you're doing this, never paste in both of the default tracking scripts that Google provides. <a
href="http://wptheming.com/2011/12/using-multiple-google-analytics-trackers/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Sometimes it&#8217;s necessary to use multiple Google Analytics trackers on the same site.  If you&#8217;re doing this, never paste in both of the default tracking scripts that Google provides.</p><p>Instead, set up the tracking variables and just call the Google script once.  Here&#8217;s what I&#8217;ve used:</p><pre class="brush: jscript; title: ; notranslate">
&lt;script&gt;
var _gaq=[
	['_setAccount', 'UA-1111111-1'],['_trackPageview'],['_trackPageLoadTime'],
	['secondTracker._setAccount', 'UA-2222222-1'],['secondTracker._trackPageview'],['secondTracker._trackPageLoadTime']
];
(function(d, t) {
     var g = d.createElement(t),
         s = d.getElementsByTagName(t)[0];
    g.src = '//www.google-analytics.com/ga.js';
    s.parentNode.insertBefore(g, s);
}(document, 'script'));
&lt;/script&gt;
</pre><h3>Notes:</h3><ul><li>This is an optimized version of the GA script from <a
href="http://mathiasbynens.be/notes/async-analytics-snippet">Mathias Bynens</a>.</li><li>You can include more than two.  Just copy the secondTracker code and repeat.</li><li>The &#8220;secondTracker&#8221; label can be changed to anything you like.</li><li>_trackPageLoadTime isn&#8217;t required, but _trackPageview is.</li><li>The code should be placed right before the <a
href="http://code.google.com/apis/analytics/docs/tracking/gaTrackingOverview.html#trackingCodePlacement">close of the body tag</a>.</li></ul> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/12/using-multiple-google-analytics-trackers/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Ajax Themes</title><link>http://wptheming.com/2011/12/ajax-themes/</link> <comments>http://wptheming.com/2011/12/ajax-themes/#comments</comments> <pubDate>Tue, 20 Dec 2011 00:59:53 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Tutorials]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=2026</guid> <description><![CDATA[Ajax is a great method for loading new content onto a web page without having to do an entire page refresh.  It's sometimes used in WordPress themes for paging, or comments- but this tutorial explains how to ajaxify larger portions of the site and use HTML5 pushstates. <a
href="http://wptheming.com/2011/12/ajax-themes/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Ajax is a great method for loading new content onto a web page without having to do an entire page refresh.  It&#8217;s sometimes used in WordPress themes <a
href="http://www.problogdesign.com/wordpress/load-next-wordpress-posts-with-ajax/">for paging</a>, loading in full content after <a
href="http://wordpress.org/extend/plugins/ajax-read-more/">an excerpt</a>, or dynamically displaying <a
href="http://wordpress.org/extend/themes/p2">new comments</a> as they are posted.</p><p>Ajax can also be used to load more significant amounts of content, but it gets more complicated.  URLs don&#8217;t update by default, which is important if someone wants to bookmark that particular post or share it.  Browser navigation buttons for &#8220;Back&#8221; and &#8220;Forward&#8221; can also be an issue.</p><p>Thankfully there are HTML5 methods for dealing with browser history and states, and an awesome jquery plugin that provides some fallback for older browsers.  This tutorial will walk you through the code required for a theme that makes extensive use of ajax and also point you to some live demos.</p><h3>When to Use Ajax</h3><p><a
href="http://designers.mx"><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/12/designermx-590x302.jpg" alt="" title="designermx" width="590" height="302" class="alignnone size-large wp-image-2047" /></a></p><p>Sites that use ajax well generally have a good reason for it.  One of my favorite ajax sites is <a
href="http://designers.mx/">Designers MX</a>, which hosts music mixes.  By using ajax the site is able to keep the music playing uninterrupted while users navigate through the site.</p><p>When animations could be interrupted by a fresh page load, ajax is also extremely helpful.  I developed a site for <a
href="http://byronreese.com/">Byron Reece</a> that features a large slider at the top for selecting posts.  If the user had to load a new page after clicking on an item on the slider the site interaction would not have worked.</p><p>Sites that have heavy initial page weight but then just need to load in small snippets can benefit enormously from ajax.  An example might be a gallery site that displays a huge amount of thumbnails initially, but then just needs a single request for a larger image when the user selects it.</p><h3>How URL Pushstates Work</h3><p>You&#8217;ll notice on the <a
href="http://byronreese.com/">Byron Reece</a> site that when you click on an a different item in the slider the URL updates even though a fresh page load is not happening.  In modern browsers that support <a
href="https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history">HTML5 push states</a>, this url looks like any other you would normally use on the web.  In legacy browsers, like IE8, it reverts to the hash url structure (#!).</p><p>Most of this URL magic is handled by <a
href="https://github.com/balupton/History.js/">history.js</a>.  All you do is send a title and url for the new browser state when the ajax event happens.</p><h3>A Simple Example</h3><p><a
href="http://themes.wptheming.com/ajax-demo/"><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/12/ajaxdemo-590x295.png" alt="Ajax Demo Theme" title="Ajax Demo Theme" width="590" height="295" class="alignnone size-large wp-image-2049" /></a></p><p>I coded out a much simpler example of an <a
href="http://themes.wptheming.com/ajax-demo/">AJAX driven site using the TwentyEleven theme</a> as a base for an example.  Any links clicked in the top navigation menu will load the new content from that link into the #primary and #secondary divs.</p><p>I wouldn&#8217;t suggest building a site like this, as you don&#8217;t get a whole lot of upside for using ajax here, but it works well for demonstration purposes.</p><h3>Enqueue the Required Javascript</h3><p>For the pushstates to work as I used them, you&#8217;ll need to load <a
href="https://github.com/balupton/History.js/">history.js</a>.  There is a compressed version for use with jquery that can be <a
href="https://github.com/balupton/history.js/blob/master/scripts/bundled/html4+html5/jquery.history.js">downloaded here</a>.  I also load another javascript file that contains the custom code, which I saved in ajax_demo_init.js.</p><pre class="brush: php; title: ; notranslate">
/*
 * Loads the scripts that are required for push_states to work.
 *
 * https://github.com/balupton/History.js
 *
 */
function ajax_demo_init() {
	if ( !is_admin() ) {
		wp_deregister_script('historyjs');
		wp_register_script( 'historyjs', get_bloginfo( 'stylesheet_directory' ) . '/js/jquery.history.js', array( 'jquery' ), '1.7.1' );
		wp_enqueue_script( 'historyjs' );
		wp_register_script( 'ajax_demo_init', get_bloginfo( 'stylesheet_directory' ) . '/js/ajax_demo_init.js', array( 'historyjs' ), false, true );
		wp_enqueue_script( 'ajax_demo_init' );
	}
}
add_action( 'wp_enqueue_scripts','ajax_demo_init' );
</pre><h3>ajax_demo_init</h3><p>This is the custom javascript that enables history.js, tells the site which link targets to load via ajax, and pushes the new browser states.  I&#8217;ll post it as a block here, and then explain in more detail below:</p><pre class="brush: jscript; title: ; notranslate">
jQuery(document).ready(function($) {
	// Establish Variables
	var
		History = window.History, // Note: Using a capital H instead of a lower h
		State = History.getState(),
		$log = $('#log');
	// If the link goes to somewhere else within the same domain, trigger the pushstate
	$('#access a').on('click', function(e) {
		e.preventDefault();
		var path = $(this).attr('href');
		var title = $(this).text();
		History.pushState('ajax',title,path);
	});
	// Bind to state change
	// When the statechange happens, load the appropriate url via ajax
	History.Adapter.bind(window,'statechange',function() { // Note: Using statechange instead of popstate
		load_site_ajax();
	});
	// Load Ajax
	function load_site_ajax() {
		State = History.getState(); // Note: Using History.getState() instead of event.state
		// History.log('statechange:', State.data, State.title, State.url);
		//console.log(event);
		$(&quot;#primary&quot;).prepend('&lt;div id=&quot;ajax-loader&quot;&gt;&lt;h4&gt;Loading...&lt;/h4&gt;&lt;/div&gt;');
		$(&quot;#ajax-loader&quot;).fadeIn();
		$('#site-description').fadeTo(200,0);
		$('#content').fadeTo(200,.3);
		$(&quot;#main&quot;).load(State.url + ' #primary, #secondary', function(data) {
			/* After the content loads you can make additional callbacks*/
			$('#site-description').text('Ajax loaded: ' + State.url);
			$('#site-description').fadeTo(200,1);
			$('#content').fadeTo(200,1);
			// Updates the menu
			var request = $(data);
			$('#access').replaceWith($('#access', request));
		});
	}
});
</pre><h3>Pushing the Title and URL</h3><p>This snippet tells the site which links should be loaded by ajax (&#8216;#access a&#8217;).  To fully ajaxify a site you&#8217;d probably want it to <a
href="http://www.mccran.co.uk/index.cfm/2011/7/27/Simple-JQuery-way-to-detect-links-pointing-to-external-domains">detect external vs internal links</a>, and load all the internal ones with ajax.</p><pre class="brush: jscript; title: ; notranslate">
$('#access a').live('click', function(e) {
	e.preventDefault();
	var path = $(this).attr('href');
	var title = $(this).text();
	History.pushState('ajax',title,path);
});
</pre><p>The path variable is what sets the new url.  The title is what will be at the top of the browser window and show up when bookmarked.</p><p>You might need to be a little careful with the title.  You&#8217;ll notice in my <a
href="http://themes.wptheming.com/ajax-demo">Ajax Demo</a> the full title is there on a fresh page load, but on an ajax load it drops site name from the title.  To get it to match perfectly you&#8217;d have to tweak the title variable slightly.</p><h3>Binding the Listener on Statechange</h3><p>This idea took a little while for me to understand.  When someone clicks on a link that you want to load via ajax, you don&#8217;t actually trigger any of the ajax on that click function.  Instead, you have the click trigger a statechange event and pass it the needed variables for url and title.</p><p>The real work is done in the statechange listener.  Here&#8217;s a real simple example with all the console log events uncommented so you can see what&#8217;s going on:</p><pre class="brush: jscript; title: ; notranslate">
// Bind to state change
// When the statechange happens, load the appropriate url via ajax
History.Adapter.bind(window,'statechange',function() { // Note: Using statechange instead of popstate
     State = History.getState(); // Note: Using History.getState() instead of event.state
     History.log('statechange:', State.data, State.title, State.url);
     console.log(event);
});
</pre><p>(The above doesn&#8217;t actually load content, it just posts to the browser console.)</p><p>In the <a
href="http://themes.wptheming.com/ajax-demo">Ajax Demo</a>, a click on &#8220;Example One&#8221; will display the following in the browser console:</p><pre class="brush: jscript; title: ; notranslate">
statechange: [
Object
, &quot;Example One&quot;, &quot;http://themes.wptheming.com/ajax-demo/example-one/&quot;]
MouseEvent
</pre><p>You can see the event was triggered by a mouseclick, and that &#8220;Example One&#8221; is the new title, &#8220;http://themes.wptheming.com/ajax-demo/example-one&#8221; is the URL that will be loaded.</p><p>Any functions in the statechange listener will also be fired when someone clicks the &#8220;back&#8221; or &#8220;forward&#8221; buttons in their browser.</p><h3>Loading in the New Content via Ajax</h3><p>The final piece is to actually load in the new content using ajax.  If you&#8217;re unfamiliar with how this works, I&#8217;d also read the jQuery documentation the <a
href="http://api.jquery.com/load/">load function</a>.  For the TwentyEleven ajax example, we replace the #primary and #secondary divs with content from the new URL.  This all happens inside the load_site_ajax function:</p><pre class="brush: jscript; title: ; notranslate">
$(&quot;#main&quot;).load(State.url + ' #primary, #secondary', function(data) {
     /* After the content loads you can make additional callbacks*/
});
</pre><p>If you look at the original code, you&#8217;ll also notice that a couple other things I do to let the user know that new content is being loaded.</p><p>As soon as the ajax link is clicked, a loader gif is shown:</p><pre class="brush: jscript; title: ; notranslate">
$(&quot;#primary&quot;).prepend('&lt;div id=&quot;ajax-loader&quot;&gt;&lt;h4&gt;Loading...&lt;/h4&gt;&lt;/div&gt;');
$(&quot;#ajax-loader&quot;).fadeIn();
</pre><p>I also dim the content, so that the user is aware that it will change shortly:</p><pre class="brush: jscript; title: ; notranslate">
$('#content').fadeTo(200,.3);
</pre><p>Once the content has loaded, the ajax loader disappears because the new content doesn&#8217;t have the ajax loader in #primary (remember, it was prepended using javascript).  So, all that&#8217;s left to do is undim the new content:</p><pre class="brush: jscript; title: ; notranslate">
$('#content').fadeTo(200,1);
</pre><p>One final issue I had, is that the menu items weren&#8217;t highlighting correctly because WordPress applies certain classes to them depending on which page it has loaded.  When you load new content into #primary or #secondary and change the URL, the menu will keep the same classes it had on the initial page load.  One way around that is to also reload the menu:</p><pre class="brush: jscript; title: ; notranslate">
// Updates the menu
var request = $(data);
$('#access').replaceWith($('#access', request));
</pre><h3>A Note on Browser Compatibility</h3><p>Benjamin (the creator of history.js) wrote a great post about <a
href="https://github.com/balupton/history.js/wiki/Intelligent-State-Handling">supporting url states in legacy browsers</a>.  By legacy, I really mean Internet Explorer 9 and below (<a
href="http://blogs.msdn.com/b/ie/archive/2011/10/31/html5-history-in-ie10.aspx">IE10 is slated to support it</a>).</p><p>There&#8217;s a couple issues with older browsers, but I think the main one is bookmarking.  If a user visits the home page in IE8, then loads a secondary page via ajax, the URL will look something like this:</p><p><a
href="http://themes.wptheming.com/ajax-demo/#example-one/?&#038;_suid=132434056033304790466820008113">http://themes.wptheming.com/ajax-demo/#example-one/?&#038;_suid=132434056033304790466820008113</a></p><p>However, if you visit that URL directly- it will only load the home page, not the page you thought you bookmarked (http://themes.wptheming.com/ajax-demo/example-one).</p><p>I worked out a method for detecting a hash and loading the correct content after the initial load, but it doesn&#8217;t work in modern browsers- and sort of defeats the point of using ajax since it is now loading the initial page content and then new page content:</p><pre class="brush: jscript; title: ; notranslate">
// For legacy browser support
// Only checks for hashes, so more robust support might be needed if you use anchors
if (window.location.hash) {
	load_site_ajax();
}
</pre><p>I&#8217;m sure someone has worked out a better way to do this!  Please share it in the comments.</p><h3>Additional Thing to Watch Out For</h3><p>If you use social buttons like Google+ or Twitter, don&#8217;t expect them to automatically update with the new URL.  I&#8217;ll write a follow-up post about how to get those working.</p><p>A lot of WordPress themes use body classes for styling.  These won&#8217;t update on an ajax load.</p><h3>Did I Miss Something?</h3><p>If you see anywhere I can improve the code, please share it in the comments- especially about legacy browser support.</p><h3>Download the AJAX Demo Code</h3><p>If you&#8217;d like to get the <a
href="http://themes.wptheming.com/ajax-demo/">Ajax Demo</a> theme code, it&#8217;s available as a <a
href="http://pul.ly/b/27124">$5 download</a>.  The javascript code is the same as posted in this tutorial- just conveniently bundled into a theme version.</p><p><a
href="http://pul.ly/b/27124">Purchase the &#8220;Ajax Demo&#8221; Theme for $5</a><br
/> <i>(And thanks for supporting the site!)</i></p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/12/ajax-themes/feed/</wfw:commentRss> <slash:comments>12</slash:comments> </item> <item><title>Event Posts in WordPress</title><link>http://wptheming.com/2011/11/event-posts-in-wordpress/</link> <comments>http://wptheming.com/2011/11/event-posts-in-wordpress/#comments</comments> <pubDate>Mon, 21 Nov 2011 00:18:45 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Custom Post Types]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1935</guid> <description><![CDATA[A tutorial explaining how to create an events post type, complete with custom meta boxes and an example query.  There's also an example plugin that can be downloaded from GitHub. <a
href="http://wptheming.com/2011/11/event-posts-in-wordpress/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>WordPress is a constantly maturing platform.  Just over a year ago I wrote a tutorial about creating custom post types for events- but huge improvements around <a
href="http://scribu.net/wordpress/advanced-metadata-queries.html">advanced meta data queries</a> made my previous approach seem hackish.  This post is a much revised update with code examples.</p><p>If you need an out-of-the-box solution and aren&#8217;t interested in customizing the code, one of these plugins might be a quicker solution:</p><ul><li><a
href="http://wordpress.org/extend/plugins/the-events-calendar/">The Events Calender</a></li><li><a
href="http://wordpress.org/extend/plugins/calendar-press/">Calendar Press</a></li><li><a
href="http://gigpress.com/">GigPress (For Musicans)</a></li></ul><h3>The Basics</h3><p>Building an events post combines three concepts:</p><ul><li><a
href="http://codex.wordpress.org/Post_Types">Custom Post Types</a></li><li><a
href="http://codex.wordpress.org/Function_Reference/add_meta_box">Custom Metaboxes</a></li><li><a
href="http://codex.wordpress.org/Class_Reference/WP_Query#Custom_Field_Parameters">Advanced Meta Queries</a></li></ul><h3>Download the Code</h3><p>I built a working plugin with everything I describe here up on <a
href="https://github.com/devinsays/event-posts">GitHub</a>.  You can install the &#8220;Events Posts&#8221; plugin, and move the file &#8220;archive-events.php&#8221; into your theme in order to follow along.</p><p><b><a
href="https://github.com/devinsays/event-posts">Download the Code on GitHub</a></b></p><h3>The Post Type</h3><p>I create a new post type for events.  You could also use a regular post if you don&#8217;t want a separation between your regular posts and event posts, but for the purposes of this tutorial it makes it easier to explain.</p><p>If you&#8217;re not familiar with custom post types, read <a
href="http://justintadlock.com/archives/2010/04/29/custom-post-types-in-wordpress">Justin Tadlock&#8217;s excellent write up</a>.</p><p>Here&#8217;s how you would create a custom post type for events (<a
href="https://github.com/devinsays/event-posts/blob/master/event-posts.php">view on GitHub</a>):</p><pre class="brush: php; title: ; notranslate">
function ep_eventposts() {
	/**
	 * Enable the event custom post type
	 * http://codex.wordpress.org/Function_Reference/register_post_type
	 */
	$labels = array(
		'name' =&gt; __( 'Events', 'eventposttype' ),
		'singular_name' =&gt; __( 'Event', 'eventposttype' ),
		'add_new' =&gt; __( 'Add New Event', 'eventposttype' ),
		'add_new_item' =&gt; __( 'Add New Event', 'eventposttype' ),
		'edit_item' =&gt; __( 'Edit Event', 'eventposttype' ),
		'new_item' =&gt; __( 'Add New Event', 'eventposttype' ),
		'view_item' =&gt; __( 'View Event', 'eventposttype' ),
		'search_items' =&gt; __( 'Search Events', 'eventposttype' ),
		'not_found' =&gt; __( 'No events found', 'eventposttype' ),
		'not_found_in_trash' =&gt; __( 'No events found in trash', 'eventposttype' )
	);
	$args = array(
    	'labels' =&gt; $labels,
    	'public' =&gt; true,
		'supports' =&gt; array( 'title', 'editor', 'thumbnail', 'comments' ),
		'capability_type' =&gt; 'post',
		'rewrite' =&gt; array(&quot;slug&quot; =&gt; &quot;event&quot;), // Permalinks format
		'menu_position' =&gt; 5,
		'menu_icon' =&gt; plugin_dir_url( __FILE__ ) . '/images/calendar-icon.gif',  // Icon Path
		'has_archive' =&gt; true
	);
	register_post_type( 'event', $args );
}
add_action( 'init', 'ep_eventposts' );
</pre><h3>The Metaboxes</h3><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/11/event-metaboxes.gif" alt="" title="event-metaboxes" width="309" height="281" class="alignright size-full wp-image-1938" />For the user to be able to select an event start time or event end time, you&#8217;ll need to define a couple meta boxes.  My example puts two metaboxes in the right side of the post, and one for location under the main editor.</p><p>I&#8217;ve written other <a
href="http://wptheming.com/2010/08/custom-metabox-for-post-type/">tutorials about metaboxes</a> if you need a more in-depth overview.</p><p>This is a long code snippet, but the basic idea is that we get the current time and populate the metaboxes of a fresh post with that.  When someone saves a post, it checks that they have permissions to edit, and then overwrites the metabox data if it has changed.</p><pre class="brush: php; title: ; notranslate">
/**
 * Adds event post metaboxes for start time and end time
 * http://codex.wordpress.org/Function_Reference/add_meta_box
 *
 * We want two time event metaboxes, one for the start time and one for the end time.
 * Two avoid repeating code, we'll just pass the $identifier in a callback.
 * If you wanted to add this to regular posts instead, just swap 'event' for 'post' in add_meta_box.
 */
function ep_eventposts_metaboxes() {
	add_meta_box( 'ept_event_date_start', 'Start Date and Time', 'ept_event_date', 'event', 'side', 'default', array( 'id' =&gt; '_start') );
	add_meta_box( 'ept_event_date_end', 'End Date and Time', 'ept_event_date', 'event', 'side', 'default', array('id'=&gt;'_end') );
	add_meta_box( 'ept_event_location', 'Event Location', 'ept_event_location', 'event', 'normal', 'default', array('id'=&gt;'_end') );
}
add_action( 'admin_init', 'ep_eventposts_metaboxes' );
// Metabox HTML
function ept_event_date($post, $args) {
	$metabox_id = $args['args']['id'];
	global $post, $wp_locale;
	// Use nonce for verification
	wp_nonce_field( plugin_basename( __FILE__ ), 'ep_eventposts_nonce' );
	$time_adj = current_time( 'timestamp' );
	$month = get_post_meta( $post-&gt;ID, $metabox_id . '_month', true );
	if ( empty( $month ) ) {
		$month = gmdate( 'm', $time_adj );
	}
	$day = get_post_meta( $post-&gt;ID, $metabox_id . '_day', true );
	if ( empty( $day ) ) {
		$day = gmdate( 'd', $time_adj );
	}
	$year = get_post_meta( $post-&gt;ID, $metabox_id . '_year', true );
	if ( empty( $year ) ) {
		$year = gmdate( 'Y', $time_adj );
	}
	$hour = get_post_meta($post-&gt;ID, $metabox_id . '_hour', true);
    if ( empty($hour) ) {
        $hour = gmdate( 'H', $time_adj );
    }
    $min = get_post_meta($post-&gt;ID, $metabox_id . '_minute', true);
    if ( empty($min) ) {
        $min = '00';
    }
	$month_s = '&lt;select name=&quot;' . $metabox_id . '_month&quot;&gt;';
	for ( $i = 1; $i &lt; 13; $i = $i +1 ) {
		$month_s .= &quot;\t\t\t&quot; . '&lt;option value=&quot;' . zeroise( $i, 2 ) . '&quot;';
		if ( $i == $month )
			$month_s .= ' selected=&quot;selected&quot;';
		$month_s .= '&gt;' . $wp_locale-&gt;get_month_abbrev( $wp_locale-&gt;get_month( $i ) ) . &quot;&lt;/option&gt;\n&quot;;
	}
	$month_s .= '&lt;/select&gt;';
	echo $month_s;
	echo '&lt;input type=&quot;text&quot; name=&quot;' . $metabox_id . '_day&quot; value=&quot;' . $day  . '&quot; size=&quot;2&quot; maxlength=&quot;2&quot; /&gt;';
    echo '&lt;input type=&quot;text&quot; name=&quot;' . $metabox_id . '_year&quot; value=&quot;' . $year . '&quot; size=&quot;4&quot; maxlength=&quot;4&quot; /&gt; @ ';
    echo '&lt;input type=&quot;text&quot; name=&quot;' . $metabox_id . '_hour&quot; value=&quot;' . $hour . '&quot; size=&quot;2&quot; maxlength=&quot;2&quot;/&gt;:';
    echo '&lt;input type=&quot;text&quot; name=&quot;' . $metabox_id . '_minute&quot; value=&quot;' . $min . '&quot; size=&quot;2&quot; maxlength=&quot;2&quot; /&gt;';
}
function ept_event_location() {
	global $post;
	// Use nonce for verification
	wp_nonce_field( plugin_basename( __FILE__ ), 'ep_eventposts_nonce' );
	// The metabox HTML
	$event_location = get_post_meta( $post-&gt;ID, '_event_location', true );
	echo '&lt;label for=&quot;_event_location&quot;&gt;Location:&lt;/label&gt;';
	echo '&lt;input type=&quot;text&quot; name=&quot;_event_location&quot; value=&quot;' . $event_location  . '&quot; /&gt;';
}
// Save the Metabox Data
function ep_eventposts_save_meta( $post_id, $post ) {
	if ( defined( 'DOING_AUTOSAVE' ) &amp;&amp; DOING_AUTOSAVE )
		return;
	if ( !isset( $_POST['ep_eventposts_nonce'] ) )
		return;
	if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
		return;
	// Is the user allowed to edit the post or page?
	if ( !current_user_can( 'edit_post', $post-&gt;ID ) )
		return;
	// OK, we're authenticated: we need to find and save the data
	// We'll put it into an array to make it easier to loop though
	$metabox_ids = array( '_start', '_end' );
	foreach ($metabox_ids as $key ) {
		$events_meta[$key . '_month'] = $_POST[$key . '_month'];
	    $events_meta[$key . '_day'] = $_POST[$key . '_day'];
	        if($_POST[$key . '_hour']&lt;10){
	             $events_meta[$key . '_hour'] = '0'.$_POST[$key . '_hour'];
	         } else {
	               $events_meta[$key . '_hour'] = $_POST[$key . '_hour'];
	         }
	    $events_meta[$key . '_year'] = $_POST[$key . '_year'];
	    $events_meta[$key . '_hour'] = $_POST[$key . '_hour'];
	    $events_meta[$key . '_minute'] = $_POST[$key . '_minute'];
	    $events_meta[$key . '_eventtimestamp'] = $events_meta[$key . '_year'] . $events_meta[$key . '_month'] . $events_meta[$key . '_day'] . $events_meta[$key . '_hour'] . $events_meta[$key . '_minute'];
    }
	// Add values of $events_meta as custom fields
	foreach ( $events_meta as $key =&gt; $value ) { // Cycle through the $events_meta array!
		if ( $post-&gt;post_type == 'revision' ) return; // Don't store custom data twice
		$value = implode( ',', (array)$value ); // If $value is an array, make it a CSV (unlikely)
		if ( get_post_meta( $post-&gt;ID, $key, FALSE ) ) { // If the custom field already has a value
			update_post_meta( $post-&gt;ID, $key, $value );
		} else { // If the custom field doesn't have a value
			add_post_meta( $post-&gt;ID, $key, $value );
		}
		if ( !$value ) delete_post_meta( $post-&gt;ID, $key ); // Delete if blank
	}
}
add_action( 'save_post', 'ep_eventposts_save_meta', 1, 2 );
/**
 * Helpers to display the date on the front end
 */
// Get the Month Abbreviation
function eventposttype_get_the_month_abbr($month) {
    global $wp_locale;
    for ( $i = 1; $i &lt; 13; $i = $i +1 ) {
                if ( $i == $month )
                    $monthabbr = $wp_locale-&gt;get_month_abbrev( $wp_locale-&gt;get_month( $i ) );
                }
    return $monthabbr;
}
// Display the date
function eventposttype_get_the_event_date() {
    global $post;
    $eventdate = '';
    $month = get_post_meta($post-&gt;ID, '_month', true);
    $eventdate = eventposttype_get_the_month_abbr($month);
    $eventdate .= ' ' . get_post_meta($post-&gt;ID, '_day', true) . ',';
    $eventdate .= ' ' . get_post_meta($post-&gt;ID, '_year', true);
    $eventdate .= ' at ' . get_post_meta($post-&gt;ID, '_hour', true);
    $eventdate .= ':' . get_post_meta($post-&gt;ID, '_minute', true);
    echo $eventdate;
}
</pre><h3>Displaying Event Posts on Archive Pages</h3><p>To display event posts on archive pages you should run a pre_get_posts filter (<a
href="http://wptheming.com/2011/11/event-posts-in-wordpress/comment-page-1/#comment-17038">hat tip to Bill Erickson</a>).  This example filter will display the event posts five per page, sorted by their _start_eventtimestamp meta key in ascending order, and only display posts that have a start time that is later than the current time.</p><p>The following code could be placed in functions.php.  For the example plugin, it&#8217;s already in event-posts.php.</p><pre class="brush: php; title: ; notranslate">
/**
 * Customize Event Query using Post Meta
 *
 * @link http://www.billerickson.net/customize-the-wordpress-query/
 * @param object $query data
 *
 */
function ep_event_query( $query ) {
	// http://codex.wordpress.org/Function_Reference/current_time
	$current_time = current_time('mysql');
	list( $today_year, $today_month, $today_day, $hour, $minute, $second ) = split( '([^0-9])', $current_time );
	$current_timestamp = $today_year . $today_month . $today_day . $hour . $minute;
	global $wp_the_query;
	if ( $wp_the_query === $query &amp;&amp; !is_admin() &amp;&amp; is_post_type_archive( 'event' ) ) {
		$meta_query = array(
			array(
				'key' =&gt; '_start_eventtimestamp',
				'value' =&gt; $current_timestamp,
				'compare' =&gt; '&gt;'
			)
		);
		$query-&gt;set( 'meta_query', $meta_query );
		$query-&gt;set( 'orderby', 'meta_value_num' );
		$query-&gt;set( 'meta_key', '_start_eventtimestamp' );
		$query-&gt;set( 'order', 'ASC' );
		$query-&gt;set( 'posts_per_page', '5' );
	}
}
add_action( 'pre_get_posts', 'ep_event_query' );
</pre><h3>Other Queries</h3><p>If you just wanted to display the event posts in the sidebar or on a different template (and not worry about paging), you could do something like this:</p><pre class="brush: php; title: ; notranslate">
 $args = array( 'post_type' =&gt; 'event',
'meta_key' =&gt; '_start_eventtimestamp',
'orderby'=&gt; 'meta_value_num',
'order' =&gt; 'ASC',
'posts_per_page' =&gt; 20,
 );
 $events = new WP_Query( $args );
if ( $events-&gt;have_posts() ) :
	echo '&lt;ul&gt;';
	while ( $events-&gt;have_posts() ) : $events-&gt;the_post();
		echo '&lt;li&gt;&lt;a href=&quot;' . get_permalink() . '&quot;&gt;' . get_the_title() . '&lt;/a&gt;&lt;/li&gt;';
	endwhile;
	echo '&lt;/ul&gt;';
endif;
</pre><h3>Extending</h3><p>This tutorial isn&#8217;t meant to be a full-fledged plugin- just an example to get you started.  Several improvements could be made, such as using a jquery datepicker to select the date (or even just making sure you end time is after your start time and a valid date).  If your primary users are in the United States or other countries on a 12-hour clock, you might want to use an AM/PM selector.</p><p>If you&#8217;re interested in learning more, check out <a
href="http://www.noeltock.com/web-design/wordpress/custom-post-types-events-pt1/">Noel Tock&#8217;s excellent events tutorial</a> which covers some of these examples.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/11/event-posts-in-wordpress/feed/</wfw:commentRss> <slash:comments>17</slash:comments> </item> <item><title>Cleaning Up the TimThumb Hack</title><link>http://wptheming.com/2011/08/cleaning-up-the-timthumb-hack/</link> <comments>http://wptheming.com/2011/08/cleaning-up-the-timthumb-hack/#comments</comments> <pubDate>Sat, 27 Aug 2011 22:55:59 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Tutorials]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1750</guid> <description><![CDATA[Several of my websites were hacked this last week using the TimThumb exploit.  Here's instructions on how to find the files and replace them- and also the process I used for patching up my sites after the hack. <a
href="http://wptheming.com/2011/08/cleaning-up-the-timthumb-hack/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<div
id="attachment_1759" class="wp-caption alignright" style="width: 310px"><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/tim-300x176.jpg" alt="" title="tim" width="300" height="176" class="size-medium wp-image-1759" /><p
class="wp-caption-text">Tim Thumb Hacker</p></div><p>Several of my websites were hacked this week using the TimThumb exploit.  The issue has been <a
href="http://markmaunder.com/2011/08/02/technical-details-and-scripts-of-the-wordpress-timthumb-php-hack/">known for a couple weeks</a> now.</p><p>Although I had updated the majority of sites and had notified former clients, I still hadn&#8217;t gotten to some of the smaller sites yet- like my <a
href="http://food.lainehardy.com">girlfriend&#8217;s food blog</a>.</p><p>And word to the wise, your girlfriend&#8217;s food blog should always be top priority.</p><p>Hackers are using a variety of techniques to hijack WordPress sites right now, but this is how I cleaned up the ones on my server.</p><h3>Make Backup of Everything</h3><p>Common sense, but worth mentioning again.  You never know when you might accidentally delete a directory you need or wipe part of the database.  Most cPanels wills let you easily export a copy of the database.  And it&#8217;s a lot easier to download a second copy or your files then rebuild them all from scratch.</p><h3>Get Shell Access to Your Host</h3><p>If you only have one site, this may not be necessary.  But I have over twenty WordPress installs running on my server and I wanted to find all the files that were compromised and fix them quickly.  Most hosts offer shell access.  With BlueHost, I just had to go into my control panel and enable it.</p><p>Here&#8217;s BlueHost&#8217;s <a
href="https://my.bluehost.com/cgi/help/180">instructions for setting up shell access</a> and for <a
href="https://my.bluehost.com/cgi/help/301">logging in via shell</a>.</p><h3>Fix TimThumb Vulnerability</h3><p>You can download the latest version of TimThumb with the security fixes here: <a
href="http://timthumb.googlecode.com/svn/trunk/timthumb.php">http://timthumb.googlecode.com/svn/trunk/timthumb.php</a> (Just save the file out).</p><p>Replace any instances of TimThumb.php on your server with the new version.  WooThemes used the name &#8220;thumb.php&#8221; for this file, so you should also look for that.</p><p>If you have shell access you can do a quick search to find all instances of timthumb with:</p><pre>
find *  -iname 'timthumb*' -ls
</pre><p>or</p><pre>
find *  -iname 'thumb.php' -ls (for WooTheme versions)
</pre><p>In many cases I found themes that were not being used and just deleted them directly:</p><pre>
rm -rf path/to/theme
</pre><p>Most theme companies have also already released fixes, so you could also get the latest version directly from them and replace your current theme.</p><h3>Clean Up After the Hack</h3><p>DISCLAIMER: I don&#8217;t consider myself to be a security expert, but these are the steps I took to clean up my site.  If anyone else has additional recommendations, please drop them in the comments or post a link.</p><ol><li>I wiped the entire directory of the hacked site since I didn&#8217;t know which files has been added or compromised.</li><li>I changed my database passwords and uploaded a new clean version of WordPress with a fresh wp-config.php file.</li><p>If you&#8217;re using shell this is very quick (<a
href="http://codex.wordpress.org/Installing_WordPress">http://codex.wordpress.org/Installing_WordPress</a>):<br/></p><pre>
wget http://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz
</pre></li><li>I manually checked my backed-up &#8220;wp-content&#8221; to make sure no odd new files had been added.  I specifically checked for files that other folks have reported as being exploited, like:<br
/> <br/></p><pre>
/wp-content/uploads/feed-file.php
/wp-content/uploads/feed-files.php
/wp-content/themes/******/cache/.htaccess
</pre><p>The file in my case was:</p><pre>
/wp-content/data.php
</pre><p>There&#8217;s an excellent post that goes into the hacking methods in more detail and suggests other files to check at: <a
href="http://redleg-redleg.blogspot.com/2011/08/malware-hosted-newportalsecom.html">http://redleg-redleg.blogspot.com/2011/08/malware-hosted-newportalsecom.html</a></li><li>I also grepped the backed-up &#8220;wp-content&#8221; directory for any files with base64_decode.  There are legitimate reasons to have base64_decode in a file, but if you don&#8217;t know where the file came from, or what it does, find out.<p>Here&#8217;s how you grep a directory:</p><pre>grep -r base64_decode *</pre><p>If you want to grep your entire server, try</p><pre>grep -r --exclude={wp-app.php,class-simplepie.php,class-IXR.php} base64_decode *</pre></li><li>When I was reasonably confident my backup wp-content directory was clean, I re-uploaded it.</li><li>I reset my <a
href="http://codex.wordpress.org/Hardening_WordPress#File_permissions">file permissions as specified by WordPress</a> in the codex</li><li>I logged back into WordPress and reset the admin passwords.</li><li>I reset my permalinks to be completely sure the htaccess was overwritten.</li></ol><h3>Anything I Missed?</h3><p>If anyone else has other suggestions or recommendations, please post them below.</p><p>And please update <strong>ALL</strong> sites that might have the TimThumb vulnerability.  Your girlfriend will thank you.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/08/cleaning-up-the-timthumb-hack/feed/</wfw:commentRss> <slash:comments>13</slash:comments> </item> <item><title>Portfolio Post Type Plugin</title><link>http://wptheming.com/2011/08/portfolio-post-type-plugin/</link> <comments>http://wptheming.com/2011/08/portfolio-post-type-plugin/#comments</comments> <pubDate>Fri, 26 Aug 2011 23:21:23 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Custom Post Types]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1704</guid> <description><![CDATA[I recently released a plugin to WordPress.org that registers a portfolio post type, related taxonomies, and adds an image when you're viewing the items in the dashboard.  Here's a walk through of the code involved. <a
href="http://wptheming.com/2011/08/portfolio-post-type-plugin/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>I recently released a <a
href="http://wordpress.org/extend/plugins/portfolio-post-type">plugin on WordPress.org</a> that registers a portfolio post type, related taxonomies, and adds an image when you&#8217;re viewing the items in the dashboard.  It clocks in at a neat 202 lines of code, most of that being labels.</p><p><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/portfolio-590x314.png" alt="" title="Portfolio Dashboard" width="590" height="314" class="alignnone size-large wp-image-1701" /></p><p>I&#8217;ve held onto to the plugin for a while in GitHub since it&#8217;s an easy thing for any developer to build out- but I&#8217;ve recently been thinking that a standard plugin in the repository might be beneficial as it could make more themes more portable.  (Especially for users of my <a
href="http://wordpress.org/extend/themes/portfolio-press">Portfolio Press</a> theme)</p><p>And since I expect people to hack on this a bit as well, I wanted to do a walk through of the code.</p><h3>Plugin Header</h3><pre class="brush: php; title: ; notranslate">
/*
Plugin Name: Portfolio Post Type
Plugin URI: http://www.wptheming.com
Description: Enables a portfolio post type and taxonomies.
Version: 0.1
Author: Devin Price
Author URI: http://wptheming.com/portfolio-post-type/
License: GPLv2
*/
</pre><p>Explanation:  All plugins need to have basic header data in order to show up in plugin admin page.  If you don&#8217;t want to get updates to the plugin (I don&#8217;t expect many), change the name to something different.  Shoot, feel free to change any of this information- GPL baby!</p><h3>Enable the Portfolio Post Type</h3><pre class="brush: php; title: ; notranslate">
function portfolioposttype() {
	/**
	 * Enable the Portfolio custom post type
	 * http://codex.wordpress.org/Function_Reference/register_post_type
	 */
	$labels = array(
		'name' =&gt; __( 'Portfolio', 'portfolioposttype' ),
		'singular_name' =&gt; __( 'Portfolio Item', 'portfolioposttype' ),
		'add_new' =&gt; __( 'Add New Item', 'portfolioposttype' ),
		'add_new_item' =&gt; __( 'Add New Portfolio Item', 'portfolioposttype' ),
		'edit_item' =&gt; __( 'Edit Portfolio Item', 'portfolioposttype' ),
		'new_item' =&gt; __( 'Add New Portfolio Item', 'portfolioposttype' ),
		'view_item' =&gt; __( 'View Item', 'portfolioposttype' ),
		'search_items' =&gt; __( 'Search Portfolio', 'portfolioposttype' ),
		'not_found' =&gt; __( 'No portfolio items found', 'portfolioposttype' ),
		'not_found_in_trash' =&gt; __( 'No portfolio items found in trash', 'portfolioposttype' )
	);
	$args = array(
    	'labels' =&gt; $labels,
    	'public' =&gt; true,
		'supports' =&gt; array( 'title', 'editor', 'thumbnail', 'comments' ),
		'capability_type' =&gt; 'post',
		'rewrite' =&gt; array(&quot;slug&quot; =&gt; &quot;portfolio&quot;), // Permalinks format
		'menu_position' =&gt; 5,
		'has_archive' =&gt; true
	);
	register_post_type( 'portfolio', $args);
}
add_action( 'init', 'portfolioposttype' );
</pre><p>This is the basic code needed to register the portfolio post type.  I&#8217;ve wrapped it all in one function that fires on init.  For a deeper explanation of all the arguments ($args) check out <a
href="http://codex.wordpress.org/Function_Reference/register_post_type">the codex post</a>.</p><h3>Register Taxonomies</h3><p>Wrapped in that same function I also register two taxonomies for categories and tags.  Since that code is long, and basically just a bunch of labels I&#8217;ll point you to the <a
href="https://github.com/devinsays/portfolio-post-type/blob/master/portfolio-post-type.php">actual code on GitHub</a>.  If you don&#8217;t want taxonomies, you don&#8217;t actually need that code.</p><h3>Use Post Thumbnails</h3><p>If you want your post type to be able to use thumbnails, you&#8217;ll need to register it:</p><pre class="brush: php; title: ; notranslate">
// Allow thumbnails to be used on portfolio post type
add_theme_support( 'post-thumbnails', array( 'portfolio' ) );
</pre><h3>View Thumbnail Images in Column View of Dashboard</h3><p>For something like portfolio items, it&#8217;s nice to see the images when you&#8217;re scanning posts in the dashboard.  I wrote about how that <a
href="http://wptheming.com/2010/07/column-edit-pages/">functionality works in a separate post.</a> That&#8217;s handled by the code listed under:</p><pre class="brush: php; title: ; notranslate">
/**
 * Add Columns to Portfolio Edit Screen
 * http://wptheming.com/2010/07/column-edit-pages/
 */
 </pre><h3>Display a Custom Icon for Portfolio Menus</h3><p>I was lucky enough to get Ben Dunkle (the designer of the WordPress icons), to make some portfolio icons for me.  To display theme out in the dasboard, here&#8217;s the code:</p><pre class="brush: php; title: ; notranslate">
/**
 * Displays the custom post type icon in the dashboard
 */
function portfolioposttype_portfolio_icons() {
    ?&gt;
    &lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
        #menu-posts-portfolio .wp-menu-image {
            background: url(&lt;?php echo plugin_dir_url( __FILE__ ); ?&gt;images/portfolio-icon.png) no-repeat 6px 6px !important;
        }
		#menu-posts-portfolio:hover .wp-menu-image, #menu-posts-portfolio.wp-has-current-submenu .wp-menu-image {
            background-position:6px -16px !important;
        }
		#icon-edit.icon32-posts-portfolio {background: url(&lt;?php echo plugin_dir_url( __FILE__ ); ?&gt;images/portfolio-32x32.png) no-repeat;}
    &lt;/style&gt;
&lt;?php }
add_action( 'admin_head', 'portfolioposttype_portfolio_icons' );
</pre><p>I also wrote about custom post type icons in <a
href="http://wptheming.com/2010/11/how-to-use-custom-post-type-icons/">more depth on this post</a>.</p><h3>Custom Meta Boxes</h3><p>This plugin doesn&#8217;t register any custom meta boxes, but those obviously might be useful if you want to include a link a website, or a purchase button, etc.  If you&#8217;re interested in doing that- see my post on adding <a
href="http://wptheming.com/2010/08/custom-metabox-for-post-type/">custom meta boxes to post types</a>.</p><h3>Download</h3><ul><li><a
href="http://wordpress.org/extend/plugins/portfolio-post-type/">WordPress.org</a></li><li><a
href="https://github.com/devinsays/portfolio-post-type">GitHub</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/08/portfolio-post-type-plugin/feed/</wfw:commentRss> <slash:comments>13</slash:comments> </item> <item><title>Admin Notices in WordPress</title><link>http://wptheming.com/2011/08/admin-notices-in-wordpress/</link> <comments>http://wptheming.com/2011/08/admin-notices-in-wordpress/#comments</comments> <pubDate>Wed, 10 Aug 2011 00:51:37 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Tutorials]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1638</guid> <description><![CDATA[Occasionally a plugin or theme will need to display a notice to users in the WordPress dashboard. This is fairly easy to do using the admin_notices hook, which shows a standard message box at the top of the screen. <a
href="http://wptheming.com/2011/08/admin-notices-in-wordpress/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Occasionally a plugin or theme will need to display a notice to users in the WordPress dashboard.  This is fairly easy to do using the admin_notices hook, which shows a standard message box at the top of the screen.</p><div
id="attachment_1657" class="wp-caption alignnone" style="width: 600px"><img
src="http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/admin_notices-590x334.png" alt="" title="admin_notices" width="590" height="334" class="size-large wp-image-1657" /><p
class="wp-caption-text">Example Admin Notices</p></div><h3>Display a Standard Notice</h3><pre class="brush: php; title: ; notranslate">
function my_admin_notice(){
    echo '&lt;div class=&quot;updated&quot;&gt;
       &lt;p&gt;I am a little yellow notice.&lt;/p&gt;
    &lt;/div&gt;';
}
add_action('admin_notices', 'my_admin_notice');
</pre><p>Since this div is classed &#8220;updated&#8221; the notice will display yellow.  If the class is changed to &#8220;error&#8221; it displays in red.</p><h3>How To Make a Dismissible Notice</h3><p>With a little more work it&#8217;s also possible to display a notice stays present until the user clicks to ignore it.  This could be a good way to ensure the user sees the message but also won&#8217;t get annoyed by it.</p><p>The following example was adapted from the <a
href="http://wordpress.org/extend/plugins/addthis/">AddThis</a> plugin.  I also use something similar in the <a
href="https://github.com/devinsays/options-framework-plugin">Options Framework</a>.</p><p>If a user clicks to hide the notice, it will save their preference in the user meta.</p><pre class="brush: php; title: ; notranslate">
/* Display a notice that can be dismissed */
add_action('admin_notices', 'example_admin_notice');
function example_admin_notice() {
	global $current_user ;
        $user_id = $current_user-&gt;ID;
        /* Check that the user hasn't already clicked to ignore the message */
	if ( ! get_user_meta($user_id, 'example_ignore_notice') ) {
        echo '&lt;div class=&quot;updated&quot;&gt;&lt;p&gt;';
        printf(__('This is an annoying nag message.  Why do people make these? | &lt;a href=&quot;%1$s&quot;&gt;Hide Notice&lt;/a&gt;'), '?example_nag_ignore=0');
        echo &quot;&lt;/p&gt;&lt;/div&gt;&quot;;
	}
}
add_action('admin_init', 'example_nag_ignore');
function example_nag_ignore() {
	global $current_user;
        $user_id = $current_user-&gt;ID;
        /* If user clicks to ignore the notice, add that to their user meta */
        if ( isset($_GET['example_nag_ignore']) &amp;&amp; '0' == $_GET['example_nag_ignore'] ) {
             add_user_meta($user_id, 'example_ignore_notice', 'true', true);
	}
}
</pre><h3>Display Notices Only On Certain Admin Pages</h3><p>If possible, target the notice to only appear on certain pages where the user needs to see them.  You can do that by using the $pagenow global variable.</p><p>For example, this notice will only appear on the plugins page:</p><pre class="brush: php; title: ; notranslate">
function my_admin_notice(){
    global $pagenow;
    if ( $pagenow == 'plugins.php' ) {
         echo '&lt;div class=&quot;updated&quot;&gt;
             &lt;p&gt;This notice only appears on the plugins page.&lt;/p&gt;
         &lt;/div&gt;';
    }
}
add_action('admin_notices', 'my_admin_notice');
</pre><h3>Check User Role Before Displaying a Notice</h3><p>Notices should only be displayed to users that can actually do something about them.  For instance, if the user cannot edit theme options, there&#8217;s no point in displaying a notice about it.</p><p>Here&#8217;s some common roles checks you might want to do:</p><pre class="brush: php; title: ; notranslate">
if ( current_user_can( 'install_plugins' ) )
if ( current_user_can( 'manage_options' ) )
if ( current_user_can( 'edit_theme_options' ) )
</pre><h3>Notice Etiquette</h3><p>Notices can be annoying, so be careful with how you use them.  Keep the text short and try not display more than one.  Use sparingly.</p><h3>Other Resources</h3><ul><li><a
href="http://theme.it/how-to-display-an-admin-notice-for-required-theme-plugins">http://theme.it/how-to-display-an-admin-notice-for-required-theme-plugins</a></li><li><a
href="http://codex.wordpress.org/Plugin_API/Action_Reference/admin_notices">http://codex.wordpress.org/Plugin_API/Action_Reference/admin_notices</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/08/admin-notices-in-wordpress/feed/</wfw:commentRss> <slash:comments>3</slash:comments> </item> <item><title>Sanitization Filters and Other Updates</title><link>http://wptheming.com/2011/05/options-framework-0-6/</link> <comments>http://wptheming.com/2011/05/options-framework-0-6/#comments</comments> <pubDate>Tue, 10 May 2011 04:54:55 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Plug-ins]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1388</guid> <description><![CDATA[I've been steadily improving the Options Framework plugin these last couple weeks.  The biggest new features are sanitization filters and better validation... <a
href="http://wptheming.com/2011/05/options-framework-0-6/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>I&#8217;ve been steadily improving the <a
href="http://wptheming.com/options-framework-plugin/">Options Framework plugin</a> these last several weeks and there&#8217;s several nice changes in this 0.6 release.  The biggest new feature is sanitization filters and better validation, but there&#8217;s also a slew of other less noticeable code improvements.</p><p>Unfortunately, a couple things that worked in previous versions will break with this release unless theme code is updated.  I debated these changes quite a bit but decided it was best to do this while the plugin is still relatively young and would affect the least amount of people.</p><h3>Validation Filters</h3><p>Validation filters are the most exciting update in the Options Framework.  Not only is there better sanitization (read security), it&#8217;s also now possible to change the default validation on any input without hacking the plugin directly.</p><p>Several users had mentioned that they wanted to enter some html in the textareas.  I updated this so $allowedtags can be used.  But if you wanted a different sanitization method, the default can now be removed and a new one added in via your themes functions.  (Edit: <a
href="https://github.com/devinsays/options-framework-plugin/issues/61">$allowedposttags might be used soon instead</a>)</p><p>Since, &#8220;embed&#8221; is not one of the $allowedtags- here&#8217;s how you could remove the default santization from a text area and allow &#8220;embed&#8221; to be used:</p><pre class="brush: php; title: ; notranslate">
/*
 * This is an example of how to override a default filter
 * for 'textarea' sanitization and use a different one.
 */
add_action('admin_init','optionscheck_change_santiziation', 100);
function optionscheck_change_santiziation() {
	remove_filter( 'of_sanitize_textarea', 'of_sanitize_textarea' );
	add_filter( 'of_sanitize_textarea', 'of_sanitize_textarea_custom' );
}
function of_sanitize_textarea_custom($input) {
	global $allowedtags;
        $of_custom_allowedtags[&quot;embed&quot;] = array(
             &quot;src&quot; =&gt; array(),
             &quot;type&quot; =&gt; array(),
             &quot;allowfullscreen&quot; =&gt; array(),
             &quot;allowscriptaccess&quot; =&gt; array(),
             &quot;height&quot; =&gt; array(),
	     &quot;width&quot; =&gt; array()
	);
        $of_custom_allowedtags = array_merge($of_custom_allowedtags, $allowedtags);
        $output = wp_kses( $input, $of_custom_allowedtags);
	return $output;
}
</pre><h3>Allow Script and Embed Tags</h3><p>A lot of folks have been asking how to include all the $allowedpsttags in addition to script and embed tags.  Here&#8217;s how you would do it (hat tip <a
href="http://www.designcrumbs.com/">Jake Caputo</a>):</p><pre class="brush: php; title: ; notranslate">
/*
 * This is an example of how to override a default filter
 * for 'textarea' sanitization and $allowedposttags + embed and script.
 */
add_action('admin_init','optionscheck_change_santiziation', 100);
function optionscheck_change_santiziation() {
    remove_filter( 'of_sanitize_textarea', 'of_sanitize_textarea' );
    add_filter( 'of_sanitize_textarea', 'custom_sanitize_textarea' );
}
function custom_sanitize_textarea($input) {
    global $allowedposttags;
    $custom_allowedtags[&quot;embed&quot;] = array(
      &quot;src&quot; =&gt; array(),
      &quot;type&quot; =&gt; array(),
      &quot;allowfullscreen&quot; =&gt; array(),
      &quot;allowscriptaccess&quot; =&gt; array(),
      &quot;height&quot; =&gt; array(),
          &quot;width&quot; =&gt; array()
      );
      $custom_allowedtags[&quot;script&quot;] = array();
      $custom_allowedtags = array_merge($custom_allowedtags, $allowedposttags);
      $output = wp_kses( $input, $custom_allowedtags);
    return $output;
}
</pre><h3>Checkbox Code Has Changed</h3><p>The most noticeable change in version 0.6 is how checkbox data is stored.  The reason it&#8217;s the most noticeable is because it&#8217;s the one most likely to break a current implementation.  Instead of storing checkbox data as &#8220;true&#8221; or &#8220;false&#8221;, I&#8217;ve changed it to &#8220;1&#8243; or &#8220;0&#8243;, which is how WordPress itself stores checkbox values and much smarter way to do it.  I&#8217;ve written about this more in another post called <a
href="http://wptheming.com/2011/05/checkboxes-and-booleans/">&#8220;Checkboxes and Booleans&#8221;</a>.</p><p>If you currently are using checkboxes in your theme, you should update options.php to use a default value of &#8220;0&#8243; or &#8220;1&#8243;.  You should also update how the theme reads checkbox data to something like:</p><pre class="brush: php; title: ; notranslate">
if ( of_get_option('checkbox_option', &quot;0&quot;) ) {
	do_action();
}
</pre><h3>Helper Function Returns Boolean False by Default</h3><p>Instead of having the helper function of_get_option return the string &#8220;false&#8221; by default, I&#8217;ve updated it to return the boolean false (thanks so a suggestion from @sawyerh).  Again, there&#8217;s more about this in my post on <a
href="http://wptheming.com/2011/05/checkboxes-and-booleans/">booleans</a>.  This should just make it easier to check if options are saved in the database.  This change isn&#8217;t likely to break something if it isn&#8217;t updated, but the new code for your theme should look like this:</p><pre class="brush: php; title: ; notranslate">
if ( !function_exists( 'of_get_option' ) ) {
function of_get_option($name, $default = false) {
	$optionsframework_settings = get_option('optionsframework');
	// Gets the unique option id
	$option_name = $optionsframework_settings['id'];
	if ( get_option($option_name) ) {
		$options = get_option($option_name);
	}
	if ( !empty($options[$name]) ) {
		return $options[$name];
	} else {
		return $default;
	}
}
}
</pre><h3>Better Handling of Option Arrays</h3><p>I updated the code for how arrays of options (like multicheckbox, background, and typography) are processed and stored.  I don&#8217;t think should affect current users- but it&#8217;s something to keep an eye on.  The new code allows the options to be stored directly into the array rather than having an additional step in the validation to process it.</p><h3>Better Display of Messages</h3><p>For anyone who dug into the code of 0.5, you probably saw my very awkward way of displaying the &#8220;Options Reset&#8221; and &#8220;Options Saved&#8221; messages.  I had an option in the database for saving the message which was set when the form was submitted and then later cleared after the message was displayed.</p><p>By digging through WordPress core I found a more elegant way to do it.  At the top of the options page I now use settings_errors(), which automatically displays any messages.</p><p>The messages are set in optionsframework_validate() as it processes the form.  So, the reset message it now set like so:</p><pre class="brush: php; title: ; notranslate">
// If the reset button was clicked
	if (!empty($_POST['reset'])) {
		// If options are deleted sucessfully update the error message
		if (delete_option($option_name) ) {
			add_settings_error('options-framework', 'restore_defaults', __('Default options restored.'), 'updated fade');
		}
	}
</pre><h3>Thanks and Credits</h3><p>I owe a big thanks to <a
href="http://wordpress.mfields.org/">Michael Fields</a> for this release.  He advised me on how to implement the validation filters and did a thorough code review which caught many of the html escaping errors.  If you need a really solid WordPress developer to check your code, I&#8217;d highly recommend him.</p><p>I was only able to have a code review done thanks to several generous donations.  Having cash to spend on a third party code check was extremely helpful, made the plugin much stronger, and was well spent!  If you use the plugin and can contribute, a <a
href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&#038;hosted_button_id=X238BDP4QGTV2">PayPal donation</a> will make my day.</p><p>And finally, several people have been helping out with testing and development on GitHub.  I really appreciate it!  I&#8217;m probably leaving someone out, but big thanks to @mantone, @rejithomas, @sawyerh, @samargulies, @mfields, @adampickeringq, @djevrek, @utkarshkukreti, @celtic7, @helgatheviking, @johnraz, @jmilone, @thumbnet, and @pastorjack.</p><p>Update: Version 0.6 has been released</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/05/options-framework-0-6/feed/</wfw:commentRss> <slash:comments>40</slash:comments> </item> <item><title>Checkboxes and Booleans</title><link>http://wptheming.com/2011/05/checkboxes-and-booleans/</link> <comments>http://wptheming.com/2011/05/checkboxes-and-booleans/#comments</comments> <pubDate>Sun, 08 May 2011 05:30:09 +0000</pubDate> <dc:creator>Devin</dc:creator> <category><![CDATA[Tutorials]]></category> <category><![CDATA[booleans]]></category> <guid
isPermaLink="false">http://wptheming.com/?p=1372</guid> <description><![CDATA[A little tutorial on boolean basics.  The string "false" returns true, and other fun facts. <a
href="http://wptheming.com/2011/05/checkboxes-and-booleans/">Continue reading <span
class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Checkboxes are one of the more difficult form elements to deal with.  For one, checkboxes don&#8217;t get sent in $_POST request unless they are checked.  Any other element, like a text input or a radio button, will get sent regardless of it&#8217;s state.  But if someone were to uncheck an option form and send it there would be no indication in the $_POST request that its state had changed.</p><p>The way to get around this, or the way that I did, is to automatically assume all checkboxes in a form are unchecked unless your $_POST data explicitly tells you otherwise.  In the <a
href="https://github.com/devinsays/options-framework-plugin">Options Framework</a> this required an extra step as I was looping through my options array and validating the data from the $_POST:</p><pre class="brush: php; title: ; notranslate">
function optionsframework_validate($input) {
if (!empty($_POST['update'])) {
     // Get the options array defined for the form
     $options = optionsframework_options();
     foreach ($options as $option) {
          $id = $option['id'];
          //  Set the check box to &quot;0&quot; by default
          if ( 'checkbox' == $option['type'] &amp;&amp; ! isset( $input[$id] ) ) {
               $input[$id] = &quot;0&quot;;
           }
</pre><p>In the above code I&#8217;m setting any checkboxes that weren&#8217;t set in the form (and therefore weren&#8217;t checked) to &#8220;0&#8243;, and combining with all the other form data ($input) that was sent in the $_POST request.  That way I have a complete set of checkboxes available in my $input rather than just the ones that were checked.</p><h3>How to Store Checkbox State in the Database</h3><p>The other main issue I had was how to store the checkbox data in the database.  In the early versions of the <a
href="https://github.com/devinsays/options-framework-plugin">Options Framework</a> I saved checkbox data as either &#8220;true&#8221; or &#8220;false&#8221;.  This was fine as long as I always evaluated the option (e.g. if ( get_option(&#8220;checkbox&#8221;) == &#8220;false&#8221;) ) { do something }).</p><p>However, this caused a problem if I wanted to call the option directly, because if &#8220;checkbox&#8221; is set to &#8220;false&#8221;, get_option(&#8220;checkbox&#8221;) returns true.  Confused?</p><p>&#8220;False&#8221; (notice the quotes) is a string not a boolean, and most strings return true.  If the option &#8220;checkbox&#8221; was set to &#8220;pancake&#8221;, that would also return true.</p><p>This little script checks 11 values and gives their boolean result:</p><pre class="brush: php; title: ; notranslate">
$booleancheck = array(1,0,-1,&quot;1&quot;,&quot;0&quot;,false,true,'false','true',&quot;randomstring&quot;,&quot;&quot;);
echo '&lt;ul&gt;';
foreach ($booleancheck as $key) {
     echo '&lt;li&gt;'. $key.': ';
     if ($key) {
          echo &quot;True&quot;;
      }
      else {
           echo &quot;False&quot;;
      }
      echo '&lt;/li&gt;';
      }
echo '&lt;/ul&gt;';
</pre><p>You&#8217;ll see that &#8220;0&#8243;,&#8221;1&#8243; will return false and true respectively regardless of whether they are sent as a string or an integer- and that&#8217;s the way I eventually decided to save checkbox data.</p><p>I also should have studied the WordPress core options a little earlier because that&#8217;s how it does it.  If you pull up /wp-admin/options.php of your WordPress site you&#8217;ll see all check boxes either saved as &#8220;0&#8243; or &#8220;1&#8243;.</p> ]]></content:encoded> <wfw:commentRss>http://wptheming.com/2011/05/checkboxes-and-booleans/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> </channel> </rss>
