<?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>Bill Erickson</title> <atom:link href="http://www.billerickson.net/feed/" rel="self" type="application/rss+xml" /><link>http://www.billerickson.net</link> <description>WordPress Consulting</description> <lastBuildDate>Fri, 10 May 2013 14:19:04 +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> <item><title>Displaying Popular Posts using BE-Stats</title><link>http://www.billerickson.net/displaying-popular-posts-using-be-stats/</link> <comments>http://www.billerickson.net/displaying-popular-posts-using-be-stats/#comments</comments> <pubDate>Fri, 22 Feb 2013 21:50:11 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Release]]></category> <category><![CDATA[plugin]]></category> <category><![CDATA[popular]]></category> <category><![CDATA[stats]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4518</guid> <description><![CDATA[<p>Use BE Stats to keep track of your most popular posts for display in your site.</p><p>The post <a
href="http://www.billerickson.net/displaying-popular-posts-using-be-stats/">Displaying Popular Posts using BE-Stats</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>Jetpack is a great plugin for collecting stats on your posts. But the only frontend display of that data they provide is the Top Pages and Posts widget. What if you want to filter that down to a more refined list?</p><p><a
href="http://www.wordpress.org/extend/plugins/be-stats">BE Stats</a> is the plugin for you. It asks for the 100 most popular posts over the past 30 days.  I filter this list down to just posts and then store its ranking as post meta (&#8216;be_stats&#8217;).</p><p>See it in use here: <a
href="http://www.billerickson.net/popular/">http://www.billerickson.net/popular/</a></p><p><strong><a
href="http://www.wordpress.org/extend/plugins/be-stats">Download BE-Stats from WordPress.rog</a></strong></p><p>You can then write custom queries using that metadata, or use the <a
href="http://www.wordpress.org/extend/plugins/display-posts-shortcode">Display Posts Shortcode</a> which makes it even easier.</p><p>Here&#8217;s some examples using the shortcode:</p><p><code>[display-posts orderby="popular"]</code><br
/> Lists the 10 most popular posts<br
/> <code>[display-posts orderby="popular" posts_per_page="4"]</code><br
/> Lists the 4 most popular posts<br
/> <code>[display-posts orderby="popular" posts_per_page="4" tag="basic"]</code><br
/> Lists the 4 most popular posts tagged &#8220;basic&#8221;</p><p>I developed this for a recent client project (still under development, will share link once it is live). The website is a news site that releases a lot of content. On the home and category archives they wanted to display recent posts and popular posts from that section.  Here&#8217;s what I placed where I want popular posts:</p> <script src="https://gist.github.com/5016718.js"></script><noscript><pre><code class="language-php php">&lt;?php
// Popular Posts
	$args = array(
		'meta_key' =&gt; 'be_stats',
		'orderby' =&gt; 'meta_value_num',
		'order' =&gt; 'ASC',
		'posts_per_page' =&gt; 3,
		'cat' =&gt; get_query_var( 'cat' ),
		'post__not_in' =&gt; $used_posts,
	);
	$loop = new WP_Query( $args );
	if( $loop-&gt;have_posts() ):
		echo '&lt;div class=&quot;popular-posts-listing&quot;&gt;';
		echo '&lt;h4&gt;Most Popular ' . $category-&gt;name . ' Posts&lt;/h4&gt;';
		while( $loop-&gt;have_posts() ): $loop-&gt;the_post();
			echo '&lt;div class=&quot;post&quot;&gt;';
			if( has_post_thumbnail() )
				echo '&lt;a href=&quot;' . get_permalink() . '&quot;&gt;' . be_get_post_thumbnail( null, 134, 134, true ) . '&lt;/a&gt;';
			echo '&lt;h2&gt;&lt;a href=&quot;' . get_permalink() . '&quot;&gt;' . get_the_title() . '&lt;/a&gt;&lt;/h2&gt;';
			echo wpautop( get_the_excerpt() . '&amp;hellip;' );
			echo '&lt;p class=&quot;more&quot;&gt;&lt;a href=&quot;' . get_permalink() . '&quot; class=&quot;square-button&quot;&gt;MORE&lt;/a&gt;&lt;/p&gt;';
			echo '&lt;/div&gt;';
		endwhile;
		echo '&lt;/div&gt;';
	endif;
	wp_reset_postdata();
</code></pre></noscript><p>There&#8217;s also some filters for you to customize how the plugin works.</p><p><code>be_stats_args</code> &#8211; Controls what arguments are passed to WordPress.com Stats API. Example:</p> <script src="https://gist.github.com/5005520.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Customize BE Stats Arguments
 * This will limit it to 7 days of pageviews (default is 30) and 20 posts (default is 100)
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/code/customize-be-stats-arguments
 *
 * @param array $args
 * @return array $args
 */
function be_customize_stats_args( $args ) {
	$args['days'] = 7;
	$args['limit'] = 20;
	return $args;
}
add_filter( 'be_stats_args', 'be_customize_stats_args' );</code></pre></noscript><p><code>be_stats_update</code> &#8211; Conditional for determining if stats data should be saved Examples:</p> <script src="https://gist.github.com/5005558.js"></script><noscript><pre><code class="language-php php">&lt;?php
// Save all stats data, don't limit it to just posts
add_filter( 'be_stats_update', '__return_true' );
// -- OR --
/**
 * Limit Stats to Events
 *
 */
function be_stats_for_events( $update, $id ) {
 return 'events' == get_post_type( $id );
}
add_filter( 'be_stats_update', 'be_stats_for_events', 10, 2 );</code></pre></noscript><p>Hopefully some other developers will find a use for this.</p><p>The post <a
href="http://www.billerickson.net/displaying-popular-posts-using-be-stats/">Displaying Popular Posts using BE-Stats</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/displaying-popular-posts-using-be-stats/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Any Business Questions?</title><link>http://www.billerickson.net/any-business-questions/</link> <comments>http://www.billerickson.net/any-business-questions/#comments</comments> <pubDate>Mon, 28 Jan 2013 21:52:31 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[General]]></category> <category><![CDATA[business]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4474</guid> <description><![CDATA[<p>I'd like to do more blog posts and conference talks on business/freelancing, but not sure exactly what I should talk about.</p><p>The post <a
href="http://www.billerickson.net/any-business-questions/">Any Business Questions?</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>At WordCamps and WordPress meetups, I come prepared to talk code but other developers seem more interested in my thoughts on business. It helps that I went to school as a finance major and have a few startup experiences under my belt.</p><p>I&#8217;d like to do more blog posts and conference talks on business/freelancing, but not sure exactly what I should talk about.</p><p>Do you have any topics or questions you&#8217;d like covered?</p><p>The post <a
href="http://www.billerickson.net/any-business-questions/">Any Business Questions?</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/any-business-questions/feed/</wfw:commentRss> <slash:comments>99</slash:comments> </item> <item><title>In Defense of Consulting Businesses</title><link>http://www.billerickson.net/in-defense-of-consulting-businesses/</link> <comments>http://www.billerickson.net/in-defense-of-consulting-businesses/#comments</comments> <pubDate>Wed, 19 Dec 2012 21:55:42 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[General]]></category> <category><![CDATA[business]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4462</guid> <description><![CDATA[<p>At conferences and meetings with other freelancers, a common desire I hear is to shift more of their revenue from "trade-time-for-money" services to "mailbox-money / scalable" products. As a successful service provider with no plans to shift business models, I'd like to explain my reasoning and start a discussion.</p><p>The post <a
href="http://www.billerickson.net/in-defense-of-consulting-businesses/">In Defense of Consulting Businesses</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>At conferences and meetings with other freelancers, a common desire I hear is to shift more of their revenue from &#8220;trade-time-for-money&#8221; services to &#8220;mailbox-money / scalable&#8221; products.</p><p>As a successful service provider with no plans to shift business models, I&#8217;d like to explain my reasoning and start a discussion. I&#8217;m not criticizing those who choose to make this shift &#8211; there&#8217;s many successful people I know who have, and I&#8217;ll highlight a few of them below. But I feel like everyone knows the reasons <em>for</em> making the shift and not many of the reasons <em>for not</em> making the shift.</p><h3>There&#8217;s more than two business models</h3><blockquote><p>You can never be rich trading your time for money. You need to shift to selling products, so increases in revenue do not result in increases in time spent working.</p></blockquote><p>Avoiding the obvious response to the first line (keep raising your rate until you ARE rich trading time for money), let&#8217;s focus on the second. Some people believe the decision is binary &#8211; you are a service provider or a product provider; small businesses are at one end, and startups are at the other.</p><p>I like to think of it as a spectrum. On the left you literally are trading time for money. For every X increase in revenue, it requires an equivalent increase in time. These are the people who work on an hourly basis. On the right, every X increase in revenue requires absolutely no increase in time (or other costs). This is the ideal, but is for the most part unobtainable. All businesses fall somewhere in the middle.</p><p><a
href="http://www.briangardner.com">Brian Gardner</a> has built a hugely successful business with <a
href="http://www.studiopress.com">StudioPress</a>. He builds a theme once and can sell it an unlimited amount of times without increasing the time spent developing it. BUT, each new customer does result in a marginal increase in his support costs. The theme development aspect of his business is perfectly scalable, but the support business is not (X number of customers results in Y number of support staff). StudioPress is on the right side of the spectrum, but not all the way.</p><p>I&#8217;m a WordPress developer that converts <a
href="http://www.billerickson.net/wordpress-consulting/psd-to-genesis">designs to themes</a>. Rather than working on an hourly basis, I charge on a per-project basis. While still on the left side of the spectrum, I&#8217;m able to lean towards the right for the following reasons:</p><ol><li>The more projects I work on, the faster I can develop a site through experience and <a
href="http://www.billerickson.net/code/">reusing code written earlier</a>. This increases my effective hourly rate.</li><li>The more projects I work on, the greater value I can provide to my clients through my experience. A site I built in 2012 is substantially better than the one I built in 2010 (even though that one was pretty good too <img
src='http://www.billerickson.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). This increases the rates I can charge, most evident through my project minimum. For the past three years, every January my minimum for a <a
href="http://www.billerickson.net/wordpress-consulting/psd-to-genesis">standard project</a> has gone up. But I personally believe the value I&#8217;m providing to my clients goes up even more than my rate has.</li></ol><p>For these reasons, even though I&#8217;m putting in about the same amount of time of work each year, my revenue for 2012 is much higher than 2011, and I expect the same for 2013.</p><p>As a service provider, find the more scalable aspects of your business and focus on them. Likewise, find the aspects that are less scalable and decrease your focus on them. Here&#8217;s a few examples.</p><p>I&#8217;m able to increase my efficiency at developing simple sites more than I can complex sites. Complex sites usually involve more site-specific features that you can&#8217;t leverage across projects. Clients can see a huge value in a site built at my $2,500 project minimum, but as the price increases the value I provide approaches a linear trend. The higher the project cost, the more of the project is non-reusable &#8220;time-for-money&#8221; work. For this reason, I try to focus on the smaller sites where I can deliver more value.</p><p>I can increase my efficiency at coding, but I have a hard time increasing my efficiency on the phone. An hour long call I have with a client two years ago will still take an hour today. For this reason, I try minimize phone calls and other one-on-one communication.</p><ul><li>When you land on my homepage, you&#8217;ll see the services I focus on. If you don&#8217;t want those, you&#8217;ll leave.</li><li>Once you&#8217;ve moved past that to my specific service pages or portfolio, you&#8217;ll see my project minimum. If that&#8217;s outside your budget, you&#8217;ll leave.</li><li>If you&#8217;re interested in starting a discussion, you&#8217;ll send an email using my contact form. I&#8217;ll read it, see if I have any canned responses that address the questions you have, and modify it as needed (a lot of the initial emails ask the same types of questions).</li><li>If you&#8217;re satisfied with our initial discussion, we&#8217;ll hop on the phone for 30 minutes to resolve any additional questions you have.</li></ul><p>This is much better than my old process, which was to post a phone number and let everyone who lands on my website call me.</p><h3>Products are an Investment</h3><p>When you say you want to build a product that provides recurring revenue, you&#8217;re actually saying you want to invest your time (and possibly other resources) to establish an income producing asset.</p><p>The two factors that are most important to an investment are often not considered by freelancers turning to product development: risk and return. People want the recurring revenue, but don&#8217;t extend the thought process out to how much revenue they want, how much time they&#8217;re willing to invest, and how likely they are to hit their target.</p><p><a
href="http://thomasgriffinmedia.com">Thomas Griffin</a> committed months of work to the development of <a
href="http://soliloquywp.com">Soliloquy</a>. It paid off, and he&#8217;s now able to scale back his consulting services as Soliloquy supplements the income.</p><p>But for every successful product released, there&#8217;s many that never made it this far, or made it to launch but didn&#8217;t generate enough revenue.</p><p>Before spending months or years developing your product, make sure there&#8217;s a healthy demand for it. Since you&#8217;re a freelancer already, maybe it&#8217;s something many of your clients have been wanting. You can decrease your risk by subsidizing the development cost through client work (a few clients hire you to develop this feature and you retain the rights to distribute it).</p><p>Make sure you can generate a reasonable return on your investment. If you can charge $200/hr for client work, and instead spend 1000 hours developing a plugin that sells for $20, make sure you can sell at least 10,000 of them. Also consider the <a
href="http://en.wikipedia.org/wiki/Time_value_of_money">time value of money</a>. Yes, you might sell 10,000 of them over 10 years, but a dollar earned 10 years from now is worth less than a dollar earned today. <a
href="http://blog.asmartbear.com/value-time.html">It&#8217;s also a riskier dollar</a>.</p><p>There are markets where you can trade your cash for income producing assets. Stocks, bonds, <a
href="http://www.lendingclub.com/public/steady-returns.action">individual loans</a>&#8230;. Building a product or scalable business is not the only way to have recurring revenue. These alternatives are less risky and don&#8217;t have a waiting period.</p><h3>Consider the Long-Term Costs</h3><p>Since WordPress is GPL, and all derivative, publicly distributed works are also GPL, many developers who sell themes and plugins are actually in the support business. You need to make sure your revenue per customer covers the lifetime cost of supporting that customer.</p><p>Let&#8217;s say you&#8217;re selling a theme for $50, and you&#8217;re getting 100 sales a month. You&#8217;re doing pretty good! At first the money is rolling in, and the support costs are low because you don&#8217;t have many users. Two years down the line, sales have dropped to a trickle, but now you have a customer base of 2000 that&#8217;s still requiring support.</p><p>Make sure that $50 you earned from the first customer can pay the cost to support that customer. Don&#8217;t build your business in a way that the current month&#8217;s sales pay the support costs of all the existing customers, and you need to keep growing sales in order to maintain your actual product: support.</p><p>Do as <a
href="http://www.gravityforms.com/purchase-gravity-forms/">Gravity Forms</a> does and limit your support to a defined period. Or, separate the cost of the scalable item (the product) and the non-scalable item (the support), and charge accordingly.</p><h3>In Summary</h3><p>Since even your consulting business has scalable, &#8220;startup-y&#8221; elements, it&#8217;s beneficial to think like a startup. This means <a
href="http://blog.asmartbear.com/value-time.html">your time is worth $1,000/hr</a>, and read everything else <a
href="http://blog.asmartbear.com/">Jason Cohen</a> has written.</p><p>Before changing your business strategy from consulting to products, consider if there&#8217;s ways you can improve your consulting business to include some of the benefits of a scalable business.</p><div
class="alert"><p>Enjoyed the article? Check out my WordCamp Austin talk on <a
href="http://wordpress.tv/2012/10/28/bill-erickson-managing-a-freelance-wordpress-development-business/">Managing a Freelance Business</a></p></div><p>The post <a
href="http://www.billerickson.net/in-defense-of-consulting-businesses/">In Defense of Consulting Businesses</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/in-defense-of-consulting-businesses/feed/</wfw:commentRss> <slash:comments>49</slash:comments> </item> <item><title>Building my Portfolio</title><link>http://www.billerickson.net/building-my-portfolio/</link> <comments>http://www.billerickson.net/building-my-portfolio/#comments</comments> <pubDate>Tue, 18 Dec 2012 21:20:00 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[genesis]]></category> <category><![CDATA[intermediate]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4460</guid> <description><![CDATA[<p>I walk you through how I built the portfolio section of my website.</p><p>The post <a
href="http://www.billerickson.net/building-my-portfolio/">Building my Portfolio</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>I received the following email from a developer yesterday.</p><blockquote><p> As a newcomer to the web world and genesis included, I&#8217;m attempting to learn from others and looking at great designs that inspire me. This brings me to my question. Would you be willing to share your experience or even go as far as sharing your code for your custom designed portfolio page here?</p></blockquote><p>After writing out my response, I figured others might be interested as well. Here&#8217;s the code, followed by a description of what each function does below.</p><p>First, the functions.php file:</p> <script src="https://gist.github.com/4332016.js?file=functions.php"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Functions
 *
 * @package      BE_Genesis_Child
 * @since        1.0.0
 * @link         https://github.com/billerickson/BE-Genesis-Child
 * @author       Bill Erickson &lt;bill@billerickson.net&gt;
 * @copyright    Copyright (c) 2011, Bill Erickson
 * @license      http://opensource.org/licenses/gpl-2.0.php GNU Public License
 *
 */
/**
 * Portfolio Query
 *
 * @param object $query
 * @return null
 */
function be_portfolio_query( $query ) {
  if( $query-&gt;is_main_query() &amp;&amp; !is_admin() &amp;&amp; ( is_post_type_archive( 'projects' ) || is_tax( 'type' ) ) ) {
		$query-&gt;set( 'posts_per_page', 18 );
		$query-&gt;set( 'orderby', 'menu_order' );
		$query-&gt;set( 'order', 'ASC' );
		$query-&gt;set( 'post_type', 'projects' );
	}
}
add_action( 'pre_get_posts', 'be_portfolio_query' );</code></pre></noscript><ul><li><code>be_portfolio_query()</code> customizes the query on the portfolio pages. This must go in functions.php since it needs to run before main query, which happens before the template is determined. <a
href="http://www.billerickson.net/customize-the-wordpress-query/">More information on customizing the query</a>.</li></ul><p>I specify the post type so the projects show up on the taxonomy archive pages AND so that the archive-projects.php file is used for the taxonomy archive pages. I could&#8217;ve also done this second part using the <a
href="http://www.billerickson.net/code/use-same-template-for-taxonomy-and-cpt-archive/">template_include filter</a>.</p> <script src="https://gist.github.com/4332016.js?file=archive-projects.php"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Portfolio Archive
 *
 * @package      BE_Genesis_Child
 * @since        1.0.0
 * @link         https://github.com/billerickson/BE-Genesis-Child
 * @author       Bill Erickson &lt;bill@billerickson.net&gt;
 * @copyright    Copyright (c) 2011, Bill Erickson
 * @license      http://opensource.org/licenses/gpl-2.0.php GNU Public License
 *
 */
/**
 * Portfolio Body Class
 *
 * @param array $classes
 * @return array
 */
function be_portfolio_body_class( $classes ) {
  $classes[] = 'portfolio-section';
  return $classes;
}
add_filter( 'body_class', 'be_portfolio_body_class' );
/**
 * Collect Testimonials
 *
 */
function be_collect_testimonials() {
	if( have_posts() ): while( have_posts() ): the_post();
		global $post, $be_testimonials;
			$testimonial = get_post_meta( $post-&gt;ID, 'be_project_testimonial', true );
			if( $testimonial ) {
				$testimonial = '&lt;div class=&quot;clearfix&quot;&gt;&lt;/div&gt;&lt;div class=&quot;testimonial&quot;&gt;' . wpautop( $testimonial );
				$name = get_post_meta( $post-&gt;ID, 'be_project_testimonial_name', true );
				if( !empty( $name ) )
					$testimonial .= '&lt;p class=&quot;name&quot;&gt;-- ' . $name . '&lt;/p&gt;';
				$testimonial .= '&lt;/div&gt;&lt;div class=&quot;clearfix&quot;&gt;&lt;/div&gt;';
				$be_testimonials[] = $testimonial;
			}
	endwhile; endif;
}
add_action( 'genesis_before_loop', 'be_collect_testimonials' );
/**
 * Sort Projects
 *
 */
function be_sort_projects() {
	echo '&lt;div class=&quot;sort-projects&quot;&gt;';
	echo '&lt;h3&gt;&lt;span&gt;Sort by project type:&lt;/span&gt;&lt;/h3&gt;';
	echo '&lt;ul&gt;';
	// All Projects
	$class = is_post_type_archive( 'projects' ) ? 'all-projects active' : 'all-projects';
	echo '&lt;li class=&quot;' . $class . '&quot;&gt;&lt;a href=&quot;' . get_post_type_archive_link( 'projects' ) . '&quot;&gt;All&lt;br /&gt;Projects&lt;/a&gt;&lt;/li&gt;';
	// Project Types
	$types = get_terms( 'type', array( 'orderby' =&gt; 'menu_order' ) );
	foreach ( $types as $type ) {
		$class = is_tax( 'type', $type-&gt;slug ) ? $type-&gt;slug . ' active' : $type-&gt;slug;
		echo '&lt;li class=&quot;' . $class . '&quot;&gt;&lt;a href=&quot;' . get_term_link( $type, 'type' ) . '&quot;&gt;' . $type-&gt;name . '&lt;/a&gt;&lt;/li&gt;';
	}
	echo '&lt;/ul&gt;&lt;/div&gt;';
}
add_action( 'genesis_before_loop', 'be_sort_projects' );
// No Post Info
remove_action( 'genesis_before_post_title', 'genesis_post_info' );
/**
 * Project Post Classes
 *
 * @param array $classes
 * @return array
 */
function be_project_post_classes( $classes ) {
	global $wp_query;
	$classes[] = 'one-third';
	if( 0 == $wp_query-&gt;current_post || 0 == $wp_query-&gt;current_post % 3 )
		$classes[] = 'first';
	return $classes;
}
add_filter( 'post_class', 'be_project_post_classes' );
/**
 * Outer Wrap
 *
 */
function be_outer_wrap() {
	echo '&lt;div class=&quot;outer-wrap&quot;&gt;';
}
add_action( 'genesis_before_loop', 'be_outer_wrap', 80 );
/**
 * Outer Wrap Close
 *
 */
function be_outer_wrap_close() {
	echo '&lt;/div&gt;';
}
add_action( 'genesis_after_loop', 'be_outer_wrap_close', 1 );
/**
 * Project Archive Content
 *
 */
function be_project_archive_content() {
	global $post;
	echo '&lt;div class=&quot;image&quot;&gt;&lt;a href=&quot;' . get_permalink() . '&quot;&gt;' . get_the_post_thumbnail( $post-&gt;ID, 'be_project_thumbnail' ) . '&lt;/a&gt;&lt;/div&gt;';
	$terms_output = '';
	$terms = get_the_terms( $post-&gt;ID, 'type' );
	foreach( $terms as $term )
		$terms_output .= '&lt;a class=&quot;icon-type ' . $term-&gt;slug . '&quot; href=&quot;' . get_term_link( $term, 'type' ) . '&quot;&gt;' . $term-&gt;name . '&lt;/a&gt; ';
	if( !empty( $terms_output ) )
		echo wpautop( $terms_output );
}
add_action( 'genesis_post_content', 'be_project_archive_content' );
/**
 * Project Divider
 *
 */
function be_project_divider() {
	global $wp_query;
	$count = $wp_query-&gt;current_post + 1;
	if( 0 == $count % 3 &amp;&amp; $count !== 6 &amp;&amp; $count !== $wp_query-&gt;post_count )
		echo '&lt;div class=&quot;divider&quot;&gt;&lt;/div&gt;';
}
add_action( 'genesis_after_post', 'be_project_divider' );
/**
 * Project Quote
 *
 */
function be_project_quote() {
	global $wp_query, $be_testimonials;
	if( !( ( 5 &lt; $wp_query-&gt;found_posts &amp;&amp; 5 == $wp_query-&gt;current_post ) || ( $wp_query-&gt;current_post ==  ( $wp_query-&gt;found_posts - 1 ) &amp;&amp; 5 &gt; $wp_query-&gt;found_posts ) ) )
		return;
	if( isset( $be_testimonials ) &amp;&amp; !empty( $be_testimonials ) ) {
		$testimonial = $be_testimonials[0];
		if( is_tax( 'type', 'mobile-design' ) )
			$testimonial = $be_testimonials[2];
		if( is_tax( 'type', 'design-to-website' ) )
			$testimonial = $be_testimonials[1];
		echo $testimonial;
	} else {
		echo '&lt;div class=&quot;divider&quot;&gt;&lt;/div&gt;';
	}
}
add_action( 'genesis_after_post', 'be_project_quote' );
genesis();</code></pre></noscript><ul><li><code>be_portfolio_body_class()</code> adds a consistent body class to all the pages using this template, for styling (if I didn&#8217;t use this, I&#8217;d have to write styles with body.post-type-archive-projects and body.tax-type )</li><li><code>be_collect_testimonials()</code>, I wanted to display a testimonial between certain projects, and wanted that testimonial to be from one of the projects featured on this page. So before the projects are listed, I loop through them all and pull out all the testimonials. I can then use this in a function lower down in the page</li><li><code>be_sort_projects()</code> adds the links before the project listing that lets you go from the main portfolio (all projects) to one of the taxonomy archives</li><li><code>be_project_post_classes()</code> &#8211; This looks at the loop counter and adds column classes to the posts, breaking it into multiple columns. <a
href="http://www.billerickson.net/a-better-and-easier-grid-loop/">See this tutorial</a> for more information.</li><li><code>be_outer_wrap()</code> and <code>be_outer_wrap_close()</code> add a wrapping div around all the posts and testimonials so they can be cleared with CSS</li><li><code>be_project_archive_content()</code> is the content of each project. It has the image and then displays the taxonomy terms (Project Type) below it</li><li><code>be_project_divider()</code> adds a dividing line after every 3 posts, except after the 6th one (where a testimonial is the divider)</li><li><code>be_project_quote()</code> adds a testimonial after the 6th project (it&#8217;s #5 since it starts counting at 0) OR after the last project if there are less than 6. I also have it choosing different testimonials for different taxonomies so the same testimonial isn&#8217;t used on every page</li></ul><p>The post <a
href="http://www.billerickson.net/building-my-portfolio/">Building my Portfolio</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/building-my-portfolio/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>Multiple Authors for a Post</title><link>http://www.billerickson.net/wordpress-post-multiple-authors/</link> <comments>http://www.billerickson.net/wordpress-post-multiple-authors/#comments</comments> <pubDate>Mon, 12 Nov 2012 10:38:19 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[genesis]]></category> <category><![CDATA[intermediate]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=2496</guid> <description><![CDATA[<p>If you have a magazine-like website, you might need to specify multiple authors for a single post.</p><p>The post <a
href="http://www.billerickson.net/wordpress-post-multiple-authors/">Multiple Authors for a Post</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<div
class="alert"><p>This article was originally published in January 2011.<br
/> I rewrote the article in November 2012 to take advantage of new features developed since then.</p></div><p>If you have a magazine-like website, you might need to specify multiple authors for a single post. There&#8217;s a great plugin called <a
href="http://wordpress.org/extend/plugins/co-authors-plus/">Co-Authors Plus</a> that will let you do this.</p><p>Once installed, you can use <a
href="http://vip.wordpress.com/documentation/incorporate-co-authors-plus-template-tags-into-your-theme/">many template tags</a>, or you can use the <code>get_coauthors()</code> function to retrieve all the author data and use it however you like.</p><h3>Authors Shortcode</h3><p>The Post Info area of Genesis can be <a
href="http://my.studiopress.com/snippets/post-info/#customize">customized using a filter</a>. We could drop the <code>coauthors_posts_links()</code> function in there to list authors and link to their archive pages, but it would be even better to create a shortcode to be used in the post info section. If that function isn&#8217;t available (ex: someone disabled the plugin), it will use the standard <code>get_author_posts_url()</code> instead.</p><p>We will create a shortcode called <code>[post_authors_post_link]</code> with the following parameters:</p><ul><li>between &#8211; what to display between authors, default is a comma</li><li>between_last &#8211; what to display between the last two authors, default is &#8220;and&#8221;</li><li>before &#8211; what to display before the list of authors, default is empty</li><li>after &#8211; what to display after the list of authors, default is empty</li></ul><p>To set up the shortcode, add the following to your functions.php file or <a
href="http://www.billerickson.net/core-functionality-plugin/">core functionality plugin</a>:</p> <script src="https://gist.github.com/4061770.js"></script><noscript><pre><code class="language-php php"> /**
 * Post Authors Post Link Shortcode
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/wordpress-post-multiple-authors/
 *
 * @param array $atts
 * @return string $authors
 */
function be_post_authors_post_link_shortcode( $atts ) {
	$atts = shortcode_atts( array(
		'between'      =&gt; null,
		'between_last' =&gt; null,
		'before'       =&gt; null,
		'after'        =&gt; null
	), $atts );
	$authors = function_exists( 'coauthors_posts_links' ) ? coauthors_posts_links( $atts['between'], $atts['between_last'], $atts['before'], $atts['after'], false ) : $atts['before'] . get_author_posts_url() . $atts['after'];
	return $authors;
}
add_shortcode( 'post_authors_post_link', 'be_post_authors_post_link_shortcode' );</code></pre></noscript><p>Now if we want to update the Genesis Post Info to display all post authors (with &#8220;by&#8221; right before the listing), we&#8217;d add this to functions.php:</p> <script src="https://gist.github.com/4061836.js"></script><noscript><pre><code class="language-php php">/**
 * List Authors in Genesis Post Info
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/wordpress-post-multiple-authors/
 *
 * @param string $info
 * @return string $info
 */
function be_post_info( $info ) {
	$info = '[post_authors_post_link before=&quot;by &quot;]';
	return $info;
}
add_filter( 'genesis_post_info', 'be_post_info' );</code></pre></noscript><h3>Author Boxes</h3><p>Genesis also has a feature called the Author Box that displays the author information at the bottom of the post. But this doesn&#8217;t use Co-Authors Plus, so if you have multiple authors only one will show up at the bottom.</p><p>First we&#8217;ll remove the Genesis Author Box. Then we&#8217;ll copy the code into our own function, but this one will accept an author ID field so we can render any author with it. Finally, we&#8217;ll create a function that checks to see if Co-Authors Plus is in use, and if it is, display author boxes for all authors; if it isn&#8217;t, display the author box for the normal post author.</p> <script src="https://gist.github.com/4061869.js"></script><noscript><pre><code class="language-php php">// Remove Genesis Author Box and load our own
remove_action( 'genesis_after_post', 'genesis_do_author_box_single' );
add_action( 'genesis_after_post', 'be_author_box' );
/**
 * Load Author Boxes
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/wordpress-post-multiple-authors/
 */
function be_author_box() {
	if( !is_single() )
		return;
	if( function_exists( 'get_coauthors' ) ) {
		$authors = get_coauthors();
		foreach( $authors as $author )
			be_do_author_box( $author-&gt;data-&gt;ID );
	} else {
		be_do_author_box( get_the_author_ID() );
	}
}
/**
 * Display Author Box
 * Modified from Genesis to use an ID
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/wordpress-post-multiple-authors/
 */
function be_do_author_box( $id = false ) {
	if( ! $id )
		return;
	$authordata    = get_userdata( $id );
	$gravatar_size = apply_filters( 'genesis_author_box_gravatar_size', 70, $context );
	$gravatar      = get_avatar( get_the_author_meta( 'email', $id ), $gravatar_size );
	$title         = apply_filters( 'genesis_author_box_title', sprintf( '&lt;strong&gt;%s %s&lt;/strong&gt;', __( 'About', 'genesis' ), get_the_author_meta( 'display_name', $id ) ), $context );
	$description   = wpautop( get_the_author_meta( 'description', $id ) );
	/** The author box markup, contextual */
	$pattern = $context == 'single' ? '&lt;div class=&quot;author-box&quot;&gt;&lt;div&gt;%s %s&lt;br /&gt;%s&lt;/div&gt;&lt;/div&gt;&lt;!-- end .authorbox--&gt;' : '&lt;div class=&quot;author-box&quot;&gt;%s&lt;h1&gt;%s&lt;/h1&gt;&lt;div&gt;%s&lt;/div&gt;&lt;/div&gt;&lt;!-- end .authorbox--&gt;';
	echo apply_filters( 'genesis_author_box', sprintf( $pattern, $gravatar, $title, $description ), $context, $pattern, $gravatar, $title, $description );
}</code></pre></noscript><p>The post <a
href="http://www.billerickson.net/wordpress-post-multiple-authors/">Multiple Authors for a Post</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/wordpress-post-multiple-authors/feed/</wfw:commentRss> <slash:comments>20</slash:comments> </item> <item><title>Overriding Options and Meta</title><link>http://www.billerickson.net/overriding-options-and-meta/</link> <comments>http://www.billerickson.net/overriding-options-and-meta/#comments</comments> <pubDate>Thu, 26 Jul 2012 18:56:41 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[advanced]]></category> <category><![CDATA[genesis]]></category> <category><![CDATA[meta]]></category> <category><![CDATA[options]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4300</guid> <description><![CDATA[<p>One of my favorite “under-the-hood” features of WordPress and Genesis is the ability to override almost any kind of option/meta: site options, post meta, user meta, term meta….</p><p>The post <a
href="http://www.billerickson.net/overriding-options-and-meta/">Overriding Options and Meta</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p
style="text-align:center; font-size: 10px;">Photo by <a
href="http://www.flickr.com/photos/900hp/4017949487/">900hp</a></p><p>One of my favorite “under-the-hood” features of WordPress and Genesis is the ability to override almost any kind of option/meta: site options, post meta, user meta, term meta….</p><p>Make sure you are careful with these filters though. Themes and plugins will expect certain options/meta to be returned when they call a core function, and if you&#8217;re not careful you could break them. Make sure you only return your changes in the very specific instances in which you want them.</p><p>Here’s a few common use cases:</p><ol><li>Change the admin email on certain pages (site option)</li><li>Change the default layout of the homepage (site option)</li><li>Force a layout on the homepage (post meta)</li><li>Turn the Genesis Author Box on automatically for author archives (user meta)</li><li>Specify a default title for term archives (term meta)</li></ol><h3>Site Options</h3><p>Site options are anything stored in the wp_options database, and you typically access them using the <code>get_option()</code> function.</p><p>WordPress provides a filter for these options, called <code>pre_option_($key)</code> <em>(see wp-includes/option.php, line 36 in WP 3.4.1)</em>.</p><p><strong>Example 1: Change the admin email on certain pages</strong></p><p>As a simple example, let’s say you had an email link in your footer, and you wanted to modify the ‘admin_email’ option on blog posts to use the blog post’s author email (I probably wouldn’t use this approach, but it shows you how to use the filter).</p> <script src="https://gist.github.com/3183253.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Add &quot;Email Us&quot; link to footer
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 */
function be_email_us() {
	echo '&lt;p&gt;&lt;a href=&quot;mailto:' . get_option( 'admin_email' ) . '&quot;&gt;Email Us&lt;/a&gt;&lt;/p&gt;';
}
add_action( 'wp_footer', 'be_email_us' );
/**
 * Modify Author Email option on Single Posts
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string $email
 * @return string
 */
function be_post_author_email( $email ) {
	global $post;
	if( is_single() )
		$email = get_the_author_meta( 'user_email', $post-&gt;post_author );
	return $email;
}
add_filter( 'pre_option_admin_email', 'be_post_author_email' );
</code></pre></noscript><p>Genesis has a lot of options, but instead of storing them all as individual entries in the database, it stores them as a single serialized array. This is great for performance, but it makes using the above filter difficult. So Genesis provides a filter for Genesis options and any other options that use the <a
href="http://www.billerickson.net/admin-pages-with-genesis/">Genesis Settings API</a>.</p><p><strong>Example 2: Change the default layout of the homepage</strong></p><p>You can set the site-wide default layout in Genesis > Theme Settings, which sets the Genesis option &#8216;site_layout&#8217;. But on the homepage you might want to have a different default layout. We&#8217;ll set this using the <code>genesis_pre_get_option_($key)</code> filter <em>(see lib/functions/options, line 57 in Genesis 1.8.2)</em>.</p> <script src="https://gist.github.com/3183357.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Use Full Width Content on Homepage
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string $layout
 * @return string
 */
function be_home_layout( $layout ) {
	return 'full-width-content';
}
add_filter( 'genesis_pre_get_option_site_layout', 'be_home_layout' );
</code></pre></noscript><p>Since this is such a popular use, Genesis has already made functions that return all the built-in layouts, and you can see a list of them at the end of this post. We can replace the code above with this:</p> <script src="https://gist.github.com/3183365.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Use Full Width Content on Homepage
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string $layout
 * @return string
 */
add_filter( 'genesis_pre_get_option_site_layout', '__genesis_return_full_width_content' );</code></pre></noscript><p>Please note that both the above code snippets are to be placed in front-page.php or home.php &#8212; whichever template file is responsible for your homepage.</p><h3>Post Meta</h3><p>WordPress provides a filter called <code>get_post_metadata</code> <em>(see wp-includes/meta.php, line 274 in WP 3.4.1)</em> for modifying post meta.</p><p><strong>Example 3: Force a layout on the homepage</strong></p><p>The Genesis function that determines the page layout first checks the post&#8217;s meta field &#8216;_genesis_layout&#8217;, and if none is specified it uses the sitewide default (which is what we overwrote in the previous example). The code below overrides the post meta, so the user can&#8217;t change the layout on this page:</p> <script src="https://gist.github.com/3183557.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Force Full Width Layout
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string/array $meta_value
 * @param int $post_id
 * @param string $meta_key
 * @param bool $single
 * @return string/array $meta_value
 */
function be_home_full_layout( $meta_value, $post_id, $meta_key, $single ) {
	if( '_genesis_layout' == $meta_key )
		$meta_value = 'full-width-content';
	return $meta_value;
}
add_filter( 'get_post_metadata', 'be_home_full_layout', 10, 4 );
</code></pre></noscript><p>Again, this code is running in front-page.php or home.php &#8212; whatever is responsible for your homepage. Since the filter passes the $post_id, we could also run this in functions.php like so:</p> <script src="https://gist.github.com/3183581.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Force Full Width Layout
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string/array $meta_value
 * @param int $post_id
 * @param string $meta_key
 * @param bool $single
 * @return string/array $meta_value
 */
function be_home_full_layout( $meta_value, $post_id, $meta_key, $single ) {
	if( get_option( 'page_on_front' ) == $post_id &amp;&amp; '_genesis_layout' == $meta_key )
		$meta_value = 'full-width-content';
	return $meta_value;
}
add_filter( 'get_post_metadata', 'be_home_full_layout', 10, 4 );</code></pre></noscript><p>We&#8217;re now making sure the $meta_key matches the one we want AND we&#8217;re making sure $post_id is the page set to the front page in General > Reading. If you wanted this on your blog page instead, you could check $post_id against <code>get_option( 'page_for_posts' )</code>.</p><h3>User Meta</h3><p>WordPress provides a filter called <code>get_the_author_($key)</code> <em>(see wp-includes/author-template.php, line 112 in WP 3.4.1)</em> for modifying author meta. You can use this to override any of the <a
href="http://codex.wordpress.org/Function_Reference/get_the_author_meta">author meta from WP Core</a>, and also any author meta added by themes and plugins.</p><p><strong>Example 4: Turn the Genesis Author Box on automatically for author archives</strong></p><p>If you have the Genesis theme installed and go to Users > Your Profile, there&#8217;s a checkbox that lets you specify if an author box should show up on your author profile and/or at the bottom of all your posts. These update the user meta fields genesis_author_box_archive and genesis_author_box_single, respectively.</p><p>This code will make genesis_author_box_archive always return true, so the author box it automatically shows up on all author archive pages:</p> <script src="https://gist.github.com/3183622.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Display Author Box on all author archives
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param bool $option
 * @return bool
 */
add_filter( 'get_the_author_genesis_author_box_archive', '__return_true' );</code></pre></noscript><p>You&#8217;ll notice I&#8217;m using a function called <code>__return_true()</code>, but that I didn&#8217;t create that function. This is one of the WordPress core helper functions. See the end of this post for a list of more helper functions.</p><h3>Term Meta</h3><p><a
href="http://core.trac.wordpress.org/ticket/10142">WordPress doesn&#8217;t have term meta</a>, which is one of the few things it is lacking as a CMS. Genesis needed term meta for all its SEO and layout options, so it created term meta using the options table.</p><p>Genesis also added a filter, <code>genesis_term_meta_($key)</code> <em>(see lib/functions/options.php, line 228 in Genesis 1.8.2)</em>, used for modifying this meta.</p><p><strong>Example 5: Specify a default title for term archives </strong></p><p>On my photography blog, I have a taxonomy called &#8220;people&#8221; that I use for tagging people in photos. For taxonomy archives, Genesis has term meta for an Archive Headline and Description. I wanted to display a headline at the top of all the term archive pages, but didn&#8217;t want to manually create them.</p><p>Here&#8217;s a simple function I wrote that first checks to see if a headline has been provided, and if it hasn&#8217;t it uses &#8220;Photos of [person's name]&#8220;.</p> <script src="https://gist.github.com/2040961.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Person Intro
 * Displays &quot;Photos of .. &quot; name on term archive of people taxonomy
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/overriding-options-and-meta
 *
 * @param string $headline
 * @param object $term
 * @return string $headline
 */
function be_person_intro( $headline, $term ) {
	if( !is_tax( 'people' ) || !empty( $headline ) )
		return $headline;
	return 'Photos of ' . $term-&gt;name;
}
add_filter( 'genesis_term_meta_headline', 'be_person_intro', 10, 2 );</code></pre></noscript><p>Relevant filters:</p><ul><li>WP: pre_option_($key) in wp-includes/option.php, line 36</li><li>WP: get_the_author_($key) in wp-includes/author-template.php, line 112</li><li>WP: get_post_metadata in wp-includes/meta.php, line 274</li><li>Genesis: genesis_pre_get_option_($key) in lib/functions/options.php, line 57</li><li>Genesis: genesis_term_meta_($key) in lib/functions/options.php, line 228</li></ul><p>Helper Functions:</p><ul><li>WP: <code>__return_true()</code> – returns the Boolean state of true</li><li>WP: <code>__return_false()</code> – returns the Boolean state of false</li><li>WP: <code>__return_zero()</code> – returns a value of zero (0)</li><li>WP: <code>__return_empty_array()</code> – returns an empty array, as in array()</li><li>WP: <code>__return_null()</code> – returns null (or void)</li><li>Genesis: <code>__genesis_return_full_width_content()</code> &#8211; returns &#8216;full-width-content&#8217;</li><li>Genesis: <code>__genesis_return_content_sidebar()</code> &#8211; returns &#8216;content-sidebar&#8217;</li><li>Genesis: <code>__genesis_return_sidebar_content()</code> &#8211; returns &#8216;sidebar-content&#8217;</li><li>Genesis: <code>__genesis_return_content_sidebar_sidebar()</code> &#8211; returns &#8216;content-sidebar-sidebar&#8217;</li><li>Genesis: <code>__genesis_return_sidebar_content_sidebar()</code> &#8211; returns &#8216;sidebar-content-sidebar&#8217;</li><li>Genesis: <code>__genesis_return_sidebar_sidebar_content()</code> &#8211; returns &#8216;sidebar-sidebar-content&#8217;</li></ul><p>The post <a
href="http://www.billerickson.net/overriding-options-and-meta/">Overriding Options and Meta</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/overriding-options-and-meta/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Custom Secondary Menu</title><link>http://www.billerickson.net/custom-secondary-menu/</link> <comments>http://www.billerickson.net/custom-secondary-menu/#comments</comments> <pubDate>Tue, 19 Jun 2012 14:59:31 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[intermediate]]></category> <category><![CDATA[menu]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4291</guid> <description><![CDATA[<p>Custom Menus are great for sitewide menus, but what if you only want to list a section's subpages? I'll walk you through pulling the relevant subpages from your custom menu.</p><p>The post <a
href="http://www.billerickson.net/custom-secondary-menu/">Custom Secondary Menu</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>Custom Menus are great for sitewide menus, but what if you only want to list a section&#8217;s subpages?</p><p>In the screenshot above, you&#8217;ll see the Plays dropdown lists three subpages. And on the right side, those same subpages are listed because we&#8217;re currently in the Plays section.</p><p>There&#8217;s a few popular approaches:</p><ul><li>Use wp_list_pages() to dynamically list all subpages to the current section. The downside here is you can&#8217;t easily hide items from this list, and you have to keep the page order in sync with the custom menu&#8217;s page order (so they appear in the same order). You also can&#8217;t provide custom lables for menu items. In the above example, &#8220;Current Season&#8221; is really a page called &#8220;2011-2012 Season&#8221;, and they&#8217;ll have older seasons they do not want displayed in this menu.</li><li>Create a custom menu for each section. It&#8217;s a lot of work maintaining all these menus, and the end user can&#8217;t easily add a new section to the site since someone will need to create a menu for that section.</li></ul><p>Assuming your main menu has all your site&#8217;s sections and their desired subpages, we can use <code>wp_get_nav_menu_items()</code> to pull only the relevant subpages.</p> <script src="https://gist.github.com/2954640.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Section Menu
 * Displays the subpages of the current section
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/custom-secondary-menu
 */
function be_section_menu() {
	// Only run on pages
	if( !is_page() )
		return;
	// If top level page, use current ID; else use highest ancestor
	global $post;
	$section_id = empty( $post-&gt;ancestors ) ? $post-&gt;ID : end( $post-&gt;ancestors );
	// Get all the menu locations
	$locations = get_nav_menu_locations();
	// Find out which menu is in the 'primary' location
	$menu = wp_get_nav_menu_object( $locations[ 'primary' ] );
	// Grab all menu items in this menu that have a parent of the current section.
	// This grabs the subpages, assuming the current section is a top level page
	$menu_items = wp_get_nav_menu_items( $menu-&gt;term_id, array( 'post_parent' =&gt; $section_id ) );
	// If there are menu items, build the menu
	if( !empty( $menu_items ) ) {
		echo '&lt;ul class=&quot;section-submenu&quot;&gt;';
		$first = true;
		foreach( $menu_items as $menu_item ) {
			$classes = 'page-item';
			// This adds a class to the first item so I can style it differently
			if( $first )
				$classes .= ' first-menu-item';
			$first = false;
			// This marks the current menu item
			if( get_the_ID() == $menu_item-&gt;object_id )
				$classes .= ' current_page_item';
			echo '&lt;li class=&quot;' . $classes . '&quot;&gt;&lt;a href=&quot;' . $menu_item-&gt;url . '&quot;&gt;' . $menu_item-&gt;title . '&lt;/a&gt;&lt;/li&gt;';
		}
		echo '&lt;/ul&gt;';
	}
}
add_action( 'genesis_before_loop', 'be_section_menu' );</code></pre></noscript><p>The post <a
href="http://www.billerickson.net/custom-secondary-menu/">Custom Secondary Menu</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/custom-secondary-menu/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>Migrating WordPress Websites</title><link>http://www.billerickson.net/migrating-wordpress-websites/</link> <comments>http://www.billerickson.net/migrating-wordpress-websites/#comments</comments> <pubDate>Wed, 23 May 2012 20:29:19 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[intermediate]]></category> <category><![CDATA[migration]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4277</guid> <description><![CDATA[<p>A step-by-step guide to migrating WordPress websites.</p><p>The post <a
href="http://www.billerickson.net/migrating-wordpress-websites/">Migrating WordPress Websites</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>I wrote an article two years ago on <a
href="http://www.billerickson.net/how-to-move-your-wordpress-website/">how to move your WordPress website</a>, which has been one of my most popular posts.</p><p>Over the past two years I&#8217;ve found some improvements to my process, most notably around serialized arrays.</p><h3>Before Migrating, Custom Table Prefix</h3><p>When building a new website, make sure to use a custom table prefix (you define this in wp-config.php). If you started with an existing database, use <a
href="http://tdot-blog.com/wordpress/6-simple-steps-to-change-your-table-prefix-in-wordpress">this process</a> to change the prefix.</p><p>I like to make mine <code>[clientname][date]_</code>. So if my client was Automattic and I built the website today, the prefix would be <code>automattic05232012_</code>.</p><p>This is so that there&#8217;s no risk you upload your tables to a database with the same named tables. When working on an existing site, it&#8217;s important to have both sets of tables in the database so that if there&#8217;s any issues with the migration, you can quickly switch back to the old site while you figure out the issue.</p><h3>Migration Process</h3><ol><li>Backup the database tables. I like to use <a
href="http://www.sequelpro.com/">Sequel Pro</a> to connect to my database and export them, but you can also use the <a
href="http://wordpress.org/extend/plugins/adminer/">Adminer</a> WordPress plugin or phpMyAdmin through your server&#8217;s cPanel. The default export will the a .sql file, but you might want to use one of the compressed formats. Many hosts only allow you a small file upload (like 2MB), and compressing it can help you stay under that limit.</li><li>Backup the wp-content directory. This can be as easy as FTP&#8217;ing onto your server and dragging a copy of wp-content/ to your computer. If you have SSH access and are comfortable with command line, I like to make a tarball because it is much faster to download than a lot of small files. Use a command like this: <code>tar -pvczf wp-content.tar.gz wp-content/</code>.</li><li>Upload the database to the new server. If WordPress is already installed, use the Adminer plugin to add the database. If not, you might use phpMyAdmin provided by the host.</li><li>Upload <a
href="http://interconnectit.com/124/search-and-replace-for-wordpress-databases/">searchreplacedb2.php</a> to the top level of your server (wherever wp-config.php is). This is used to find all the uses of the old domain and replace them with the new domain. The reason we&#8217;re using this tool instead of simply doing it in the database is that it will work with serialized arrays. Click the above link to read more about the problem and solution. If you&#8217;re really worried about security, do this locally or on your development server so that this file never has to be on the production server.</li><li>Load the the search and replace tool at http://www.yoursite.com/searchreplacedb2.php. It will auto-populate the database information from wp-config.php. Click &#8220;Next&#8221;, select the applicable tables, then on the find/replace screen type the old and new domain. Make sure you do not include a trailing slash (correct = http://www.yoursite.com | incorrect = http://www.yoursite.com/). Do the find and replace, then remove this file from your server.</li><li>If a brand new website, upload copy of WordPress along with your custom wp-content directory. If there&#8217;s already a WordPress website live, follow the rest of the steps below.</li><li>On your computer, unpackage the wp-content tarball (if you tarballed it), and then rename the folder wp-content.new. The name doesn&#8217;t matter, it just has to be different than wp-content. Upload the folder to the same directory that has the old wp-content/ .</li><li>Open wp-config.php. Find the line that starts with $table_prefix. Comment it out, and create a new $table_prefix line with your custom table prefix. <a
href="https://gist.github.com/05cb807ce392056b7a6d">It will look something like this</a>.</li><li>Save wp-config.php, and then immediately rename wp-content/ to wp-content.old/, and wp-content.new to wp-content/</li><li>Check the website&#8217;s homepage. If anything looks wrong, switch to the old site while you figure it out. Go into wp-config.php, comment out the new table prefix and remove the commenting on the old table prefix. Rename the wp-content directories back to how they were.</li><li>If the site is working, log in and go to Settings &gt; Permalinks, then click Save. This will update the permalink structure and ensure all URLs work.</li><li>Go to Settings &gt; Privacy and make sure you have the site visible to search engines. I use the <a
href="http://wordpress.org/extend/plugins/wordpress-seo/">WordPress SEO</a> plugin which has a big red nag on the backend if you&#8217;re privacy settings aren&#8217;t set correctly. Since I don&#8217;t want that nag showing up on my dev environment, I include <a
href="https://github.com/billerickson/Core-Functionality/blob/master/lib/functions/general.php#L159">this snippet</a> in my functionality plugin (line 150-159) to hide it under certain conditions (localhost or master-wp in the domain, since those are where I develop).</li><li>On the old site, install the <a
href="http://wordpress.org/extend/plugins/migration-notice/">Migration Notice</a> plugin. I&#8217;ve had a lot of clients get confused about which backend they are supposed to post new content to, so if you aren&#8217;t deleting the dev environment immediately this prevents that issue.</li><li>Make sure you&#8217;ve removed searchreplacedb2.php. This was mentioned in #4, but deserves reiterating (thanks WPExplorer).</li><li>Update file permissions of wp-content directory. If you&#8217;re using <a
href="http://www.billerickson.net/go/wp-engine">WPEngine</a>, they&#8217;ve added a super useful button &#8220;Reset File Permissions&#8221; in their WPEngine page of your WordPress backend.</li></ol><p>The post <a
href="http://www.billerickson.net/migrating-wordpress-websites/">Migrating WordPress Websites</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/migrating-wordpress-websites/feed/</wfw:commentRss> <slash:comments>46</slash:comments> </item> <item><title>A better, and easier, grid loop</title><link>http://www.billerickson.net/a-better-and-easier-grid-loop/</link> <comments>http://www.billerickson.net/a-better-and-easier-grid-loop/#comments</comments> <pubDate>Thu, 15 Mar 2012 18:26:58 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[genesis]]></category> <category><![CDATA[gridloop]]></category> <category><![CDATA[intermediate]]></category> <category><![CDATA[wordpress]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4242</guid> <description><![CDATA[<p>Use the post_class filter to easily build a multi-column listing of posts.</p><p>The post <a
href="http://www.billerickson.net/a-better-and-easier-grid-loop/">A better, and easier, grid loop</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<div
class="alert"><p>Don&#8217;t feel like coding? I built a plugin that does this for you.<br
/> See the <a
href="http://wordpress.org/extend/plugins/genesis-grid-loop/">Genesis Grid Loop</a> plugin.</p></div><p><em>Note: This technique can be used by all WordPress themes. I&#8217;m proposing it as a replacement for the Genesis-specific grid loop, so aspects of this post might be Genesis-specific.</em></p><p>A popular request is to list posts in multiple columns. I do it on <a
href="http://www.billerickson.net/blog/">my blog</a>, and do it often on my clients&#8217; sites.</p><p>Genesis developed a Grid Loop, which you can utilize inside your Genesis themes for this effect, and is how this blog does it. While very useful, it can be difficult to set up. I believe this is because they combined two separate functions: what content to display (your query) and how to display it.</p><p>By breaking those two functions we can use another feature of Genesis, the <a
href="http://www.studiopress.com/tutorials/content-column-classes">column classes</a>, to build grid loops easier. You can also copy that CSS to any WordPress theme and make it work too.</p><p>For this example I&#8217;ll be using a <a
href="https://github.com/billerickson/BE-Gallery">Gallery theme</a> I recently built. On the archive pages, it displays posts in three columns. See the screenshot above, or <a
href="http://photography.billerickson.net/sets/all/">click here</a>) for an example.</p><h3>Step 1: Multiple columns using <code>post_class</code></h3><p>The <code>post_class</code> filter lets us customize the classes applied to each post in the loop. Since I want this to only apply to archive pages, I&#8217;m placing it in archive.php.</p> <script src="https://gist.github.com/2040968.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Archive Post Class
 * @since 1.0.0
 *
 * Breaks the posts into three columns
 * @link http://www.billerickson.net/code/grid-loop-using-post-class
 *
 * @param array $classes
 * @return array
 */
function be_archive_post_class( $classes ) {
	$classes[] = 'one-third';
	global $wp_query;
	if( 0 == $wp_query-&gt;current_post || 0 == $wp_query-&gt;current_post % 3 )
		$classes[] = 'first';
	return $classes;
}
add_filter( 'post_class', 'be_archive_post_class' );</code></pre></noscript><p>The first line adds a class of &#8216;one-third&#8217; to all posts. Then I grab the current post counter out of <code>$wp_query</code>, and if this is the first post ( <code>0 == $wp_query-&gt;current_post</code> ) or if the remainder of the current post divided by 3 is zero (this tells us the current post is the first in a row), apply a class of &#8220;first&#8221; as well.</p><p>That&#8217;s it! You now have your content broken into multiple columns. If you want two columns, use &#8216;one-half&#8217; and divide the current post by 2. If you want four columns, use &#8216;one-fourth&#8217; and divide the current post by 4.</p><h3>Step 2: Customize the Query</h3><p>When I view the archive page now, it&#8217;s in three columns but it&#8217;s only displaying 10 posts. The last post sits by itself in its own row. I&#8217;m going to modify the main query to show 27 posts per page. You could use any number you want, just make sure it&#8217;s a multiple of columns. For more information on customizing the main query, <a
href="http://www.billerickson.net/customize-the-wordpress-query/">see this post</a>.</p> <script src="https://gist.github.com/2045807.js"></script><noscript><pre><code class="language-php php">&lt;?php
add_filter( 'pre_get_posts', 'be_archive_query' );
/**
 * Archive Query
 *
 * Sets all archives to 27 per page
 * @since 1.0.0
 * @link http://www.billerickson.net/customize-the-wordpress-query/
 *
 * @param object $query
 */
function be_archive_query( $query ) {
	if( $query-&gt;is_main_query() &amp;&amp; $query-&gt;is_archive() ) {
		$query-&gt;set( 'posts_per_page', 27 );
	}
}</code></pre></noscript><p>This code must go in functions.php, since the main query runs before it reaches archive.php (it checks the query to figure out what template to load).</p><h3>Even easier sitewide</h3><p>If you&#8217;re using this for all listings of posts (home, archive, search&#8230;), it is even easier to set up. Put the post_class filter in functions.php so it runs sitewide, and add a conditional to check if it isn&#8217;t singular:</p> <script src="https://gist.github.com/2046751.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Archive Post Class
 * @since 1.0.0
 *
 * Breaks the posts into three columns
 * @link http://www.billerickson.net/code/grid-loop-using-post-class
 *
 * @param array $classes
 * @return array
 */
function be_archive_post_class( $classes ) {
	// Don't run on single posts or pages
	if( is_singular() )
		return $classes;
	$classes[] = 'one-third';
	global $wp_query;
	if( 0 == $wp_query-&gt;current_post || 0 == $wp_query-&gt;current_post % 3 )
		$classes[] = 'first';
	return $classes;
}
add_filter( 'post_class', 'be_archive_post_class' );</code></pre></noscript><p>And instead of the function to customize the number of posts, go to Settings &gt; Reading and tweak it there.</p><h3>Advanced Example</h3><p>The code snippet below (added to functions.php), modifies the blog&#8217;s homepage and archive pages to display 5 features and 6 teasers (in three columns) on the first page. On inner pages, it displays 0 features and 12 teasers (in three columns). It also updates the post image to use image sizes specifically created for features and teasers.</p><p>The first function, <code>be_grid_loop_pagination()</code>, is where we control the grid loop. Under the comment that says &#8220;Sections of site that should use grid loop&#8221;, you can modify that list to specify where you want the grid loop displayed. Right now it is running when <code>is_home()</code> or <code>is_archive()</code> is true. The second part, under the comment that says &#8220;Specify pagination&#8221;, is where you specify how many features and teasers to show on the homepage and subsequent pages.</p><p>The second function, <code>be_grid_loop_query_args()</code>, doesn&#8217;t require any customization from you. It uses the pagination information you added to the previous function to tell WordPress how many posts show up on each page.</p><p>The third function, <code>be_grid_loop_post_classes()</code>, applies relevant classes to each post. It&#8217;s adding a class of &#8216;feature&#8217; to each feature post, and <a
href="http://www.studiopress.com/tutorials/content-column-classes">column classes</a> to the teasers. The only thing that you&#8217;d need to change is the teasers sections if you want something other than three columns. Here&#8217;s what you&#8217;d need to change if you wanted <a
href="http://diffchecker.com/5zCso759">two columns</a>.</p><p>The fourth function, <code>be_grid_image_sizes()</code>, specifies the two image sizes. Change these to whatever size you&#8217;d like. If you don&#8217;t want images, you can leave out this function and the last one, <code>be_grid_loop_image()</code>. Also note that you need to have &#8220;Include Featured Image&#8221; checked in Genesis &gt; Theme Settings &gt; Content Archives.</p><p>The fifth function, <code>be_grid_loop_image()</code>, overrides the image size set in Genesis &gt; Theme Settings &gt; Content Archives with the image sizes we created in the previous function. No changes need to be made to this function.</p><p>The sixth function, <code>be_fix_posts_nav()</code> does as its name implies. The post navigation (Older/Newer, numerical links to posts pages&#8230;) uses <code>$wp_query->max_num_pages</code> to know how many pages there are, and this is based on the current page&#8217;s posts_per_page. So if you have less posts on your homepage than inner pages, the post navigation on the homepage will be off (this is noticeable if you&#8217;re using numerical links). This code changes the max_num_pages based on the grid args.</p> <script src="https://gist.github.com/3530096.js"></script><noscript><pre><code class="language-php php">&lt;?php
/**
 * Grid Loop Pagination
 * Returns false if not grid loop.
 * Returns an array describing pagination if is grid loop
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/a-better-and-easier-grid-loop/
 *
 * @param object $query
 * @return bool is grid loop (true) or not (false)
 */
function be_grid_loop_pagination( $query = false ) {
	// If no query is specified, grab the main query
	global $wp_query;
	if( !isset( $query ) || empty( $query ) || !is_object( $query ) )
		$query = $wp_query;
	// Sections of site that should use grid loop
	if( ! ( $query-&gt;is_home() || $query-&gt;is_archive() ) )
		return false;
	// Specify pagination
	return array(
		'features_on_front' =&gt; 5,
		'teasers_on_front' =&gt; 6,
		'features_inside' =&gt; 0,
		'teasers_inside' =&gt; 12,
	);
}
/**
 * Grid Loop Query Arguments
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/a-better-and-easier-grid-loop/
 *
 * @param object $query
 * @return null
 */
function be_grid_loop_query_args( $query ) {
	$grid_args = be_grid_loop_pagination( $query );
	if( $query-&gt;is_main_query() &amp;&amp; !is_admin() &amp;&amp; $grid_args ) {
		// First Page
		$page = $query-&gt;query_vars['paged'];
		if( ! $page ) {
			$query-&gt;set( 'posts_per_page', ( $grid_args['features_on_front'] + $grid_args['teasers_on_front'] ) );
		// Other Pages
		} else {
			$query-&gt;set( 'posts_per_page', ( $grid_args['features_inside'] + $grid_args['teasers_inside'] ) );
			$query-&gt;set( 'offset', ( $grid_args['features_on_front'] + $grid_args['teasers_on_front'] ) + ( $grid_args['features_inside'] + $grid_args['teasers_inside'] ) * ( $page - 2 ) );
			// Offset is posts on first page + posts on internal pages * ( current page - 2 )
		}
	}
}
add_action( 'pre_get_posts', 'be_grid_loop_query_args' );
/**
 * Grid Loop Post Classes
 *
 * @author Bill Erickson
 * @link http://www.billerickson.net/a-better-and-easier-grid-loop/
 *
 * @param array $classes
 * @return array $classes
 */
function be_grid_loop_post_classes( $classes ) {
	global $wp_query;
	$grid_args = be_grid_loop_pagination();
	if( ! $grid_args )
		return $classes;
	// First Page Classes
	if( ! $wp_query-&gt;query_vars['paged'] ) {
		// Features
		if( $wp_query-&gt;current_post &lt; $grid_args['features_on_front'] ) {
			$classes[] = 'feature';
		// Teasers
		} else {
			$classes[] = 'one-third';
			if( 0 == ( $wp_query-&gt;current_post - $grid_args['features_on_front'] ) || 0 == ( $wp_query-&gt;current_post - $grid_args['features_on_front'] ) % 3 )
				$classes[] = 'first';
		}
	// Inner Pages
	} else {
		// Features
		if( $wp_query-&gt;current_post &lt; $grid_args['features_inside'] ) {
			$classes[] = 'feature';
		// Teasers
		} else {
			$classes[] = 'one-third';
			if( 0 == ( $wp_query-&gt;current_post - $grid_args['features_inside'] ) || 0 == ( $wp_query-&gt;current_post - $grid_args['features_inside'] ) % 3 )
				$classes[] = 'first';
		}
	}
	return $classes;
}
add_filter( 'post_class', 'be_grid_loop_post_classes' );
/**
 * Grid Image Sizes
 *
 */
function be_grid_image_sizes() {
	add_image_size( 'be_grid', 175, 120, true );
	add_image_size( 'be_feature', 570, 333, true );
}
add_action( 'genesis_setup', 'be_grid_image_sizes', 20 );
/**
 * Grid Loop Featured Image
 *
 * @param string image size
 * @return string
 */
function be_grid_loop_image( $image_size ) {
	global $wp_query;
	$grid_args = be_grid_loop_pagination();
	if( ! $grid_args )
		return $image_size;
	// Feature
	if( ( ! $wp_query-&gt;query_vars['paged'] &amp;&amp; $wp_query-&gt;current_post &lt; $grid_args['features_on_front'] ) || ( $wp_query-&gt;query_vars['paged'] &amp;&amp; $wp_query-&gt;current_post &lt; $grid_args['features_inside'] ) )
		$image_size = 'be_feature';
	if( ( ! $wp_query-&gt;query_vars['paged'] &amp;&amp; $wp_query-&gt;current_post &gt; ( $grid_args['features_on_front'] - 1 ) ) || ( $wp_query-&gt;query_vars['paged'] &amp;&amp; $wp_query-&gt;current_post &gt; ( $grid_args['features_inside'] - 1 ) ) )
		$image_size = 'be_grid';
	return $image_size;
}
add_filter( 'genesis_pre_get_option_image_size', 'be_grid_loop_image' );
/**
 * Fix Posts Nav
 *
 * The posts navigation uses the current posts-per-page to
 * calculate how many pages there are. If your homepage
 * displays a different number than inner pages, there
 * will be more pages listed on the homepage. This fixes it.
 *
 */
function be_fix_posts_nav() {
	if( get_query_var( 'paged' ) )
		return;
	global $wp_query;
	$grid_args = be_grid_loop_pagination();
	if( ! $grid_args )
		return;
	$max = ceil ( ( $wp_query-&gt;found_posts - $grid_args['features_on_front'] - $grid_args['teasers_on_front'] ) / ( $grid_args['features_inside'] + $grid_args['teasers_inside'] ) ) + 1;
	$wp_query-&gt;max_num_pages = $max;
}
add_filter( 'genesis_after_endwhile', 'be_fix_posts_nav', 5 );</code></pre></noscript><p>The post <a
href="http://www.billerickson.net/a-better-and-easier-grid-loop/">A better, and easier, grid loop</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/a-better-and-easier-grid-loop/feed/</wfw:commentRss> <slash:comments>179</slash:comments> </item> <item><title>Genesis Grid Loop Advanced</title><link>http://www.billerickson.net/genesis-grid-loop-advanced/</link> <comments>http://www.billerickson.net/genesis-grid-loop-advanced/#comments</comments> <pubDate>Sun, 12 Feb 2012 15:41:07 +0000</pubDate> <dc:creator>Bill Erickson</dc:creator> <category><![CDATA[Tutorial]]></category> <category><![CDATA[advanced]]></category> <category><![CDATA[genesis]]></category> <category><![CDATA[gridloop]]></category> <guid
isPermaLink="false">http://www.billerickson.net/?p=4219</guid> <description><![CDATA[<p>I recently worked with Gary Jones on refreshing his popular grid loop tutorial.</p><p>The post <a
href="http://www.billerickson.net/genesis-grid-loop-advanced/">Genesis Grid Loop Advanced</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></description> <content:encoded><![CDATA[<p>I recently worked with Gary Jones on refreshing his popular grid loop tutorial.</p><p><a
href="http://code.garyjones.co.uk/genesis-grid-loop-advanced/">Read the full article here.</a></p><p>The post <a
href="http://www.billerickson.net/genesis-grid-loop-advanced/">Genesis Grid Loop Advanced</a> appeared first on <a
href="http://www.billerickson.net">Bill Erickson</a>.</p>]]></content:encoded> <wfw:commentRss>http://www.billerickson.net/genesis-grid-loop-advanced/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>