Advertisement

A Better Forum List Widget for bbPress

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

When bbPress was still a standalone installation, I had tried it out and wasn't really impressed. Things were clunky and it didn't always work the way it was supposed to. After languishing for a few years, Automattic decided to take bbpress and turn it into a plugin, improving the functionality leaps and bounds and making it a strong contender amongst other forum option for WordPress.

The great thing about bbPress is that it works with most themes and automatically skins itself through the theme's CSS. You may have to change a few things here or there to get it to look how you want, but it's pretty much an out of the box solution. The only issue I had was that the Forum List widget didn't actually look like the one in the bbPress support forum.

Forum List widget on bbPress support forum
Forum List widget on bbPress support forum

For some reason, the Forum List widget that comes with bbPress doesn't have the topic count. And it doesn't work that great with forum categories. In the end, I decided to put together a little plugin to add in those features.


The Humble Beginning of a Plugin

If you've ever created a plugin before, you'll know what this little section is for:

<?php
/*
Plugin Name: Better Forum List Widget for bbPress
Description: The default bbPress Forum List widget is pretty bare bones. This plugin adds a topic count and organizes the forum categories differently.
Author: c.bavota
Version: 1.0.0
Author URI: http://www.bavotasan.com/
*/
// TODO ?>

If not, that's where you add the plugin name and the required information so that it'll appear correctly on the Plugin admin page of your WordPress installation.


The Widget API

Whenever you create a widget, you need to extend the Widget API. If that sentence doesn't make any sense to you, don't worry. It just means that you need to put a few things in place in order for your widget to work.

This is what the basic Widget API code looks like:

class BBP_Forums_Topic_Count_Widget extends WP_Widget {

	public function __construct() {
		// widget actual processes
	}

	public function widget( $args, $instance ) {
		// outputs the content of the widget
	}

	public function form( $instance ) {
		// outputs the options form on admin
	}

	public function update( $new_instance, $old_instance ) {
		// processes widget options to be saved
	}

}

This tutorial isn't really about the ins and outs of the Widget API so I suggest taking a look at the Widgets API in the WordPress Codex.


Extending the Widget API

Let's go through the API code one block at a time. First, you need to add in some basic information about the widget:

public function __construct() {
	$widget_ops = array( 'classname' => 'bbp_forums_topic_count_widget_options', 'description' => __( 'A list of categorized forums with their topic count' ) );
	parent::__construct( false, __( '(bbPress) Forums List with Topic Count' ), $widget_ops );
}

Next comes the display code for how the widget will appear on the front end. You need to use wp_list_pages() to organize the categorized forums list. You also need to include a walker to organize the list but I'll get into that a bit later.

public function widget( $args, $instance ) {
	extract( $args );
	$title = apply_filters( 'bbp_forum_widget_title', $instance['title'] );

	echo $before_widget;

	if ( ! empty( $title ) )
		echo $before_title . $title . $after_title;
	?>
	<ul>
		<?php
		wp_list_pages( array(
			'title_li' => '',
			'post_type' => bbp_get_forum_post_type(),
			'sort_column' => 'menu_order',
			'walker' => new Forum_List_Walker,
			'no_found_rows' => true,
		) );
		?>
	</ul>
	<?php
	echo $after_widget;
}
// TODO ?>

There's only one option in the widget so the admin form just needs the following:

public function form( $instance ) {
	$title = empty( $instance['title'] ) ? '' : esc_attr( $instance['title'] );
	?>
	<p>
		<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?>
			<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; ?>" />
		</label>
	</p>
	<?php
}
// TODO ?>

With only one input option, the update block just requires a simple check:

public function update( $new_instance, $old_instance ) {
	$instance = $old_instance;
	$instance['title'] = strip_tags( $new_instance['title'] );

	return $instance;
}

Registering the Widget

You've extended the API so now you need to make sure to register the widget or you won't be able to use it at all.

add_action( 'widgets_init', function() {
	register_widget( 'BBP_Forums_Topic_Count_Widget' );
} );

Taking It for a Walk

In order to make sure that your categorized forums appear correctly in the widget, you need to use an advanced walker. You called a walker instance in the wp_list_pages() function above so that you can go through the forums and the categories to make sure they all fall into place.

I suggest reading up on the walker class since it is a little complicated. Thankfully I managed to crack it to get what I needed for the plugin.

class Forum_List_Walker extends Walker {
	var $tree_type = 'page';
	var $db_fields = array (
		'parent' => 'post_parent',
		'id' => 'ID'
	);

	function start_lvl( &$output, $depth = 0, $args = array() ) {
		$indent = str_repeat( "\t", $depth );
		$output .= "\n$indent<ul class='children'>\n";
	}

	function end_lvl( &$output, $depth = 0, $args = array() ) {
		$indent = str_repeat( "\t", $depth );
		$output .= "$indent</ul>\n";
	}

	function start_el( &$output, $page, $depth, $args ) {
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

		extract( $args, EXTR_SKIP );
		$css_class = array();
		$has_children = forum_list_widget_has_children( $page->ID );
		if ( ! bbp_is_single_user() && $current_page = get_the_ID() ) {
			$_current_page = get_post( $current_page );
			if ( in_array( $page->ID, $_current_page->ancestors ) )
				$css_class[] = 'current_forum_ancestor';
			if ( $has_children )
				$css_class[] = 'forum_category';
			if ( $page->ID == $current_page )
				$css_class[] = 'current_forum_item';
			elseif ( $_current_page && $page->ID == $_current_page->post_parent )
				$css_class[] = 'current_forum_parent';
		} elseif ( $page->ID == get_option( 'page_for_posts' ) ) {
			$css_class[] = 'current_forum_parent';
		}

		$css_class = implode( ' ', $css_class );
		$topic_count = ( $has_children ) ? '' : '<span class="topic-count">' . bbp_get_forum_topic_count( $page->ID ) . '</span>';
		$forum_item = ( $has_children ) ? '<span class="forum_category_title">' . apply_filters( 'the_title', $page->post_title, $page->ID ) . '</span>' . $topic_count : '<a href="' . get_permalink( $page->ID ) . '">' . apply_filters( 'the_title', $page->post_title, $page->ID ) . $topic_count . '</a>';

		$output .= $indent . '<li class="' . $css_class . '">' . $forum_item;
	}

	function end_el( &$output, $page, $depth = 0, $args = array() ) {
		$output .= "</li>\n";
	}

}

The walker also relies on a small conditional named forum_list_widget_has_children() which checks to see if a page has sub-pages.

function forum_list_widget_has_children( $page_id ) {
	$children = get_pages( array(
		'child_of' => $page_id,
		'post_type' => bbp_get_forum_post_type(),
	) );
	if ( $children )
		return true;
	else
		return false;
}

Let's Add Some Style

It only takes a little bit of CSS to make things look good.

.forum_category_title {
	font-size: 1.2em;
	padding: 5px 0 0;
	display: block;
	font-weight: bold;
	}

.topic-count {
	float: right;
	}

.bbp_forums_topic_count_widget_options a {
	text-decoration: none;
	}

	.bbp_forums_topic_count_widget_options ul.children {
		padding: 0 0 0 5px;
		margin: 0;
		}

You also have to make sure that you hook into the appropriate action in order to display the stylesheet on the front end.

add_action( 'wp_enqueue_scripts', 'forum_list_widget_styles' );
function forum_list_widget_styles() {
	wp_enqueue_style( 'forum_list_widget_styles', plugins_url( '/css/style.css', __FILE__ ) );
}

Conclusion

If you want to see the whole she-bang together in a plugin, you can download it above. Install it and add the new Better Forum List widget to your sidebar and you'll see something like this:

The final result
The final result

Now your list will be better organized and display the total topic count for each forum.

If you have any comments or feedback on anything you read above, please feel free to discuss it below.

Advertisement