Archive for the ‘WordPress’ Category

Better Includes and Excludes for wp_list_pages

The wp_list_pages() template tag forms the navigation core of most WordPress-powered websites, but it has a few issues:

  1. The exclude_tree parameter doesn’t work. If you include a list of page ID numbers after “exclude_tree=” in your wp_list_pages() tag, all of those pages (and all of their child pages) are supposed to be excluded from the page list. However, only the first page ID actually works this way; the others are ignored.
  2. There’s no such thing as an include_tree parameter.

That second one really became a pain working on a non-profit site recently. After I turn the site over, I want the director to be able to add and move pages whenever he wants without it breaking the nice drop-down menus I worked so hard styling for him.

The Function

That’s where this handy little function comes in. It adds an include_tree parameter and solves the exclude_tree problem by taking the list of page IDs in your exclude- and include_tree parameters, finding all of the child pages (to the depth you specify), then adding those parent and child IDs to your exclude and include parameters before passing everything on to the wp_list_pages function.

function md_list_pages( $args ) {

	$defaults = array(
		'depth' => 0, 'show_date' => '',
		'child_of' => 0, 'exclude' => '',
		'title_li' => __('Pages'), 'echo' => 1,
		'authors' => '', 'sort_column' => 'menu_order, post_title',
		'link_before' => '', 'link_after' => '',
		'include' => '', 'include_tree' => '',
		'exclude_tree' => '',

	$r = wp_parse_args( $args, $defaults );

	$exclude = list_pages_tree( $r[ 'exclude' ], $r[ 'exclude_tree' ],
			$r[ 'depth' ] );
	$include = list_pages_tree( $r[ 'include' ], $r[ 'include_tree' ],
			$r[ 'depth' ] );

	$pages = wp_list_pages( 'depth=' . $r[ 'depth' ] . '&show_date='
			. $r[ 'show_date' ] . '&child_of=' . $r[ 'child_of' ]
			. '&exclude=' . $exclude . '&title_li='
			. $r[ 'title_li' ] . '&echo=0&authors=' 
            . $r[ 'authors' ] . '&sort_column=' 
            . $r[ 'sort_column' ] . '&link_before=' 
            . $r[ 'link_before' ] . '&link_after=' 
            . $r[ 'link_after' ] . '&include=' . $include );
    	echo $pages;
	} else {
    	return $pages;


function list_pages_tree( $param, $tree, $depth ) {

	// get the parent pages of the tree
	$parent_pages = get_pages( 'include=' . $tree );

	foreach ( $parent_pages as $parent ) {
		if (!empty( $param ) ) {
			$param .= ",";

		$param .= $parent->ID;

		// get the child pages of the tree
		$child_pages = get_pages ( 'child_of=' . $parent->ID
			. '&depth=' . $depth );

		foreach ( $child_pages as $child ) {
			$param .= "," . $child->ID;

	return $param;

How to Use It

Copy and paste the above functions into your functions.php file. Then replace all instances of wp_list_pages() in your template with md_list_pages(). The parameters are the same as the wp_list_pages parameters, with the addition of the include_tree parameter.

For example, I replaced this call on the non-profit site:

<?php wp_list_pages( 'depth=2&sort_column=menu_order&title_li=
			&include=5,9,11,14,2,16' ); ?>

…which only returns the included pages, with this call:

<?php md_list_pages( 'depth=2&sort_column=menu_order&title_li=
			&include_tree=5,9,11,14,2,16' ); ?>

…which returns the included pages and all of their child pages (for the drop-down menu on the site).

I realize that this method results in the template tag parameters being parsed twice, but I decided to keep wp_list_pages in the mix rather than bypass it completely because some plugins (including some on the aforementioned non-profit site) function by adding or removing actions to that template tag.

Hope this is helpful to someone else out there — I’ll be using it myself more in the future (unless WP3.0 eliminates my need for a workaround).

Update: Wow, over 350 pingbacks, and all of them spam. Thank goodness for Akismet…

Update 23 July 2011: I thought the new menu system in WordPress 3.0 pretty much killed the need for this workaround, but I guess there are still reasons to use wp_list_pages(). I’d love to hear how people are still using the function (wp_list_pages(), not my md_list_pages()) and whether they prefer it to wp_nav_menu() or not. Depending on response, I may have to rework my solution a bit to reduce the processing overhead and to weed out the bugs.


WordPress Wednesday: How to Use paginate_comments_links Only When You Really Mean It

March 10, 2010 Comments off

I was pretty happy when WordPress added paginate_comments_links because now I didn’t have to write a custom function to break blog comments into separate pages and give them a nice navigation (instead of just “next” and “previous”). However, I like to put my navigation in a div, and I like to style that div…and it can look pretty bad when my styled div shows up with no navigation (depending on my design).

The solution is pretty simple (as most of my solutions are): I wrote a simple function that only echoes my div and pagination links if the comments are paginated:

function comment_pagination() {
	//read the page links but do not echo
	$comment_page = paginate_comments_links('echo=0');

	//if there are page links, echo the navigation div and the page links
	if (!empty($comment_page)) {
		echo "<div class=\"navigation\">\n";
		echo $comment_page;
		echo "\n</div>\n";

All that’s left at this point is styling the pagination links.  The above code will give you a markup along these lines:

<div class="navigation">
	<a class='prev page-numbers'>&laquo; Previous</a>
	<a class='page-numbers'>1</a>
	<span class='page-numbers current'>2</span>
	<a class='page-numbers'>3</a>
	<a class='next page-numbers'>Next &raquo;</a>

Hopefully I’ll learn more about paginate_comments_links in the future and be able to better customize the output. We’ll see.

WordPress Wednesday: Turn Any Shortcode into a Template Tag

March 3, 2010 18 comments

I previously discussed WordPress’s shortcode API, which allows you to turn any function into a shortcode that you can embed in the content of your pages or posts. Though the example I gave was for a function I wrote, you can use this API for WordPress template tags. The easiest way is to get the Template Tag Shortcodes plugin by Justin Tadlock, although you can also write your own by adding the following line to your functions.php file:

add_shortcode('shortcode_name', 'template_tag');

But what about the other way around? The solution is just as simple with the do_shortcode function.

You may remember that my previous shortcode allowed a client to enter [link pagename=’example’] into the content of any page, and the function would insert the named page’s permalink into the content for him. If I wanted to call that shortcode as a template tag, the tag would look like this:

<?php echo do_shortcode("[link pagename='example']"); ?>

All you have to do to turn any shortcode into a template tag is substitute your shortcode (with the desired parameters defined) between the double quotes (“) and you’re good to go. This is especially helpful when plugins come with shortcodes included but not template tags (like NextGen Gallery).

What will you use the do_shortcode function for?

Update: Thanks to Holling for pointing out that I forgot to include the echo statement. Fixed now.

WordPress Wednesday: How to Transfer Your Plugin Data Using phpMyAdmin

February 10, 2010 1 comment

I often use Stray Random Quotes for clients who want to display random testimonials (or quotes, natch) on their WordPress sites. Of course, I build their sites completely on my webspace to test them before installing on the client domain. While WordPress has export and import functions for pages, posts and media, my quotes don’t transfer over so easily. Fortunately, the built-in functions of phpMyAdmin can pick up the slack.

The following assumes that you already have WordPress installed on both your test domain and the live domain. I’m using the specific example of Stray Random Quotes but you can do the same with any plugin that creates tables in your WordPress database.

First, make sure you have Stray Random Quotes (or whichever plugin you’re transferring data for) installed on the live site. This is important because the plugin will create the necessary tables for you (saves you a few steps and a lot of headache).

Second, open your WordPress database using phpMyAdmin on your test site. In the left-hand column, click on the database name to expand it, then click on the wp_stray_quotes table.

Click on the “Export” tab in the  main window. Make sure your export format is set for “SQL” and your export type is “INSERT,” then click “Go.”

You should see a text representation of the database export. Copy only the text rows that start with “INSERT INTO.” Paste these into a text file using Notepad and save as a .txt file.

Next, open the WordPress database on your live site using phpMyAdmin. Click on the database name to expand and then on wp_stray_quotes.

Click the “Import” tab, then the “Browse” button. Locate the text file you created previously and click “Open,” then click “Go.”

Your quotes should be imported into the live database.

WordPress Wednesday: All-in-One SEO Without Using wp_head()

February 3, 2010 24 comments

One of my favorite plugins to use for client sites is the All-in-One SEO Pack. I like how it allows me (or the client) to specify unique keywords, descriptions, menu text, etc. for each page or post on their site. I don’t like that it requires me to use the wp_head() function in order to edit keywords and descriptions.

For those who don’t know already, wp_head() automatically adds data to the header of your WordPress pages. Some of this data is helpful (like adding the descriptions and keywords from the All-in-One SEO Pack); some of it is not (like what version of WordPress your site uses, which helps hackers know which vulnerabilities they should try to exploit).

My workaround is to add a couple of simple php functions to my functions.php file. For example, the following function write the SEO Pack meta keywords into my WordPress page:

// function to insert All-in-One SEO Pack keywords
function keyword_insert() {
 global $post; // VERY important!

 // Retrieve keyword meta data from the SEO Pack
 $seokeywords = stripslashes(get_post_meta($post->ID, '_aioseop_keywords', true));

 // Default keywords in case none are specified for the page
 if (empty($seokeywords)) $seokeywords = "Homestar Runner, Strong Bad, The Cheat";

 // Output the html code
 $seokeyword_block = "<meta name=\"keywords\" content=\"".$seokeywords."\"/>\n";
 echo $seokeyword_block;

If I want to use the SEO Pack meta description, I alter the function like so:

// function to insert All-in-One SEO Pack description
function description_insert() {
 global $post; // VERY important!

 // Retrieve description meta data from the SEO Pack
 $seodesc = stripslashes(get_post_meta($post->ID, '_aioseop_description', true));

 // Default description in case none is specified for the page
 if (empty($seodesc)) $seodesc = "Oh! I am Homestar, and This is A Website!";

 // Output the html code
 $seodesc_block = "<meta name=\"description\" content=\"".$seodesc."\"/>\n";
 echo $seodesc_block;

If I wanted to create functions to pull additional information from the SEO Pack, they would also follow this same format. The different meta keys used by the All-in-One SEO Pack are:

  • _aioseop_keywords: meta keywords for the page; if your template already includes a <meta> keywords tag, the SEO Pack function will write a second tag — it is better to use one or the other and not both
  • _aioseop_description: meta description for the page; like the keywords, the SEO Pack function will write a second <meta> description if one is already included in the template
  • _aioseop_title: as in page title; the SEO Pack will rewrite any instances of the post title with this value unless you uncheck the “Rewrite Titles” option in the plugin settings; otherwise defaults to post title if no value is specified
  • _aioseop_menulabel: this is the text that is retrieved by the wp_list_pages() function; defaults to post title if no menu label is specified
  • _aioseop_titleatr: this is the “title” attribute for the links retrieved by the wp_list_pages() function; defaults to post title if no title attribute is specified

You will probably use the other meta keys less frequently than _aioseop_keywords and _aioseop_description since the other keys work through existing WordPress functions, although I have had use special functions for those keys on a few occasions.

Update: Silly me, I forgot to mention how to use these.

Place the following lines of code between the <head> and </head> tags in your template:

<?php description_insert(); ?>

<?php keyword_insert(); ?>

But then you probably already knew that.

WordPress Wednesday: Using the Shortcode API to Create Blog Links

December 9, 2009 5 comments

I’m working on a client site using WordPress as the CMS. My client loves how much freedom WordPress gives him to manage his site, but he wants a simple way to create internal links on the site since he is not particularly web-savvy. Obviously, TinyMCE (the default editor) has a button for links, but using it requires copy/pasting the URL and he wants something a little simpler and more dynamic.

The easiest solution is to use the AWS Easy Page Link plugin. Unfortunately, the plugin hard-codes the link, and since I’m building the site on my company’s webspace before pushing the site live I would have to re-code all the links once I move the site to the client’s webspace.

Enter the Shortcode API. Shortcodes allow you to call php functions from within your page or post content using special tags that you define.

First, I create a function in my functions.php file:

//function for creating a link from a page name
function create_pagelink($atts) {

 	//extract page name from the shortcode attributes
 	extract(shortcode_atts(array( 'pagename' => 'home', ), $atts));

 	//convert the page name to a page ID
 	$page = get_page_by_title($pagename);

 	//use page ID to get the permalink for the page
 	$link = get_permalink($page->ID);

 	//create the link and output
 	$pagelink = "<a href=\"".$link."\">".$pagename."</a>";

 	return $pagelink;

Then, directly beneath that function I add the following line:

add_shortcode('link', 'create_pagelink');

Where ‘link’ is the name of the shortcode and ‘create_pagelink’ is the function that I want to associate with the shortcode.

Now if my client wants to create a link (or have me create a link) to a page called “My Example Page” from a blog post or page on his site, we can type the following shortcode:

[link pagename="My Example Page"]

When the blog post or page with the shortcode is visited, the shortcode will be replaced by:

<a href="">My Example Page</a>

Since the function creates the link by first finding the permalink to the page, I don’t have to replace the code when I move the site.