[Skratchdot.]

When creating this blog’s theme, I decided I wanted the links in the side navigation to take up the full width of the navigation. Also, when hovering, I wanted to highlight the whole link/line. This seems like a very simple thing to do with CSS, but by default, a few of the WordPress widgets do not allow for easy customization of the placement of the post count. This makes it hard to create sidebar entries in which the whole line is a link.

For example, by default, I could only get the Archive Widget to print out links in the following format:

<li><a href="/2009/09/">September 2009</a>&nbsp;(1)</li>

I wanted the links to be in this format (note the (1) is inside the anchor tag):

<li><a href="/2009/09/">September 2009&nbsp;(1)</a></li>

Below are the steps I took to override the default behavior of the Archive Widget. For this tutorial, I’m going to modify the “classic” theme that is provided with WordPress 2.8. You can download both the original “classic” theme as well as my modified version “classic-modified” here.

NOTE: For detailed instructions on creating a WordPress theme, there are many tutorials online. Also, the WordPress.org site has fairly detailed documentation. The first tutorial I found via a google search was for entry level WordPress developers, yet it was very helpful: www.wpdesigner.com.
 

For this tutorial, the first step to take is to copy the “classic” theme. Rename the copy “classic-modified” and place it in your themes folder.

Now we’re going to modify the functions.php file by adding lines 17-18 below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
/**
 * @package WordPress
 * @subpackage Classic_Theme
 */
 
automatic_feed_links();
 
if ( function_exists('register_sidebar') )
	register_sidebar(array(
		'before_widget' => '<li id="%1$s" class="widget %2$s">',
		'after_widget' => '</li>',
		'before_title' => '',
		'after_title' => '',
	));
 
// Include the script that replaces the default archive widget
require_once('functions-widgets.php');
 
?>

Now create a blank file called functions-widgets.php and place it in your themes folder. Next, open the file “/wp-includes/default-widgets.php” and copy lines 210-271. Paste these lines in functions-widgets.php so it looks like the code below (Note I’ve added the opening and closing php tags):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
 
/**
 * Archives widget class
 *
 * @since 2.8.0
 */
class WP_Widget_Archives extends WP_Widget {
 
	function WP_Widget_Archives() {
		$widget_ops = array('classname' => 'widget_archive', 'description' => __( 'A monthly archive of your blog&#8217;s posts') );
		$this->WP_Widget('archives', __('Archives'), $widget_ops);
	}
 
	function widget( $args, $instance ) {
		extract($args);
		$c = $instance['count'] ? '1' : '0';
		$d = $instance['dropdown'] ? '1' : '0';
		$title = apply_filters('widget_title', empty($instance['title']) ? __('Archives') : $instance['title']);
 
		echo $before_widget;
		if ( $title )
			echo $before_title . $title . $after_title;
 
		if ( $d ) {
?>
		<select name="archive-dropdown" onchange='document.location.href=this.options[this.selectedIndex].value;'> <option value=""><?php echo esc_attr(__('Select Month')); ?></option> <?php wp_get_archives(apply_filters('widget_archives_dropdown_args', array('type' => 'monthly', 'format' => 'option', 'show_post_count' => $c))); ?> </select>
<?php
		} else {
?>
		<ul>
		<?php wp_get_archives(apply_filters('widget_archives_args', array('type' => 'monthly', 'show_post_count' => $c))); ?>
		</ul>
<?php
		}
 
		echo $after_widget;
	}
 
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;
		$new_instance = wp_parse_args( (array) $new_instance, array( 'title' => '', 'count' => 0, 'dropdown' => '') );
		$instance['title'] = strip_tags($new_instance['title']);
		$instance['count'] = $new_instance['count'] ? 1 : 0;
		$instance['dropdown'] = $new_instance['dropdown'] ? 1 : 0;
 
		return $instance;
	}
 
	function form( $instance ) {
		$instance = wp_parse_args( (array) $instance, array( 'title' => '', 'count' => 0, 'dropdown' => '') );
		$title = strip_tags($instance['title']);
		$count = $instance['count'] ? 'checked="checked"' : '';
		$dropdown = $instance['dropdown'] ? 'checked="checked"' : '';
?>
		<p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></p>
		<p>
			<input class="checkbox" type="checkbox" <?php echo $count; ?> id="<?php echo $this->get_field_id('count'); ?>" name="<?php echo $this->get_field_name('count'); ?>" /> <label for="<?php echo $this->get_field_id('count'); ?>"><?php _e('Show post counts'); ?></label>
			<br />
			<input class="checkbox" type="checkbox" <?php echo $dropdown; ?> id="<?php echo $this->get_field_id('dropdown'); ?>" name="<?php echo $this->get_field_name('dropdown'); ?>" /> <label for="<?php echo $this->get_field_id('dropdown'); ?>"><?php _e('Display as a drop down'); ?></label>
		</p>
<?php
	}
}
 
?>

Now rename:

9
class WP_Widget_Archives extends WP_Widget {

to:

9
class MyTheme_Widget_Archives extends WP_Widget {

We did this because we are effectively creating a new widget, so if we want to use the old “Archives” widget, we can.

Now change the following function from:

11
12
13
14
	function WP_Widget_Archives() {
		$widget_ops = array('classname' => 'widget_archive', 'description' => __( 'A monthly archive of your blog&#8217;s posts') );
		$this->WP_Widget('archives', __('Archives'), $widget_ops);
	}

to:

11
12
13
14
	function MyTheme_Widget_Archives() {
		$widget_ops = array('classname' => 'widget_archive', 'description' => __( 'A monthly archive of your blog&#8217;s posts') );
		$this->WP_Widget('mytheme_archives', __('MyTheme Archives'), $widget_ops);
	}

Notice that we not only changed the function name, but we changed the parameters to the WP_Widget call. We did this for the same reason explained above (this is a “new” widget, and we want to be able to switch back to the old version).

 

For this next step, we are going to modify the widget(), form(), and update() calls. These are the functions that display the actual widget in the sidebar, and also display the widget details in the WordPress admin console. For this new widget, I am always going to display the post count, and I’m never going to display the archive results as a dropdown, so I can remove a few of the variables that are in the default archive widget ($c, $count, $d, $dropdown). To achieve this, you can modify functions-widgets.php like so:

Before changes:

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
	function widget( $args, $instance ) {
		extract($args);
		$c = $instance['count'] ? '1' : '0';
		$d = $instance['dropdown'] ? '1' : '0';
		$title = apply_filters('widget_title', empty($instance['title']) ? __('Archives') : $instance['title']);
 
		echo $before_widget;
		if ( $title )
			echo $before_title . $title . $after_title;
 
		if ( $d ) {
?>
		<select name="archive-dropdown" onchange='document.location.href=this.options[this.selectedIndex].value;'> <option value=""><?php echo esc_attr(__('Select Month')); ?></option> <?php wp_get_archives(apply_filters('widget_archives_dropdown_args', array('type' => 'monthly', 'format' => 'option', 'show_post_count' => $c))); ?> </select>
<?php
		} else {
?>
		<ul>
		<?php wp_get_archives(apply_filters('widget_archives_args', array('type' => 'monthly', 'show_post_count' => $c))); ?>
		</ul>
<?php
		}
 
		echo $after_widget;
	}
 
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;
		$new_instance = wp_parse_args( (array) $new_instance, array( 'title' => '', 'count' => 0, 'dropdown' => '') );
		$instance['title'] = strip_tags($new_instance['title']);
		$instance['count'] = $new_instance['count'] ? 1 : 0;
		$instance['dropdown'] = $new_instance['dropdown'] ? 1 : 0;
 
		return $instance;
	}
 
	function form( $instance ) {
		$instance = wp_parse_args( (array) $instance, array( 'title' => '', 'count' => 0, 'dropdown' => '') );
		$title = strip_tags($instance['title']);
		$count = $instance['count'] ? 'checked="checked"' : '';
		$dropdown = $instance['dropdown'] ? 'checked="checked"' : '';
?>
		<p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /></p>
		<p>
			<input class="checkbox" type="checkbox" <?php echo $count; ?> id="<?php echo $this->get_field_id('count'); ?>" name="<?php echo $this->get_field_name('count'); ?>" /> <label for="<?php echo $this->get_field_id('count'); ?>"><?php _e('Show post counts'); ?></label>
			<br />
			<input class="checkbox" type="checkbox" <?php echo $dropdown; ?> id="<?php echo $this->get_field_id('dropdown'); ?>" name="<?php echo $this->get_field_name('dropdown'); ?>" /> <label for="<?php echo $this->get_field_id('dropdown'); ?>"><?php _e('Display as a drop down'); ?></label>
		</p>
<?php
	}
}
 
?>

After changes:

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
	function widget( $args, $instance ) {
		extract($args);
		$title = apply_filters('widget_title', empty($instance['title']) ? __('Archives') : $instance['title']);
 
		echo $before_widget;
		if ( $title )
			echo $before_title . $title . $after_title;
 ?>
		<ul>
			// We will complete this in the next step
		</ul>
<?php
		}
 
		echo $after_widget;
	}
 
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;
		$instance['title'] = strip_tags($new_instance['title']);
 
		return $instance;
	}
 
	function form( $instance ) {
		//Defaults
		$instance = wp_parse_args( (array) $instance, array( 'title' => '') );
		$title = esc_attr( $instance['title'] );
?>
		<p>
			<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e( 'Title:' ); ?></label>
			<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" />
		</p>
<?php
	}
}
 
?>

Now there’s one final step. We need to actually display the archives. To do that, we need to modify the widget() function. In the last step, we were left with the following line:

23
24
25
		<ul>
			// We will complete this in the next step
		</ul>

We are now going to complete this block of code. Open up “/wp-includes/general-template.php” and copy lines 799-825. Paste them into line 24 of functions-widgets.php which will give you:

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
		<ul>
		if ( 'monthly' == $type ) {
			$query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit";
			$key = md5($query);
			$cache = wp_cache_get( 'wp_get_archives' , 'general');
			if ( !isset( $cache[ $key ] ) ) {
				$arcresults = $wpdb->get_results($query);
				$cache[ $key ] = $arcresults;
				wp_cache_add( 'wp_get_archives', $cache, 'general' );
			} else {
				$arcresults = $cache[ $key ];
			}
			if ( $arcresults ) {
				$afterafter = $after;
				foreach ( (array) $arcresults as $arcresult ) {
					$url = get_month_link( $arcresult->year, $arcresult->month );
					$text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($arcresult->month), $arcresult->year);
					if ( $show_post_count )
						$after = '&nbsp;('.$arcresult->posts.')' . $afterafter;
					$output .= get_archives_link($url, $text, $format, $before, $after);
				}
			}
		</ul>

The first thing you’ll notice is the if ( ‘monthly’ == $type ) statement. We can remove this clause, because the “custom archive plugin” will only show archives by month (I wanted to keep it simple- no need for other display types). Also, this code is using a few global variables ($wpdb, $wp_locale) that aren’t yet being used properly in our version of the widget() function. We need to modify functions-widgets.php again to have the following line which will declare our global variables for use inside the function:

15
16
17
	function widget( $args, $instance ) {
		global $wpdb, $wp_locale;
		extract($args);

Also, when we copied code the first time, we didn’t copy a few of the variables being used ($where and $join). Modify functions-widgets.php like so (adding lines 25-27):

24
25
26
27
28
29
		<ul>
		//filters
		$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
		$join = apply_filters('getarchives_join', "", $r);
 
		$query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit";

Another thing about the code we copied: we can ignore the $post_count variable (since we are *always* showing post counts). We also want to make sure the post count shows up inside our anchor tag. To accomplish that, we can change:

$url = get_month_link( $arcresult->year, $arcresult->month );
$text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($arcresult->month), $arcresult->year);
if ( $show_post_count )
	$after = '&nbsp;('.$arcresult->posts.')' . $afterafter;
$output .= get_archives_link($url, $text, $format, $before, $after);

to:

$url = get_month_link( $arcresult->year, $arcresult->month );
$text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($arcresult->month), $arcresult->year);
$text .= '&nbsp;('.$arcresult->posts.')' . $afterafter;
$output .= get_archives_link($url, $text, $format, '<li>', '</li>');

Also add the correct output line before the widget() function ends:

		echo '<ul>' . $output . '</ul>';
		echo $after_widget;

Our hacked/modified version is almost complete. The last thing I’m going to do is register the new archive widget (and unregister the default version). Do so by adding the following mytheme_widget_init() function to functions-widgets.php, then calling it:

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
 * Registering MyTheme Widgets
 * Removing a few default WordPress Widgets
 *
 * @since 1.0
 */
function mytheme_widget_init() {
	// unregister some default widgets
	unregister_widget('WP_Widget_Archives');
 
	// register my own widgets
	register_widget('MyTheme_Widget_Archives');
}
 
add_action('widgets_init', 'mytheme_widget_init');

After completing the above steps, your post count will show up inside the anchor tag. One final (optional) step is modifying the style.css file so archive links are highlighted when hovering. For the “classic-modified” theme, I’m going to add:

371
372
373
374
375
376
377
378
379
380
381
382
383
384
/* Custom styles for archive widget */
.widget_archive,
.widget_archive ul,
.widget_archive ul li,
.widget_archive ul li ul,
.widget_archive ul li ul li { border:0px; margin:0px; padding:0px; list-style: none; display:block; }
.widget_archive ul li a,
.widget_archive ul li ul li a { display: block; border-bottom: 1px dotted #666; margin:0px; padding:0px; }
.widget_archive ul li a,
.widget_archive ul li a:link,
.widget_archive ul li a:visited,
.widget_archive ul li a:hover,
.widget_archive ul li a:active { text-decoration:none; }
.widget_archive ul li a:hover { background:#eee; }

Comparison of default archive widget behavior vs. modified version

BEFORE (default archive widget)

BEFORE (default archive widget)

AFTER (modified archive widget)

AFTER (modified archive widget)



5 Responses to Getting WordPress Post Counts to show up inside anchor tag

  1. skratchdot.com says:

    There’s probably a better way to override the default widget behavior, but after searching around for a little bit, I couldn’t find a way (so I came up with this quick/dirty/hacky workaround).

  2. susan says:

    you rock. i’ve been searching and searching and searching. thank you!

  3. Thanks for this, I should have thought of doing this in the first place rather than settling for second best.

    Keep up the good work.

  4. Hey buddy,

    Working on from this tutorial, I chose another method, I created a new Walker class and modified the walker code to make the count show up inside the link. Then used a filter to grab the arguments just before it was passed to the wp_list_categories() method.

    Note: I was trying to modify a categories widget.

    Cheers

  5. skratchdot.com says:

    Thanks for the feedback! It sounds like the path you took actually makes more sense. When I wrote this back in 2009, I didn’t have much experience w/ WordPress themes, and couldn’t find any links explaining how to make (what I thought should be) this easy change.

    I took this quick route just to get something out there (I’m not even using that theme anymore). I definitely don’t like the idea of copy-n-pasting a bunch of code to make small tweaks. I should have taken a more OOP approach.

    Anyways, thanks again for the comments!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">