09.04.2006

Since I implemented the Asides feature into Binary Blue, I went crazy on its misbehaviours as soon as you don’t use it as inline asides but as sidebar asides instead. Today I finally sat down and dugg once again into the depths of WordPress to find out how to circumvent the issues. I must admit that I only tested the K2 implementation way as it is a pretty convenient one - maybe there are other implementations out there that don’t suffer from these bugs, but at least K2 does.

The issues drill down to the following ones:

  1. As soon as you have an article defined as Asides that would regularly be displayed on your front page, the amount of posts displayed on the frontpage will be smaller than defined in the blog’s reading options. This may finally result in the display of an empty page, when all of the posts that would usually be displayed on that page are Asides posts.
  2. The amount of Asides posts being displayed in the sidebar varies from time to time and often does not reflect the value you’ve set up in the theme’s options.

These issues have their reason in the way this feature is implemented so far. There is a check in the file named theloop.php which looks up whether the current post that is handled in the loop is a member of the category that has been defined as the Asides one. If that post is an Asides post, but the configuration is set to display the asides in the sidebar, the complete post will not be displayed. Unfortunately, when theloop.php starts its dance, the complete post query has already been done, so there is no chance to add older posts further down if other posts fall “through the gates” coz of this sidebar asides check.

The only way to solve this issue is to get into the database query itself which would build up the posts list for the current page. So my task was to find a way to modify the SQL query that’s submitted to the database server. Fortunately, WordPress offers exactly the filter hooks we need for this special task, and unfortunately, both are awefully bad documented. ;) Long rant, little sense, we simply need to utilize both posts_join and posts_where filter hook to solve issue no. 1.

Just look at the following code:


function bb_asidesfilter_join($join)
{
  global $wpdb;
  // If the page is not a category page, then we need the JOIN.
  if (!is_category() && !is_admin() && !is_single()){
		return " LEFT JOIN {$wpdb->post2cat} ON {$wpdb->posts}.ID = {$wpdb->post2cat}.post_id " . $join;
  }
  else {
	  return $join;
  }
}

function bb_asidesfilter_where($where)
{
  global $wpdb;
  $bb_asides_cat = get_option('bb_asidescat');
  $sidebar_asides = get_option('bb_asidespos');

  if (('0' != $bb_asides_cat) && ('sidebar' == $sidebar_asides) && !is_admin() && !is_category() && !is_single())
  {
    $where = $where." AND (".$wpdb->post2cat.".category_id NOT IN (".$bb_asides_cat.'))';
  }
  return $where;
}

// apply the sidebar asides filters only if appropriate
if (get_option('bb_asidescat') != '0' && get_option('bb_asidespos') == 'sidebar' && (!is_single() && in_category(get_option('bb_asidescat'))))
{
	add_filter('posts_join', 'bb_asidesfilter_join');
	add_filter('posts_where', 'bb_asidesfilter_where');
}

What does this code do? Well, pretty simple, but looking odd ;) First we check whether we are already on the run to display a category’s posts, and if not, we need to add the JOIN statement that will allow to look for each post’s categories. This is the function that has to be added to the posts_join hook. Second, the WHERE clause of the database query must receive an expansion to make sure that no post will be visible that is assigned to the sidebar asides category. This second function then is hooked up at the posts_where hook. Both hooks will be applied only if sidebar asides are active, otherwise there’s simply no need to add server load to the database server with a useless JOIN ;).

The result of this code (I’ve added it to the functions.php file of the Binary Blue theme suite) is the display of the startpage with exactly the amount of posts per page we’d expect due to the setup in the general reading options ;) But, take care: you must not add a second category to a post you want to be an Asides one - otherwise the display will be erroneous again!

Now for the solution for the second issue - we want to display always the correct amount of sidebar asides posts, and this is done the following way (this code will be stored in the sidebar.php file, or, in case of Binary Blue, as a distinct block that is once called in sidebar.php and is secondly part of a BB specific Sidebar Widget):


< ?php
if (!is_paged() && is_home())
{
  $bb_asides = get_option('bb_asidespos');
  if ($bb_asides == 'sidebar')
  {
    $bb_asidescat = get_option('bb_asidescat');
    $bb_asidesnumber = get_option('bb_asidesnumber');
?>
  <li class="rubrik" id="sidebarAsides">
    <h2>< ?php _e('Asides', '4null4.de'); ?></h2>
< ?php
    $temp_query = $wp_query; // save original loop
    // the two filters must not get applied now!
    remove_filter('posts_join', 'bb_asidesfilter_join');
    remove_filter('posts_where', 'bb_asidesfilter_where');
?>
    <ul id="sidebarAsidesList">
< ?php
    query_posts('cat='.$bb_asidescat.'&posts_per_page='.$bb_asidesnumber);
    while (have_posts())
    {
      the_post();
      if (($bb_asides == 'sidebar') && in_category($bb_asidescat))
      {
?>
      <li>
        <h2><a href="<?php the_permalink($post->ID) ?>" rel="bookmark" title="< ?php _e('Permanenter Link auf diesen Aside', '4null4.de'); ?>">< ?php the_title(); ?></a></h2>
        <div class="aside" id="p<?php the_ID(); ?>">< ?php the_excerpt(); ?></div>
      </li>
< ?php
      }
    } // End Asides Loop
    $wp_query = $temp_query; // revert to original loop
    // re-add the two filters needed for sidebar asides
    add_filter('posts_join', 'bb_asidesfilter_join');
    add_filter('posts_where', 'bb_asidesfilter_where');
?>
    </ul>
  </li>
< ?php
  }
}
?>

The solution is, once again, pretty simple, as soon as you’ve found it ;) The solution’s core is the query_posts() call with the correct additional parameters. In addition, we need to backup the original query for future use (you can’t be sure where the sidebar code is called, right?). For the same reason we have to remove the filters we’ve defined in functions.php temporarily, otherwise we’d filter the very query we want to submit to the database. If we’d forget to remove the filters, we’d receive just an empty resultset, and that would not be the result we want to receive, right? ;) After the query has been submitted successfully, there is the usual stuff to build up the loop, and finally we have to restore both the backed up query and the two filters. There’s a pretty simple reason to re-apply them - there may be other sidebar plugins or widgets which would rely on them, so it’s always better to re-apply them than to totally forget them ;)

Well, that’s it, my dear friends - if you want to implement this or if you just want to get rid of that bugridden old solution, you just need a little additional code to support the three options this solution uses - Asides category id, Asides position (’inline’ or ’sidebar’) and the amount of Asides posts to display in case of an activated Sidebar Asides option.

This corrected Asides feature will be available in the next Binary Blue release, just before you ask ;)

Verwandte Artikel:



Hinweis: Wegen des hohen Aufkommens an Kommentarspam und als Kommentar getarnten Werbelinks werden alle Kommentare auf diesem Blog zuerst in die Moderation geschickt. Ich schalte neue Kommentare von echten Besuchern so schnell wie möglich frei. Beleidigende oder gegen geltendes Recht verstoßende Kommentare werden gelöscht.

Bisher 6 Kommentare zum Artikel

  1. CountZero meint

    oh, no worry - it’s taken care of them in theloop.php, but thanks for mentioning them anyways ;)

  2. Zeo meint

    Don’t forget about is_page(), is_search(), is_archive() and is_tag() ;)

  3. CountZero meint

    Zeo,

    I have fixed both issues now in the code above. It works great with both Inline and Sidebar Asides, the Asides category list works again, and the Permalink to the different Asides posts work again as well, as long as you assure in theloop.php that inline asides posts aren’t excluded from the single post view.

    Feel free to add this fix to K2 as well - that was the main purpose why I posted this article and backlinked it in the K2 bugtracker ;)

  4. CountZero meint

    damn, you name it. sorry, I wasn’t aware of this until now. I didn’t expect that the admin/manage posts panel makes use of the very same filter hook. Alas, I’d name that a bug in WordPress itself that it uses the same hook.

    Well, I’ll fix that this evening - my working day in the office has just started ;)

  5. Zeo meint

    uh! The asides post disappear from ‘admin panel’ and the post link not exist.

  6. Zeo meint

    Excellent!

    I’ve tried your workaround to solve this matter and so far so good. Keep us updated!