Get $500+ of the best After Effects files, video templates and music for only $20!
Creating a Tabbed Widget for Custom Post Types

Creating a Tabbed Widget for Custom Post Types

Tutorial Details
  • Technology: Wordpress, jQuery UI
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 minutes

Custom post types give you unlimited possibilities for creating content. The trick is trying to present it all to your users. We’re talking bounce rate here, and one of the best ways to decrease your bounce rate and keep users on your site is to present as much content as possible in a clean and organized manner. This tutorial offers one solution by showing you how to create a custom, tabbed widget that grabs your latest articles from multiple custom post types, and as a bonus, shows you how to display star ratings next to each post!


Overview. Our Plan Of Attack

This tutorial explains how to modify an existing theme (not how to create a plugin) by adding new custom post types, and then creating a custom widget that tabs between the new custom post types using a fancy jQuery transition. Additionally, you will learn how to use custom meta fields on the new custom post types to assign a star rating which will show up in the custom widget we create.

Finally, the widget will be able to sort the custom post types by either date or rating. Here is a blueprint of our final product and where each of the elements come from:

There are only three files that you will need to modify in order to add this functionality to your theme, all of which are standard files that come with virtually all WP themes:

  1. functions.php
  2. header.php
  3. style.css

I will be using the default WP 3.0 theme called TwentyTen in this tutorial, but the method will be the same for any theme that you want to modify – you will still edit (or create if they don’t exist) these three files no matter which theme you are modifying/creating. The custom post types I’ll be creating are Movie Reviews, Music Reviews, and Game Reviews:


Step 1. Create The Custom Post Types

Before we build the widget that will display our reviews, we must first create the reviews themselves, and to do that we’ll need to create three new custom post types. This tutorial assumes you are familiar with creating custom post types, so if you need to brush up on that before you proceed, the Codex explains how to register new post types in detail. Now we will create our Movie Reviews post type. Open the functions.php file and add the following code at the end:

//##########################################
//create custom post type of Movie Reviews
//##########################################

function create_oswc_movie_reviews() {
	register_post_type( 'oswc_movie_reviews',
		array(
			'labels' => array(
				'name' => __( 'Movie Reviews' , 'twentyten'),
				'singular_name' => __( 'Movie Review' , 'twentyten'),
				'add_new' => __('Add new review', 'twentyten'),
				'edit_item' => __('Edit review', 'twentyten'),
				'new_item' => __('New review', 'twentyten'),
				'view_item' => __('View review', 'twentyten'),
				'search_items' => __('Search reviews', 'twentyten'),
				'not_found' => __('No reviews found', 'twentyten'),
				'not_found_in_trash' => __('No reviews found in Trash', 'twentyten')
			),
			'public' => true,
			'menu_position' => 25,
			'rewrite' => array('slug' => 'movie-review'),
			'supports' => array('title','editor','author','thumbnail','excerpt','trackbacks','custom-fields','comments','revisions'),
			'taxonomies' => array('category', 'post_tag')
		)
	);
}
add_action( 'init', 'create_oswc_movie_reviews' );

This creates a post type called oswc_movie_reviews, tells WP what all the labels of the post type should be, makes the post type public, puts it right after the Comments menu in the WP admin menu structure, rewrites the slug of the post type (prettier in permalinks), tells WP to enable certain attributes for the post type (such as a thumbnail and an excerpt), and finally tells WP to let the user assign categories and tags to the post type just like regular posts.

For the sake of brevity we will not define any custom taxonomies, since the widget we’ll be creating does not interact with taxonomies in any way. In a real-world situation, you would likely create cush custom taxonomies as Movie Genre, Movie Director, Actors, etc. To see how to create custom taxonomies, refer to the WordPress Codex linked to above.

“When writing your own functions and variables, it’s always a good idea to use a prefix in the name just to make sure it doesn’t share the same name as another WP or third-party plugin functions or variables. You’ll notice this tutorial either prefaces, or includes within all function and variable names, the string oswc_.”

Next, let’s create the other two custom post types we’ll use in this widget. The logic is exactly the same as the Movie Reviews type we just added. All we do is change the code to reflect Music Reviews and Game Reviews, respectively. Add this code after the code you added above:

//##########################################
//create custom post type of Music Reviews
//##########################################

function create_oswc_music_reviews() {
	register_post_type( 'oswc_music_reviews',
		array(
			'labels' => array(
				'name' => __( 'Music Reviews', 'twentyten' ),
				'singular_name' => __( 'Music Review', 'twentyten' ),
				'add_new' => __('Add new review', 'twentyten'),
				'edit_item' => __('Edit review', 'twentyten'),
				'new_item' => __('New review', 'twentyten'),
				'view_item' => __('View review', 'twentyten'),
				'search_items' => __('Search reviews', 'twentyten'),
				'not_found' => __('No reviews found', 'twentyten'),
				'not_found_in_trash' => __('No reviews found in Trash', 'twentyten')
			),
			'public' => true,
			'menu_position' => 26,
			'rewrite' => array('slug' => 'music-review'),
			'supports' => array('title','editor','author','thumbnail','excerpt','trackbacks','custom-fields','comments','revisions'),
			'taxonomies' => array('category', 'post_tag')
		)
	);
}
add_action( 'init', 'create_oswc_music_reviews' );

//##########################################
//create custom post type of Game Reviews
//##########################################

function create_oswc_game_reviews() {
	register_post_type( 'oswc_game_reviews',
		array(
			'labels' => array(
				'name' => __( 'Game Reviews', 'twentyten' ),
				'singular_name' => __( 'Game Review', 'twentyten' ),
				'add_new' => __('Add new review', 'twentyten'),
				'edit_item' => __('Edit review', 'twentyten'),
				'new_item' => __('New review', 'twentyten'),
				'view_item' => __('View review', 'twentyten'),
				'search_items' => __('Search reviews', 'twentyten'),
				'not_found' => __('No reviews found', 'twentyten'),
				'not_found_in_trash' => __('No reviews found in Trash', 'twentyten')
			),
			'public' => true,
			'menu_position' => 27,
			'rewrite' => array('slug' => 'game-review'),
			'supports' => array('title','editor','author','thumbnail','excerpt','trackbacks','custom-fields','comments','revisions'),
			'taxonomies' => array('category', 'post_tag')
		)
	);
}
add_action( 'init', 'create_oswc_game_reviews' );

Upload your modified functions.php file and you will now see menu items for all three custom post types. Your admin panel should look like this:


Step 2. Write Some Reviews

The Review

Now that we have enabled the option to add reviews, let’s write some! Click Add new review under the Movie Reviews menu and write the review just like you would write a normal post. Give it a title, post content, categories, tags, and a featured image.

The Rating

So what do we need to do so that the review has a star rating that shows up in the widget? Simple: add a Custom Field. First, make sure you have the Custom Fields panel enabled in your Screen Options toolbox by toggling the Screen Options tab and selecting Custom Fields:

With the custom fields visible, add a new custom field named Rating. Give it a number value from 0 through 5, incrementing by .5. So, the available values would be 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5.

“The only part you need to add for this widget to work is the title and the custom field, everything else is there for example, and what you add depends on how you want to setup your specific site.”

Add reviews the exact same way for the other two review types. Add at least a few reviews for each of the three types so that the widget will have something to display. With our content in place, now we’re ready to build the widget that will display our reviews. We will attack the widget in three phases: the PHP, then the jQuery, and finally the CSS.


Step 3. Build the Custom Widget PHP

This is the most involved step of the tutorial. In this step, we will write the PHP code that creates and adds our custom widget to WordPress, and then write a custom PHP function that takes care of displaying the star ratings for each review.

The Custom Widget

There are four functions we need to add when extending the WP_Widget class in order to add our widget. Here is the structure of the code we will use to extend the class:

class My_Widget extends WP_Widget {
	function My_Widget() {
		// widget actual processes
	}

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

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

    function form($instance) {
		// outputs the options form on admin
	}
}
register_widget('My_Widget');

As you can see, we need a function that names and creates the widget, a function that controls how it displays on the front end, a function that handles updating the widget options, and a function that controls how it displays in the admin panel (the WordPress >> Appearance >> Widgets page). After we do all that, we register the widget with WordPress.

For details on developing widgets, see the Codex, which further explains the WordPress Widgets API.

Let’s build out each function and make everything unique to our custom widget. First, we wrap it all in a class with our widget name:

class oswc_tabbed_latest_reviews extends WP_Widget {
	// ...widget functions...
}

Then we add the first function inside of that class. This function has the same name as the class, which is the name of our widget. We will be setting up the nice name of our widget that displays on the widgets page, as well as some default settings for the widget:

function oswc_tabbed_latest_reviews() {
	// widget settings
	$widget_ops = array( 'classname' => 'Latest Review Tabs', 'description' => 'Displays reviews by most recent or highest rated in a jQuery tabbed format.' );
	// widget control settings
	$control_ops = array( 'width' => 250, 'height' => 350, 'id_base' => 'oswc_tabbed_latest_reviews' );
	// create the widget
	$this->WP_Widget( 'oswc_tabbed_latest_reviews', 'Latest Review Tabs', $widget_ops, $control_ops );
}

Now let’s add the next function. First, we grab the variables from our widget (which we’ll setup in the next function), and then create a variable to see which way we’re sorting the widget:

function widget( $args, $instance ) {

	extract( $args );

	/* User-selected settings. */
	$sort = $instance['sort'];
	$showmovies = $instance['showmovies'];
	$showmusic = $instance['showmusic'];
	$showgames = $instance['showgames'];
	$nummovies = $instance['nummovies'];
	$nummusic = $instance['nummusic'];
	$numgames = $instance['numgames'];

	if($sort=="highest-rated") {
		$feedsort="meta_value";
		$metakey="&meta_key=Rating";
	} else {
		$feedsort="date";
		$metakey="";
	}

Then, we start creating the HTML. Wrap the entire widget in a div with id of “tabbed-reviews” (the jQuery will hook into this later) and then setup a tab for each review type. Finally, another div wraps the content in each tab:

/* HTML output */
?>

<div id="tabbed-reviews">
    <ul class="tabnav">
        <?php if($showmovies) { ?><li><a href="#tabs-movies">Movies</a></li><?php } ?>
        <?php if($showmusic) { ?><li><a href="#tabs-music">Music</a></li><?php } ?>
        <?php if($showgames) { ?><li><a href="#tabs-games">Games</a></li><?php } ?>
    </ul>

    <div class="tabdiv-wrapper">

Inside of the tabdiv-wrapper div we need to create a loop that grabs post titles and star ratings from a specified custom post type. The first tab is for Movie Reviews, so we check to see if that review type is being shown in the widget options, and if so we display a tab with an unordered list inside of it. Here’s the full block of code for the Movie Review tab content, which we’ll pick apart below:

<?php if($showmovies) { ?>

    <div id="tabs-movies" class="tabdiv">
        <ul>
            <?php // setup the query
            $args='&suppress_filters=true&posts_per_page='.$nummovies.'&post_type=oswc_movie_reviews&order=DESC&orderby='.$feedsort.$metakey;
            $cust_loop = new WP_Query($args);
            if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
                // if we're sorting by rating and this item does not have a rating, hide it
                $rating = get_post_meta(get_the_ID(), "Rating", $single = true);
                if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
                    ?>
                    <li>																					

                        <?php oswc_show_rating($rating); // show the stars ?>	

                        <a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

                    </li>

                <?php } ?>

            <?php endwhile;
            endif;
            wp_reset_query(); ?> 

            <li class="last gentesque tooltip" title="View all movie reviews"><a href="movie-reviews/">More</a></li>

        </ul>
    </div>

<?php } ?>

The heart of that block of code is the custom loop that we create in order to grab posts from the Movie Reviews post type. Here are the arguments we use for the loop:

$args='&suppress_filters=true&posts_per_page='.$nummovies.'&post_type=oswc_movie_reviews&order=DESC&orderby='.$feedsort.$metakey;

First we use suppress_filters just to be sure we only grab the specific post type we want (some themes or plugins can have functionality which injects all loops with all post types – this argument makes sure our widget stays true to its specified post type).

The most important part is the post_type=oswc_movie_reviews, since that tells this loop to only look for the custom post type we created above. Finally, we just plug in our widget options for number of posts and sorting.

To get our stars, we grab the custom field value we added to the review:

$rating = get_post_meta(get_the_ID(), "Rating", $single = true);

And then we pass that value to a function that we haven’t created quite yet:

<?php oswc_show_rating($rating); // show the stars ?>

After the loop, we include a More link. Put the URL to your specific custom post type page in for the link href (creating this page is outside the scope of this tutorial).

<li class="last gentesque tooltip" title="View all movie reviews"><a href="movie-reviews/">More</a></li>

Now we just need to do the same thing for Music Reviews and Game Reviews. Here’s the full block of code for both review types:

<?php if($showmusic) { ?>

    <div id="tabs-music" class="tabdiv">
        <ul>
            <?php // setup the query
            $args='&suppress_filters=true&posts_per_page='.$nummusic.'&post_type=oswc_music_reviews&order=DESC&orderby='.$feedsort.$metakey;
            $cust_loop = new WP_Query($args);
            if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
                // if we're sorting by rating and this item does not have a rating, hide it
                $rating = get_post_meta(get_the_ID(), "Rating", $single = true);
                if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
                    ?>
                    <li>																					

                        <?php oswc_show_rating($rating); // show the stars ?>	

                        <a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

                    </li>

                <?php } ?>

            <?php endwhile;
            endif;
            wp_reset_query(); ?> 

            <li class="last gentesque tooltip" title="View all music reviews"><a href="music-reviews/">More</a></li>

        </ul>
    </div>

<?php } ?>

<?php if($showgames) { ?>

    <div id="tabs-games" class="tabdiv">
        <ul>
            <?php // setup the query
            $args='&suppress_filters=true&posts_per_page='.$numgames.'&post_type=oswc_game_reviews&order=DESC&orderby='.$feedsort.$metakey;
            $cust_loop = new WP_Query($args);
            if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
                // if we're sorting by rating and this item does not have a rating, hide it
                $rating = get_post_meta(get_the_ID(), "Rating", $single = true);
                if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
                    ?>
                    <li>																					

                        <?php oswc_show_rating($rating); // show the stars ?>	

                        <a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

                    </li>

                <?php } ?>

            <?php endwhile;
            endif;
            wp_reset_query(); ?> 

            <li class="last gentesque tooltip" title="View all video game reviews"><a href="game-reviews/">More</a></li>

        </ul>
    </div>

<?php } ?>

Don’t forget to close the div tags we used at the beginning of our widget HTML and close the PHP function!

        </div>

    </div>

<?php
}

Our third function inside of the WP_Widget class is the update function. All this function does is receive the old and new options from the widget when the Save button is clicked, and updates the widget settings array accordingly:

function update( $new_instance, $old_instance ) {
    $instance = $old_instance;

    $sort = $instance['sort'];
    $showmovies = $instance['showmovies'];
    $showmusic = $instance['showmusic'];
    $showgames = $instance['showgames'];
    $nummovies = $instance['nummovies'];
    $nummusic = $instance['nummusic'];
    $numgames = $instance['numgames'];

    /* Strip tags (if needed) and update the widget settings. */
    $instance['sort'] = strip_tags( $new_instance['sort'] );
    $instance['showmovies'] = isset( $new_instance['showmovies'] );
    $instance['showmusic'] = isset( $new_instance['showmusic'] );
    $instance['showgames'] = isset( $new_instance['showgames'] );
    $instance['nummovies'] = strip_tags( $new_instance['nummovies'] );
    $instance['nummusic'] = strip_tags( $new_instance['nummusic'] );
    $instance['numgames'] = strip_tags( $new_instance['numgames'] );

    return $instance;
}

Our fourth and final function for creating our widget sets up how our widget will appear on the admin page. You’ll notice a form field is created for each option that we need to deal with. This code will enable the widget to offer following 7 options:

  1. How to sort the posts
  2. Whether or not to display the Movie Reviews tab
  3. How many Movie Reviews to display
  4. Whether or not to display the Music Reviews tab
  5. How many Music Reviews to display
  6. Whether or not to display the Game Reviews tab
  7. How many Game Reviews to display

Here is the code that sets up our widget admin options:

	function form( $instance ) {

		/* Set up some default widget settings. */
		$defaults = array( 'sort' => 'latest', 'showmovies' => true, 'showmusic' => true, 'showgames' => true, 'nummovies' => 10, 'nummusic' => 10, 'numgames' => 10 );
		$instance = wp_parse_args( (array) $instance, $defaults ); ?>

		<p>
			<input class="radio" type="radio" <?php if($instance['sort']=='highest-rated') { ?>checked <?php } ?>name="<?php echo $this->get_field_name( 'sort' ); ?>" value="highest-rated" />
			Order reviews by highest rated<br />
			<input class="radio" type="radio" <?php if($instance['sort']!='highest-rated') { ?>checked <?php } ?>name="<?php echo $this->get_field_name( 'sort' ); ?>" value="latest" />
			Order reviews by latest
		</p>	

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showmovies']) ? $instance['showmovies'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showmovies' ); ?>" name="<?php echo $this->get_field_name( 'showmovies' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'nummovies' ); ?>" name="<?php echo $this->get_field_name( 'nummovies' ); ?>" value="<?php echo $instance['nummovies']; ?>" style="width:30px" />
			movie reviews                    

		</p>    

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showmusic']) ? $instance['showmusic'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showmusic' ); ?>" name="<?php echo $this->get_field_name( 'showmusic' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'nummusic' ); ?>" name="<?php echo $this->get_field_name( 'nummusic' ); ?>" value="<?php echo $instance['nummusic']; ?>" style="width:30px" />
			music reviews                    

		</p>    

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showgames']) ? $instance['showgames'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showgames' ); ?>" name="<?php echo $this->get_field_name( 'showgames' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'numgames' ); ?>" name="<?php echo $this->get_field_name( 'numgames' ); ?>" value="<?php echo $instance['numgames']; ?>" style="width:30px" />
			game reviews                    

		</p>

		<?php
	}
}

Notice we closed out our entire class at the very end of that function. Now that we’ve created the guts of our widget, let’s not forget to register it with WordPress!

//LOAD THE WIDGET
add_action("widgets_init", "oswc_load_widgets");
function oswc_load_widgets()
{
	register_widget('oswc_tabbed_latest_reviews');
}

Here is the full code block which is used to create the widget for your convenience:

//BUILD THE WIDGET
class oswc_tabbed_latest_reviews extends WP_Widget {
	function oswc_tabbed_latest_reviews() {
		// widget settings
		$widget_ops = array( 'classname' => 'Latest Review Tabs', 'description' => 'Displays reviews by most recent or highest rated in a jQuery tabbed format.' );
		// widget control settings
		$control_ops = array( 'width' => 250, 'height' => 350, 'id_base' => 'oswc_tabbed_latest_reviews' );
		// create the widget
		$this->WP_Widget( 'oswc_tabbed_latest_reviews', 'Latest Review Tabs', $widget_ops, $control_ops );
	}
	function widget( $args, $instance ) {

		extract( $args );

		/* User-selected settings. */
		$sort = $instance['sort'];
		$showmovies = $instance['showmovies'];
		$showmusic = $instance['showmusic'];
		$showgames = $instance['showgames'];
		$nummovies = $instance['nummovies'];
		$nummusic = $instance['nummusic'];
		$numgames = $instance['numgames'];

		if($sort=="highest-rated") {
			$feedsort="meta_value";
			$metakey="&meta_key=Rating";
		} else {
			$feedsort="date";
			$metakey="";
		}

		/* HTML output */
		?>

		<div id="tabbed-reviews">
			<ul class="tabnav">
				<?php if($showmovies) { ?><li><a href="#tabs-movies">Movies</a></li><?php } ?>
				<?php if($showmusic) { ?><li><a href="#tabs-music">Music</a></li><?php } ?>
				<?php if($showgames) { ?><li><a href="#tabs-games">Games</a></li><?php } ?>
			</ul>

			<div class="tabdiv-wrapper">

				<?php if($showmovies) { ?>

					<div id="tabs-movies" class="tabdiv">
						<ul>
							<?php // setup the query
							$args='&suppress_filters=true&posts_per_page='.$nummovies.'&post_type=oswc_movie_reviews&order=DESC&orderby='.$feedsort.$metakey;
							$cust_loop = new WP_Query($args);
							if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
								// if we're sorting by rating and this item does not have a rating, hide it
								$rating = get_post_meta(get_the_ID(), "Rating", $single = true);
								if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
									?>
									<li>																					

										<?php oswc_show_rating($rating); // show the stars ?>	

										<a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

									</li>

								<?php } ?>

							<?php endwhile;
							endif;
							wp_reset_query(); ?> 

							<li class="last gentesque tooltip" title="View all movie reviews"><a href="movie-reviews/">More</a></li>

						</ul>
					</div>

				<?php } ?>

				<?php if($showmusic) { ?>

					<div id="tabs-music" class="tabdiv">
						<ul>
							<?php // setup the query
							$args='&suppress_filters=true&posts_per_page='.$nummusic.'&post_type=oswc_music_reviews&order=DESC&orderby='.$feedsort.$metakey;
							$cust_loop = new WP_Query($args);
							if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
								// if we're sorting by rating and this item does not have a rating, hide it
								$rating = get_post_meta(get_the_ID(), "Rating", $single = true);
								if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
									?>
									<li>																					

										<?php oswc_show_rating($rating); // show the stars ?>	

										<a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

									</li>

								<?php } ?>

							<?php endwhile;
							endif;
							wp_reset_query(); ?> 

							<li class="last gentesque tooltip" title="View all music reviews"><a href="music-reviews/">More</a></li>

						</ul>
					</div>

				<?php } ?>

				<?php if($showgames) { ?>

					<div id="tabs-games" class="tabdiv">
						<ul>
							<?php // setup the query
							$args='&suppress_filters=true&posts_per_page='.$numgames.'&post_type=oswc_game_reviews&order=DESC&orderby='.$feedsort.$metakey;
							$cust_loop = new WP_Query($args);
							if ($cust_loop->have_posts()) : while ($cust_loop->have_posts()) : $cust_loop->the_post(); $postcount++;
								// if we're sorting by rating and this item does not have a rating, hide it
								$rating = get_post_meta(get_the_ID(), "Rating", $single = true);
								if(($rating && $feedsort=="meta_value") || ($feedsort!="meta_value")) {
									?>
									<li>																					

										<?php oswc_show_rating($rating); // show the stars ?>	

										<a class="post-title" href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>												

									</li>

								<?php } ?>

							<?php endwhile;
							endif;
							wp_reset_query(); ?> 

							<li class="last gentesque tooltip" title="View all video game reviews"><a href="game-reviews/">More</a></li>

						</ul>
					</div>

				<?php } ?>

			</div>

		</div>

	<?php
	}
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;

		$sort = $instance['sort'];
		$showmovies = $instance['showmovies'];
		$showmusic = $instance['showmusic'];
		$showgames = $instance['showgames'];
		$nummovies = $instance['nummovies'];
		$nummusic = $instance['nummusic'];
		$numgames = $instance['numgames'];

		/* Strip tags (if needed) and update the widget settings. */
		$instance['sort'] = strip_tags( $new_instance['sort'] );
		$instance['showmovies'] = isset( $new_instance['showmovies'] );
		$instance['showmusic'] = isset( $new_instance['showmusic'] );
		$instance['showgames'] = isset( $new_instance['showgames'] );
		$instance['nummovies'] = strip_tags( $new_instance['nummovies'] );
		$instance['nummusic'] = strip_tags( $new_instance['nummusic'] );
		$instance['numgames'] = strip_tags( $new_instance['numgames'] );

		return $instance;
	}
	function form( $instance ) {

		/* Set up some default widget settings. */
		$defaults = array( 'sort' => 'latest', 'showmovies' => true, 'showmusic' => true, 'showgames' => true, 'nummovies' => 10, 'nummusic' => 10, 'numgames' => 10 );
		$instance = wp_parse_args( (array) $instance, $defaults ); ?>

		<p>
			<input class="radio" type="radio" <?php if($instance['sort']=='highest-rated') { ?>checked <?php } ?>name="<?php echo $this->get_field_name( 'sort' ); ?>" value="highest-rated" />
			Order reviews by highest rated<br />
			<input class="radio" type="radio" <?php if($instance['sort']!='highest-rated') { ?>checked <?php } ?>name="<?php echo $this->get_field_name( 'sort' ); ?>" value="latest" />
			Order reviews by latest
		</p>	

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showmovies']) ? $instance['showmovies'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showmovies' ); ?>" name="<?php echo $this->get_field_name( 'showmovies' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'nummovies' ); ?>" name="<?php echo $this->get_field_name( 'nummovies' ); ?>" value="<?php echo $instance['nummovies']; ?>" style="width:30px" />
			movie reviews                    

		</p>    

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showmusic']) ? $instance['showmusic'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showmusic' ); ?>" name="<?php echo $this->get_field_name( 'showmusic' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'nummusic' ); ?>" name="<?php echo $this->get_field_name( 'nummusic' ); ?>" value="<?php echo $instance['nummusic']; ?>" style="width:30px" />
			music reviews                    

		</p>    

		<p>
			<input class="checkbox" type="checkbox" <?php checked(isset( $instance['showgames']) ? $instance['showgames'] : 0  ); ?> id="<?php echo $this->get_field_id( 'showgames' ); ?>" name="<?php echo $this->get_field_name( 'showgames' ); ?>" />
			Display
			<input id="<?php echo $this->get_field_id( 'numgames' ); ?>" name="<?php echo $this->get_field_name( 'numgames' ); ?>" value="<?php echo $instance['numgames']; ?>" style="width:30px" />
			game reviews                    

		</p>

		<?php
	}
}
//LOAD THE WIDGET
add_action("widgets_init", "oswc_load_widgets");
function oswc_load_widgets()
{
	register_widget('oswc_tabbed_latest_reviews');
}

The Star Rating Function

That’s it for our widget. One last thing to do in our functions.php file: create the function that we called above which outputs the HTML for the star rating. Here’s the function you should add below the widget code that you added above:

//html display of stars
function oswc_show_rating($rating) {
	$output = '<div class="stars">';
	$output .= '<div class="star';
	if($rating>=1) {
		$output .= ' full';
	} elseif($rating>0) {
		$output .= ' half';
	}
	$output .= '"> </div>';
	$output .= '<div class="star';
	if($rating>=2) {
		$output .= ' full';
	} elseif($rating>1) {
		$output .= ' half';
	}
	$output .= '"> </div>';
	$output .= '<div class="star';
	if($rating>=3) {
		$output .= ' full';
	} elseif($rating>2) {
		$output .= ' half';
	}
	$output .= '"> </div>';
	$output .= '<div class="star';
	if($rating>=4) {
		$output .= ' full';
	} elseif($rating>3) {
		$output .= ' half';
	}
	$output .= '"> </div>';
	$output .= '<div class="star';
	if($rating>=5) {
		$output .= ' full';
	} elseif($rating>4) {
		$output .= ' half';
	}
	$output .= '"> </div>';
	$output .= '</div>';
	echo $output;
}

“The star images this widget uses are contained in the download file for this tutorial.”

This function accepts the rating value (a number between but including 0 and 5) and uses if…else statements to check and see how many stars to make yellow and how many stars to make grey. It displays a div with class “star” five times in a row, and each time it checks the rating and compares it to which star it’s displaying, and adds either a class of “full” if the rating is greater than or equal to the current star, or a class of “half” if the rating is greater than the previous star.

Confused by the half star logic? It simply means if the star is greater than the previous star, but not greater than or equal to this star, this must be the half star.

Note: the stars are all inside a div with class “stars”, which is what we’ll target to apply our styles later.


Step 4. Setup The Widget

Now that we have done all the work of coding our widget, WordPress will recognize it on the widgets page, and we can set it up to appear in our sidebar. Go to Appearance >> Widgets and drag our new widget, called Latest Review Tabs, into one of the sidebars. We will use the Primary Widget Area in the TwentyTen theme. Expand the widget settings to see the available options we setup in the previous step:

Select how you’d like to sort the widget (either by highest rated or latest), select to display each of the three review types, and indicate how many of each review type you’d like to display. Save your options and you will now have a (somewhat) working widget on your site. Of course, we haven’t applied any style yet, so it’s not going to look too pretty until we complete the next two steps: applying jQuery and CSS.


Step 5. Splash In Some jQuery

This widget uses the jQuery UI for tab transitions. So, we need to do three simple things in order to hook up the jQuery UI with our widget: 1) load the jQuery library, 2) load the jQuery UI plugin, and 3) call the jQuery UI tabs function on our widget.

Load the jQuery library

WordPress comes with jQuery already included. Some people prefer to load it from a CDN or even upload the library and use it within their theme. To learn about loading jQuery within WordPress, visit the Codex. To include it in the TwentyTen theme, all we have to do is all one line of code in the header.php file. Open the header.php file and add the following call to jQuery before the wp_head() function (so that would be somewhere before line 51 in the TwentyTen theme):

<?php wp_enqueue_script("jquery"); ?>

Load the jQuery UI plugin

This is the same technique as loading the jQuery library, except we want to call this one after the wp_head() function (so that would be somewhere after line 51 in the TwentyTen theme):

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.5.3/jquery-ui.min.js"></script>

Call the jQuery UI tabs function on our widget

Add the following code directly after the jQuery UI code we just added:

<script type="text/javascript">
	jQuery.noConflict();
	jQuery(document).ready(function() {
		jQuery('#tabbed-reviews > ul').tabs({ fx: { opacity: 'toggle', duration: 200 } });
	});
</script>

We are simply calling the tabs function (which the jQuery UI provides for us) on the unordered list inside of the #tabbed-reviews div tag, and we are applying an opacity toggle on the transitions between the tabs. You can decrease the duration for a faster toggle, or increase it for a slower toggle (think of this number as a delay instead of a speed).


Step 6. Style the Widget Using CSS

If you view your widget right now you probably won’t be very pleased since we have not styled it at all. The last step is to apply some CSS rules which will make our widget actually look like a widget, and function correctly as well. Open your style.css file and add the following style at the end of the file:

/*************************************************
jquery tabs
*************************************************/
#tabbed-reviews a {
	font-family:Arial, Helvetica, sans-serif;
}
#tabbed-reviews a:hover {
	text-decoration:none;
}
ul.tabnav {
	margin:0px !important;
	padding:0px !important;
}
ul.tabnav li {
	display: inline;
	list-style: none;
}
ul.tabnav li a {
	text-decoration: none;
	text-transform: uppercase;
	color: #444;
	font-weight: bold;
	display:inline-block;
	padding: 5px 9px 6px 9px;
	outline: none;
	font-size:.8em;
}
ul.tabnav li a:hover, ul.tabnav li a:active, ul.tabnav li.ui-tabs-selected a {
	color:#FFF;
	background:url(images/sidebar-header-bg.png) repeat-x 0px 0px;
}
.tabdiv-wrapper {
	border:1px solid #CCC;
	margin-bottom:20px;
}
.tabdiv {
	background: #E8E8E8;
	border:1px solid #FFF;
	border-top:0px;
	font-size:1em;
}
.tabdiv ul {
	margin:0px !important;
}
.tabdiv li {
	list-style-type:none;
	list-style-image: none !important;
}
.tabdiv li a {
	display:block;
	padding:10px 10px;
	border-bottom:1px solid #ccc;
	border-top:1px solid #fff;
	color:#444;
}
.tabdiv li a:hover {
	background:#F0F0F0;
	color:#222;
}
.tabdiv li.last a {
	text-align:center;
	font-weight:bold;
	text-transform:uppercase;
	color:#666;
	font-size:1em;
	padding:2px 0px 2px 0px;
	border-bottom:0px;
}
.ui-tabs-hide {
	display: none;
}

/*stars*/
.tabdiv .stars {
	float:right;
	margin-top:3px;
}
.tabdiv .stars {
	margin:10px 5px 0px 0px !important;
}
.tabdiv .star {
	width:14px;
	height:14px;
	float:left;
	margin-left:2px;
	background:url(images/star-empty-small.png) no-repeat 0px 0px;
}
.tabdiv .star.half {
	background:url(images/star-half-small.png) no-repeat 0px 0px;
}
.tabdiv .star.full {
	background:url(images/star-full-small.png) no-repeat 0px 0px;
}

The first part of the style applies to the tabs and the content areas inside of each tab. As you can see, padding, borders, and background colors are given to the different elements that make up the tabs container. The second part of the style applies to the star containers. A background image is used for the three different star states of empty, half, and full, and the stars are floated next to each other so they appear in a horizontal line.


Conclusion

Developing in WordPress is all about leveraging different technologies to present a rich user experience for your site visitors. In this tutorial we used the standard WordPress core features including the custom post types and the custom widgets API, and we expanded upon that by adding in the helpful jQuery library, followed by the jQuery UI plugin, and finally we used the power of CSS to make everything look sleek and professional.

The greatest benefit of ideas such as this widget is that you can present more content to your users without overwhelming them, with the ultimate goal of getting them to stay within your site for as long as possible.

Hopefully this particular widget can help you to decrease that bounce rate and create a more enjoyable experience for your users! If you haven’t already, be sure to take a look at the demo before giving it a try yourself.

Add Comment

Discussion 48 Comments

  1. thanks for this tut! ill take some time for read later xD

  2. LastRose says:

    might want to preventDefault in the javascript, and isn’t there a more elegent way to insert the javascript other than putting it in the theme directly?
    wp_register_script, wp_enqueue_script or something like that?

  3. LastRose says:

    Thanks for this, Was looking at doing something similar myself.

  4. Why wouldn’t you just make a Custom Post type called Reviews and then use the categories there? Seems redundant to separate it like this. I would personally hate the spammed nav.

    • Brian says:
      Author

      The purpose of this tutorial is to show you how to make a tabbed widget that displays your different custom post types. The reason you would not use the same post type for all the reviews is because the reviews have unique taxonomies, which wouldn’t be possible if they were all under the same review type. That’s not really the point of the tutorial anyway… :)

    • Kevin says:

      true it’s similar, but I think Smashing Mag’s article is more about setting up the custom post types

    • Author

      That’s a very nice tut. Thanks for posting. I wrote this tut before that one was published (June 2nd) and mine is based on code I wrote as part of my Continuum theme that I uploaded to ThemeForest on May 1st. Just for the record :)

  5. Martin says:

    Brilliant tut and incidentally exactly what I needed for my upcoming WordPress template :)

    Thanks!

  6. Ravi says:

    Great tutorial. Thank you :)

  7. Vivek Parmar says:

    That’s a great tut. Would be of great of you if you let me know how to show tabbed widgets in thesis wordpress theme?

  8. ihsantypo says:

    I love the tutorial very easy ! but i want to ask
    in wptuts+ we will find tutorial talk about create account on wordpress and expalane all the options that’s available on wordpress !!

  9. Joost says:

    Nice tutorial!

    One thing I would possibly change is to make one sprite with all possible ratings (1, 1.5, 2, …, 5).
    That would make oswc_show_rating a lot simpler.

    function oswc_show_rating($rating) {
    return ” . $rating ‘ Stars’;
    )

    Need to add a few lines in the css to position the sprite, but we’d save two http requests.

    Ah well, just nitpicking. Thanks for the write up!

    • Joost says:

      OK, Something went wrong with the code there.

      function oswc_show_rating($rating) {
      return ‘<div class=”star-’ . str_replace(‘.’, ‘-’, $rating .’”>’ . $rating ‘ Stars</div>’;
      )

      Hope this looks better.

  10. Ashmita says:

    O la la …. great post. heading to my local test for to use it.

  11. Raymond says:

    Thanks! Excellent tut!

  12. Pippin says:

    Very well done, though I’d really have liked to see you use a custom meta box with perhaps a drop down select for the rating instead of a custom field. With all of the power available in custom meta boxes, there is really no reason at all to use regular custom fields.

  13. ansimuz says:

    Great tut!

    I’mm very happy with the new “wp tuts” site.

  14. owhhh god!
    Best tutorial about wordpress I have ever read. Incredible.
    Hope that wpTuts will has more and more awesome articles.
    Good job!

  15. The Rating meta data could have been done better.

    Rather then using the built-in custom fields editor you could have created your own interface for that.

    • Brandon Jones says:

      Hi Azizur – We’ll actually be going over that as a separate topic down the line :) Thanks for the comment!

    • Brian says:
      Author

      I totally agree, and I can’t wait for someone to write a tutorial on that so I can learn more about how to do it :)

  16. Boubakr says:

    Great tuts, thank you

  17. Piet says:

    Great Tutorial, thanks so much for the entire writeup! It was long and worth it :)

    Two things that could be improved in my opinion. Other people also already commented on the stars. I am doing a site now with reviews where the author rates and the audience rates. The audience is done with a plugin and for the author I use a category taxonomy where the description of the category consists of images of stars (1 star is 1 full star and 4 empty ones; 5 stars is 5 full ones). To do this with custom fields seems very cumbersome.

    My second item that could be improved is localization. Obviously it’s a royal pain to localize code, but it becomes instantly globally useful instead of now: only suitable for a one language environment.

    Anyways, will definitely use this tutorial to give my own site some cool widget, thanks!

  18. Interesting tutorial. I think you are using way to much code to achieve the tab results. You can optimize and streamline the code a lot more, especially considering you are basically repeating code blocks.

    For easy re-usability I suggest compacting the code by putting the post types you want in your widget in an array and cycling through it. ie:

    $widget_post_array = array(
    ‘movies’ => array(‘ID=>’tabs-movies’,'Post_Type’=>’oswc_movie_reviews’),
    ‘music’ => …,
    ‘games’ => …
    );

    foreach($widget_post_array as $key => $value){
    //run repeating code here
    $html = ‘ ‘
    $html .= ”
    $args=’&suppress_filters=true&posts_per_page=’.$nummovies.’&post_type=$value[Post_Type]&order=DESC&orderby=’.$feedsort.$metakey;
    // run your display of the query results here ….

    //reset the query
    wp_reset_query(); ?>

    Now, you can use the same approach for most of your code and basically cut it down and make it far more user-friendly. It also makes the code more portable for use in other templates.

    In fact, you can even use this approach when creating yuor custom post types in functions.php and leave your functions file far less cluttered.

    The bottom line is that, for such a small postage stamp on the website it is a heck of a lot of code to have to traverse when editing. Also, you functions.php gets loaded for every page no matter what so the size of the file, itself, is important. Fill it with mass amounts of code for both administration and for front end and the file gets large very quickly.

    The better approach would also be to load files dynamically for your functions.php by storing the code in a seperate file and loading it only if needed (using nonce includes) by simply adding the appropriate checks:

    Admin specific
    admin_init / admin_menu
    Front End or general
    loop_start / init
    More can be found in the codex

    Cheers

    • Brandon Jones says:

      Thanks for sharing the ideas Neil! This looks great… definitely worth considering as an alternative that’s a bit more graceful.

      • After reading this tutorial it finally sparked my latest article. I’ve been sitting for 2 weeks trying to unblock my mind on what to write and this led me to write about my pet peeve with theme developers and the bloated functions.php files!

        I figure, I can’t write for you guys (never passes your inspection :-( ) but I must say that tuts+ certainly inspires me. Great work on finally adding a WordPress focused site!

        • Posted a reply to your article on your blog. I agree with the cleanliness of code idea behind the foreach, but as far as “minimizing page load/impact” for having large functions.php files, I completely disagree.

          There are 20 or so other places you should worry about optimizing that will actually have impact on page load speed rather than a bloated functions file IMO.

          • Author

            Agreed. I do like the idea of cleaning up code though, and I absolutely don’t doubt that my code can be cleaned and optimized a bit, even if it’s only for the benefit of ease of editing (not much to do with site performance).

          • Keep in mind that there are dozens of articles on the web for optimizing your javascript files, css files, image files and even basic html. The scope of the article is not to re-hash already common material but to bring perspective to the functions.php file.

            Why would your front end WordPress website need to load a 100kb functions.php file for your home page when most of the code is defining custom post types, menu options and other administrative specific content?

            In the case of this tutorial optimizing the code can be done beyond the foreach loop but the first approach is to eliminate repetitive code. Why? Just like the functions.php file – it simply doesn’t need to be there when there is a more efficient way of doing things.

    • Nicolas says:

      Hey,

      I did something similar a while ago and as Neil said, it saves you a lot of code and is easier to organize/modify stuff afterward if you put some code in a foreach loop.

      In the tutorial, you hard-coded the more link – which is not an option for me since I wanted my widget to be compatible with any custom post type. I am wondering how I would go about adding the CPT more link dynamically with code ?

      Thank you for this great new site btw !

      • If you are talking about

        More

        Just add it to your array that you are looping through.

        $widget_post_array = array(
        ‘movies’ => array(
        ‘ID=>’tabs-movies’,
        ‘Post_Type’=>’oswc_movie_reviews’
        ‘Page_Slug’=>’movies-reviews’,
        ‘Alt_Text’=>’reviews of all movies’,
        ),
        ‘music’ => …,
        ‘games’ => …
        );

        Pretty simple right?! Basically just use the array to define anything you will need.

        • ..I hate the “accepted” code pre. Never seems to work without the < > in ascii. This is what should be there instead of “more”

          <li class=”last gentesque tooltip” title=”View all music reviews”><a href=”music-reviews/”>More</a></li>

      • Sorry to repost again. I think what you are looking for is:

        get_post_types()

  19. Arif Riyanto says:

    it’s good, very detail.. thanks

  20. Arif says:

    I will try this one..

  21. Ginva says:

    Nice Tutorial, I’ve a plan to created a tabbed widget for my site and maybe i’ll try this tutorial… Thanks :)

  22. nuRESPONSE says:

    Great tutorial! You’ve done a great job in describing the nuts and bolts to this plugin and widget in the proper order. The example widget is actually a useful one, I know that I’ll be implementing this idea into an upcoming project.

    Also, I really appreciate the full code you added at the end of the tutorial. for easy copy/paste into new file.

  23. Mike says:

    Ok, Brian McCulloh great tut, but what about to add that stars – rating under the post?

    Many thanks

  24. getsoftgames says:

    great tutorial, this site is very helpful for me

  25. alfi says:

    Hi Brian, amazing tut. I have a question. how to show the rating on the below or above post in home page, just like your continuum theme.

  26. romulo_ctba says:

    You should tottally pu that all togheter for the lazy ones download!

    if u do that, let me know! lol

  27. romulo_ctba says:

    Oh, sorry, bro, u already did it.
    Thnx it is great!

  28. pmpksamy says:

    how to insert word press middle content section please help me

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.