prependChild on DOMElement in PHP, Sort of.

This evening I wanted to add a new LI onto a WordPress navigation programmatically. After considering a few different options, I decided I wanted to use PHP’s DOMDocument, and use this as an opportunity to more familiarize myself with its ins and outs.

Looking into DOMElement, I was pleased to see there was an appendChild() method, but a little down to see there there was no prependChild(). After searching online, I saw a few people creating custom classes to make their way around this absense, however I found a method that I prefer a little more.

Stumbling upon insertBefore(), I realized all of my problems had been solved…kinda.¬†insertBefore() was leaving me just a bit confused; it sounds as though you call it from the element you wish to move around, but you don’t. In fact, you don’t even call it on the element you’re trying to snuggle up against. Instead, you call it on the parent of the two, referencing both elements as the arguments.

add_action( 'wp_nav_menu', 'custom_nav_menu' );
function custom_nav_menu( $menu ) {
  // Load it
  $html = new DOMDocument();
  $html->loadHTML( $menu );
  // Get it
  $list = $html->getElementsByTagName( 'ul' )->item( 0 );
  $first = $list->getElementsByTagName( 'li' )->item( 0 );
  $new_item = $html->createElement( 'li', 'Hello World' );
  // Place it
  $list->insertBefore( $new_item, $first );
  // Show it
  return $html->saveXML( $list->parentNode );
}

This gave the desired results, without requiring any new classes or other more complicated work-arounds. To my knowledge I don’t see any downfalls of it either, at least not at this time.

Some of you may be scratching your head at the last line, where I’m calling saveXML() rather than saveHTML(). As for 5.3.6 you can pass in an optional DOMNode to specify which portion of the DOMDocument you’d like to render. I happen to be using 5.3.5, so this option is not available to me and all I got to see was a nasty error message. Fortunately saveXML() worked as a nice substitute with little-to-no side-effects.

Unexpected Results While Styling WordPress Menus

Today I sat down to convert a static theme consisting of nothing more than HTML and CSS into a WordPress theme, complete with calls to functions like wp_nav_menu() for the construction of the menu’s. For this particular theme I began with a copy of the TwentyTen theme. When I came across the task of establishing a navigational menu I found some rather unexpected results from WordPress – something I think they should consider changing in future releases. (Note: The following assumes version 3.1.3 is being used)

When you open up header.php in the TwentyTen theme you’ll find the following on or around line 85:

<?php wp_nav_menu( array( 
  'container_class' => 'menu-header', 
  'theme_location' => 'primary' 
) ); ?>

This is really pretty straight forward. It defines itself as the location for any menu assigned to the “primary” spot. Additionally, it sets a container class to “menu-header”. Given this information, we should expect this to result in is an unordered list wrapped in a div (the default container is a div) that has the class “menu-header”. So what markup is generated by WordPress when somebody sets up a fresh copy of it and checks out their TwentyTen theme?

<div class="menu">
  <ul>
    <!-- ... -->
  </ul>
</div>

At first there may not appear to be any problems, but look more closely at the container class declared in the call to wp_nav_menu() and what is actually applied in the generated markup. While we declared a class of “menu-header”, what we actually get is a class of “menu”. This really frustrated me this evening.

Some users of WordPress have been unable to reproduce this effect, and the reason is fairly simple why they couldn’t – they likely have a menu already assigned to the “primary” location. Anybody who does not initially have a menu created and applied to this location will be left scratching their head.

So what is WordPress doing exactly? Initially WordPress will look for a menu assigned to this location, if it doesn’t find one it will move on to do something else. Along down the line, just before throwing its hands up completely it will call a fallback method to generate something for the user. The default fallback method is provided for us in the source (line 141):

$defaults = array( 
  /* ... */
  'fallback_cb' => 'wp_page_menu'
  /* ... */
);

So if no menu is present when wp_nav_menu() is called, it will fallback into calling the wp_page_menu() method. Up to this point this shouldn’t seem like much of a threat. Sure, if I don’t have a menu WordPress can go ahead and generate a list of pages that I might have published. Unfortunately, this is where some communication-breakdown occurs and things get a bit messy.

Recall the generated HTML list previously shown, this is what wp_page_menu() generated for us in the absence of a user-defined menu. Like wp_nav_menu(), wp_page_menu() has its own set of internally-declared default arguments that will determine much of how the results will look and feel. We can see these defaults declared below:

$defaults = array(
  'sort_column' => 'menu_order, post_title', 
  'menu_class' => 'menu', 
  'echo' => true, 
  'link_before' => '', 
  'link_after' => ''
);

The only thing we’re interested in right now is the ‘menu_class’ which, by default, is set to “menu”. This is where the “menu” class comes from in the place of our “menu-header” class passed to wp_nav_menu(). This isn’t the light at the end of the tunnel, things get even messier from here.

While it’s nice of WordPress to generate a list for us in the absence of a menu, the integrity of our data breaks down in the process. When we call wp_nav_menu() and tell it to set the “container_class” to “menu-header”, the resulting call to wp_page_menu() completely ignores this request. Again, this isn’t the end of the mess, the wp_nav_menu() function also allows us to provide a ‘menu_class’, or a class which should be applied to the generated menu within the container. Suppose we did this:

<?php wp_nav_menu( array( 
  'container_class' => 'menu-header', 
  'menu_class' => 'foo', 
  'theme_location' => 'primary' 
) ); ?>

What we should expect to see from this is a menu that looks like the following:

<div class="menu-header">
  <ul class="foo">
    <!-- ... -->
  </ul>
</div>

This should be the result regardless if pages or custom menu items populate the UL. If no menu is present, and WordPress falls back on the wp_page_menu() function, what it generates is anything but expected:

<div class="foo">
  <ul>
    <!-- ... -->
  </ul>
</div>

Note how the “menu_class” is actually applied to the container, and the “container_class” of “menu-header” is entirely abandoned. This isn’t what we asked for.

If we turn around and create a menu called “Sample Menu”, and set it to be displayed in the “primary” location, what we are presented with is the following:

<div class="menu-header">
  <ul id="menu-sample-menu" class="foo">
    <!-- ... -->
  </ul>
</div>

Notice how it gets the container class correct, and the menu class correct when we actually have a menu for it to display.

I’m hoping this problem will be resolved in future versions of WordPress for the sanity of those creating themes. Not for them alone, but for the reputation of WordPress as a whole too. If its own internal communication isn’t consistent, it makes for a very bad testimony when those of us out in the real world are attempting to act as evangelists for the wonderful platform.

Finding and Linking to Adjacent Posts with Same Custom Key/Value Pair in WordPress

Today I continued working on merging multiple custom post types back into regular posts, and differentiated them with a custom key/value pair. Some of these old post types would link to their adjacent siblings – videos to previous and next videos, cartoons to previous and next cartoons, etc. With these posts no longer being a custom post type, a video would be linking to whatever post preceded it, which would be an article, cartoon, or any number of things – this wasn’t good.

I wasn’t thinking this would be too much of an issue, after all, don’t we have the freedom to provide constraints to functions like previous_post_link() and can’t we tell functions like get_posts() to return only posts whose ID is greater than or less than the current post id? I was wrong on both assumptions – kinda. You do have the freedom to create some constraints on previous_post_link(), but only at the category level. That means if I wanted videos to only link to videos, I would have to use category names as some form of post type¬†declaration, and I’m not about to do that. As for get_posts(), you cannot use any less-than or greater-than logic in relation to the present post ID.

What now? The solution I ended up going with was found in filters, specifically a custom filter tied to the “posts_where” filter. This idea comes from the WordPress documentation on the query_posts() function which provides an example of how to get posts whose date is within a timespan.

So the filter I ended up creating looks like this:

function filter_where_prev( $where = '' ) {
  global $post;
  return $where .= " AND ID < " . $post->ID;
}

I should note that that this code does in fact reside within single.php. The post id used will be from whatever post is presently residing in the $post variable. Next, we add this filter onto WordPress’ native filter. We’ll do this via the add_filter() function.

add_filter( "posts_where", "filter_where_prev" );

At this point we can now leverage the inherent power of the query_posts() function to tie things up. Remember that I wanted to restrict my results to posts that contain a specific key/value pair. Fortunately, this is something that query_posts() can indeed help me with:

query_posts( $query_string . "&meta_key=foo&meta_value=bar" );

Be sure to set your desired order and orderby criteria as well. I’ve left those out of my example for brevity.

With our new query established, we can proceed to run through our own micro-loop:

if ( have_posts() ) :
  while ( have_posts() ) : the_post();
    // Do whatever you like here
    // <li><?php the_title(); ?></li>
  endwhile;
endif;

Once we’re done building a list of references to older (or newer) posts, we need to clean up a bit. We don’t want future queries to use our filter, so we need to remove that. Additionally, we should reset our query as well:

remove_filter( "posts_where", "filter_where_prev" );
wp_reset_query();

Voila! That’s it. Now to do this for both the previous and next items, I had to create two filters. Be sure to remove any filters you create immediately after using them, and always reset your query!

Quickly Adding Thumbnails to WordPress Archives

Post thumbnails make archives look much more attractive in my personal opinion, yet they are not natively displayed in the otherwise-beautiful theme shipped with the latest version of WordPress, Twenty Ten.

As is the case with most WordPress stuff, this shouldn’t stop us from adding it in ourselves – this is exactly what we’ll do. I should note that I’m a student of WordPress, and wouldn’t consider myself an expert by any measure, but I do know enough to get around and do so without making a mess (correct me if I proceed to make a mess).

Our goal today is to add a small thumbnail next to our posts on the archive pages of the Twenty Ten theme. Of course what you’ll learn here can easily be applied to most themes. We’ll be making use of primarily two functions, modifying a third, and adding a few lines of CSS to accomplish this today.

We’ll be modifying only two files today, loop.php and style.css. Within loop.php we will add the code to show the image, and within style.css we’ll add the rules to style the post and the image itself. Let’s begin!

loop.php, where the magic happens

Within loop.php we will find and abundance of if-else-statements (at times I will break this down into numerous smaller files to improve readability). Within Twenty Ten version 1.1 we will find a comment on line 122 saying “How to display all other posts.” This is where we will do our initial changes. Look at the two lines following this comment:

<?php else : ?>
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

First we’ll want to determine whether or not the post has a thumbnail associated with it. If there is a thumbnail, we’re going to add a class to the post div which will make some changes to the formatting to accommodate the thumbnail itself. If there is no thumbnail, we’ll leave the post div as-is.

<?php else : ?>
<?php $tnail = has_post_thumbnail() ? "hasthumb" : "nothumb" ; ?>
<div id="post-<?php the_ID(); ?>" <?php post_class($tnail); ?>>
<?php the_post_thumbnail( 'thumbnail' ); ?>

There are a couple of things going on here, so let’s break them down individually. First, we’re building a CSS classname off of the presence (or lack) of a thumbnail. For anybody who might be confused with the syntax, we’re using the ternary operator to do this:

$tnail = has_post_thumbnail() ? "hasthumb" : "nothumb" ;

has_post_thumbnail() returns either true, or false. If it’s true, the value of $tnail will be “hasthumb,” and if it’s false the value of $tnail will be “nothumb”. We then carry that value down into our call to post_class() where it will be added to the beginning of a list of many other classnames generated by WordPress for this post.

Within the div that contains the post itself, we add a call to the_post_thumbnail() function and specify that we want the thumbnail size. This function assumes you want the thumbnail size even if you leave out a value. The thumbnail size is configurable in your settings (settings > media) – for my site I’m using the default 150px by 150px. If the post has a thumbnail, the HTML to show the image will be generated and the image will be placed within the resulting HTML.

Styling the Image

Now we need to style the image. Without adding our CSS, the image will sit on top of the post, which isn’t what we want. Instead, we would like to place the image to the left of the post, with a nice gutter between image and content. For this next step, let’s open up style.css and navigate down to the bottom of the file to add the following:

/* =Post Thumbnails
-------------------------------------------------------------- */
body.home div.post.hasthumb,
body.archive div.post.hasthumb,
body.search div.post.hasthumb {
  padding-left: 170px;
  position: relative;
}
body.home div.post.hasthumb img.attachment-thumbnail,
body.archive div.post.hasthumb img.attachment-thumbnail,
body.search div.post.hasthumb img.attachment-thumbnail {
  position: absolute;
  top: 0; left: -5px;
  border: 5px solid #fefefe;
  box-shadow: 0px 1px 3px #CCC;
  -moz-box-shadow: 0px 1px 3px #CCC;
  -webkit-box-shadow: 0px 1px 3px #CCC;
}

A careful look at these rules will show that they will only apply on the home, archive and search pages. Additionally, the selectors only apply to the posts that have images representing them since they are the only entries that have the hasthumb classname.

That’s it, you should be able to save all changes and begin adding a featured image for each of your posts and find it right along side your excerpt.