WordPress 2.8 Single Post Navigation Widget

Since WordPress 2.8, there is a new Widget API. In our post Build A WordPress 2.8 Widget With The New Widget API, I have used a simple example to describe how to build a Widget. This time Heiko and I've created something more complex.


It is a Post Navigation Widget, which lists in the single post view (single.php) a specific number of posts which were published before this post and a certain number of posts which were published after this post. I think this is a nice way to show older posts in the sidebar. Here's a screenshot, left of the backend, right of the sidebar:

WordPress Single Post Navigation Widget

I created a query to check if the class WP_Widget even exists, so the user won't get any error messages in WordPress versions prior to 2.8.

<?php
if (class_exists('WP_Widget')) {
    class WPE_Widget_Post_Navigation extends WP_Widget {

        function WPE_Widget_Post_Navigation() {
            $widget_ops = array('classname' => 'wpe_widget_post_navi', 'description' => __( "Some posts before and after the current post") );
            $this->WP_Widget('wpe-post-navi', __('WPE Single Post Navigation'), $widget_ops);
        }

        function widget($args, $instance) {
            if(is_single()) {
                global $post, $wpdb;
                extract($args);

                if ( !$number = (int) $instance['number'] )
                    $number = 5;
                elseif( $number < 1 )
                    $number = 1;
                elseif( $number > 10 )
                    $number = 10;

                $title_before 		= apply_filters('widget_title', empty($instance['title_before']) ? 'Posts before' : $instance['title_before']);
                $title_after	 	= apply_filters('widget_title', empty($instance['title_after']) ? 'Posts after' : $instance['title_after']);
                $before_widget_2	= preg_replace("/(wpe\-post\-navi-\d+)/", "$1-1", $before_widget);

                $querystr = "
                    SELECT *
                    FROM $wpdb->posts wposts
                    WHERE wposts.ID != $post->ID
                    AND wposts.post_type = 'post'
                    AND wposts.post_status = 'publish'
                    AND wposts.post_date %s '$post->post_date'
                    ORDER BY wposts.post_date %s
                    LIMIT $number
                 ";

                $leading_posts = $wpdb->get_results(sprintf($querystr, '<', 'DESC'), OBJECT);
                $trailing_posts = $wpdb->get_results(sprintf($querystr, '>', 'ASC'), OBJECT);

                if ($trailing_posts && count($trailing_posts)) {
                    echo $before_widget_2 . $before_title . $title_before . $after_title . "<ul>";
                    $trailing_posts = array_reverse($trailing_posts);
                    foreach ($trailing_posts as $post) {
                        setup_postdata($post);
                        ?> <li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?> </a></li><?php
                    }
                    echo "</ul>" . $after_widget;
                }

                if ($leading_posts && count($leading_posts)) {
                    echo $before_widget . $before_title . $title_after . $after_title . "<ul>";
                    foreach ($leading_posts as $post) {
                        setup_postdata($post);
                        ?> <li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?> </a></li><?php
                    }
                    echo "</ul>" . $after_widget;
                }
                wp_reset_query();
            }
        }

        function update( $new_instance, $old_instance ) {
            $instance = $old_instance;
            $instance['title_before'] = strip_tags($new_instance['title_before']);
            $instance['number'] = (int) $new_instance['number'];
            $instance['title_after'] = strip_tags($new_instance['title_after']);

            return $instance;
        }
        function form( $instance ) {
            $title_before = ($instance['title_before'] == '') ? 'Posts before' : esc_attr($instance['title_before']);
            $title_after = ($instance['title_after'] == '') ? 'Posts after' : esc_attr($instance['title_after']);
            if ( !$number = (int) $instance['number'] )
                $number = 5;
            elseif ( $number < 1 )
                $number = 1;
            elseif ( $number > 10 )
                $number = 10;
    ?>
            <p><label for="<?php echo $this->get_field_id('title_before'); ?>">Title before:</label>
            <input class="widefat" id="<?php echo $this->get_field_id('title_before'); ?>" name="<?php echo $this->get_field_name('title_before'); ?>" type="text" value="<?php echo $title_before; ?>" /></p>
            <p><label for="<?php echo $this->get_field_id('title_after'); ?>">Title after:</label>
            <input class="widefat" id="<?php echo $this->get_field_id('title_after'); ?>" name="<?php echo $this->get_field_name('title_after'); ?>" type="text" value="<?php echo $title_after; ?>" /></p>
            <p><label for="<?php echo $this->get_field_id('number'); ?>">Number of posts to show before and after current post:</label>
            <input id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo $number; ?>" size="3" /><br />
            <small>(at most 10)</small></p>
    <?php
        }
    }
    register_widget('WPE_Widget_Post_Navigation');
}
?>

Most of this should be self-explanatory. Now to the function widget () , which does the actual work. Here we had to do a little trick, because the widget actually creates 2 widgets, and they would get the same ID. That wouldn't be valid, so the code adds a -1 to one of the widget elements:

$before_widget_2 = preg_replace("/(wpe\-post\-navi-\d+)/", "$1-1", $before_widget);

2 queries are getting executed. One for the posts before this post and one for the posts after this post. if posts are available they will show up on the sidebar.

Download Widget.
Then unpack and copy the code into functions.php. Please note, the widget is for WordPress 2.8.

Comments are closed.

9 comments

  1. Wallace

    Great plugin!
    By the way, can we control how many words in excerpts?

  2. Home Trends

    This is nice tweak.... and add some Widget feature make this code very useful for created extra ordinary wordpress

  3. luis

    hi,

    this page uses some widget to show the previous post name and the next post name, how do you do it? using this widget?

    thanks,

  4. luis

    hi, I found that it was simpler than that and there is a function (the regular function) for these links in wordpress, so I forgot to look closely at the functions that were already there.

    Encouragements,

  5. zauberer & hütchenspieler

    Hi, thanks for this nice widget, i use it and hope lot of users will like it. One question, how can i give the headlines befor & after an h2 for the layout?

  6. Michael

    @zauberer: In your functions.php you have a function

        register_sidebar(array(
            'name' => 'Sidebar',
            'before_widget' => '<li id="%1$s" class="widget %2$s">',
            'after_widget' => '</li>',
            'before_title' => '<h2 class="widgettitle">',
            'after_title' => '</h2>'
        ));
    

    You can change the values to your need.

  7. Dan

    How would one include this in a plugin as oppose to functions.php?

  8. redlex

    After following this tutorial, I couldn't get a couple of things to work. The permalinks will not work with the register_post_type() and arrays outlined in a function; I simply removed this and permalinks worked fine

One pingback

  1. Offene Tabs: Wordpress’n’Webdesign Tips & Tricks - stefan.waidele.info