An In-Depth Look at the Dashboard Widgets API

Dashboard

The WordPress Dashboard is the first thing a user views when they login to manage their website. This presents an opportunity to greet them with useful information regarding their theme, as well as an opportunity to attract recurring customers, whether it’s through displaying your blog’s RSS feed to them, a donation link, or displaying the recent posts from a custom post type in your theme. This is all possible using WordPress’s Dashboard Widgets API.

Download Source Code

Starting Simple

Let’s start with the very basics. Pete Mall wrote a nice beginner tutorial on the Dashboard Widget API, introduced in WP 2.7, and I’m just going to quickly recap a lot of what he said, and then get into the nitty-gritty.

You add a Dashboard Widget by utilizing wp_add_dashboard_widget which takes the following arguments:

  1. Widget ID – The CSS ID name applied to the widget.
  2. Widget Title – The title that will display at the top of the widget.
  3. Content Callback – The function that adds the content to your widget.
  4. Control Callback (Optional) – Don’t worry about this yet, I’ll get to it in a bit.

Let’s create a very simple text widget to get us started. First we need to create the “Content Callback” that outputs our custom widget’s text, so let’s create that (in your theme’s functions.php):

function say_what_up(){
     echo 'What up, yo.';
}

Then we need to create the function that registers our custom widget:

function register_widgets(){
     wp_add_dashboard_widget( 'our-css-id', 'Widget Title', 'say_what_up');
}

We’re not done yet! We still need to hook into the dashboard action to register our widget:

add_action('wp_dashboard_setup', 'register_widgets' );

Our very simple text widget

Removing Default Widgets

If you’re anything like me, the default widgets are nearly useless to you and just clutter up the dashboard. Removing these widgets is pretty straight forward and only requires one line of code (per widget).

Again, you’ll need to write the following code in a function that you can use to hook into the wp_dashboard_setup action and register:

function remove_widgets(){
  // Remove "Incoming Links" widget
  remove_meta_box( 'dashboard_incoming_links', 'dashboard', 'side' );
  
  // Remove "Quick Press" widget
  remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );
} 

// Hook into the dashboard action
add_action('wp_dashboard_setup', 'remove_widgets' );

The above code removes the “Incoming Links” and “Quick Press” Dashboard widgets. You can also use the following names to remove other widgets:

  • “Right Now”: dashboard_right_now
  • “Recent Comments”: dashboard_recent_comments
  • “Plugins”: dashboard_plugins
  • “Recent Drafts”: dashboard_recent_drafts
  • “WordPress Blog”: dashboard_primary
  • “Other News”: dashboard_secondary

Adding an RSS Dashboard Widget

Text is cool and all, but what if you wanted to keep your users up to date with your company’s latest news? Well, it’s actually really easy to add your RSS feed to your theme’s Dashboard, just like the WordPress Blog’s RSS feed is there by default.

This is all that you need to add:

function shaken_rss_output(){
    echo '<div class="rss-widget">';
     
       wp_widget_rss_output(array(
            'url' => 'http://feeds.feedburner.com/shakenandstirredweb/MLnE',  //put your feed URL here
            'title' => 'Latest News from Shaken &amp; Stirred', // Your feed title
            'items' => 2, //how many posts to show
            'show_summary' => 1, // 0 = false and 1 = true 
            'show_author' => 0,
            'show_date' => 1
       ));
       
       echo "</div>";
}

Of course, this won’t output anything until we hook wp_dashboard_setup and register the widget:

// Hook into wp_dashboard_setup and add our widget
add_action('wp_dashboard_setup', 'shaken_rss_widget');
  
// Create the function that adds the widget
function shaken_rss_widget(){
  // Add our RSS widget
  wp_add_dashboard_widget( 'shaken-rss', 'RSS Widget', 'shaken_rss_output');
}

Our fancy RSS Widget

Query Posts

Creating a widget that queries custom post types is another option that you may find useful, possibly for things like event calendars or invitations. WordPress basically does this already with the default “Recent Drafts” widget, and we’re just going to customize it a bit to fit our needs.

Build the Query

The first thing we need to do is create our posts query:

function shaken_posts_output(){
  $posts_query = new WP_Query( array(
    // Leave this as "post" if you just want blog posts
    'post_type' => 'post', 
    'post_status' => 'publish',
    'posts_per_page' => 5,
    'orderby' => 'date',
    'order' => 'DESC'
  ) );
  $posts =& $posts_query->posts;

Check that we have posts

We now have a variable, $posts, which should be holding an array of our posts. We need to verify this first before outputting the post content in order to avoid errors:

if ( $posts && is_array( $posts ) ) {
  // Output posts
} else{
  _e('There are no published posts.');
}

Output the posts

Assuming that we have posts to play with, we need to loop through the $posts array and get the content we want to display:

Begin the loop:

$list = array();
foreach ( $posts as $post ) { // Loop through our array
    $url = get_edit_post_link( $post->ID ); // The URL to the "Edit" post page
    $title = get_the_title( $post->ID ); // The title of the post
    $chars = 30; // Our character limit

Create the title of the post and link it to the “Edit” page:

$item = "<h4><a href='$url' title='" . sprintf( __( 'Edit &#8220;%s&#8221;' ), esc_attr( $title ) ) . "'>" . esc_html($title) . "</a> <abbr title='" . get_the_time(__('Y/m/d g:i:s A'), $post) . "'>" . get_the_time( get_option( 'date_format' ), $post ) . '</abbr></h4>';

Create the post content:

if ( $the_content = preg_split( '#\s#', strip_tags( $post->post_content ), $chars+1 , PREG_SPLIT_NO_EMPTY ) )
    $item .= '<p>' . join( ' ', array_slice( $the_content, 0, $chars ) ) . ( $chars < count( $the_content ) ? '&hellip;' : '' ) . '</p>';

Finally, output the content if we have it or display a message if there aren’t any posts:

$list[] = $item;
} // End the foreach loop
    
// Output the posts...
?>
    <ul>
      <li><?php echo join( "</li>\n<li>", $list ); ?></li>
    </ul>
<?php
} else { 
    // If there are no posts, then we tell the user.
    _e('There are no posts at the moment'); 
}

Our Dashboard posts query in action

Allowing Configurations

Building upon our query posts example, we can also allow the user to configure how many posts should display. I only discovered this feature while writing this tutorial, but if you hover over the title of most of the default widgets, you’ll see a “Configure” link in the right corner. We’re going to add this to our Posts Query widget by setting the control callback function when we add the widget using wp_add_dashboard_widget.

Our original code:
wp_add_dashboard_widget( 'shaken-posts', 'Posts Query Widget', 'shaken_posts_output');

Will now become:
wp_add_dashboard_widget( 'shaken-posts', 'Posts Query Widget', 'shaken_posts_output', 'shaken_posts_control');

Notice the new shaken_posts_control callback? Let’s create that function now.

function shaken_posts_control(){
    
    $widget_id = 'shaken-posts'; // This must be the same ID we set in wp_add_dashboard_widget
    $form_id = 'shaken-posts-control'; // Set this to whatever you want
    
    // Checks whether there are already dashboard widget options in the database
    if ( !$widget_options = get_option( 'dashboard_widget_options' ) )
      $widget_options = array(); // If not, we create a new array
    
    // Check whether we have information for this form
    if ( !isset($widget_options[$widget_id]) )
      $widget_options[$widget_id] = array(); // If not, we create a new array
    
    // Check whether our form was just submitted
    if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST[$form_id]) ) {
      /*
       * Get the value. In this case ['items'] is from the input 
       * field with the name of '.$form_id.'[items]
       */
      $number = absint( $_POST[$form_id]['items'] );
      
      $widget_options[$widget_id]['items'] = $number; // Set the number of items
      update_option( 'dashboard_widget_options', $widget_options ); // Update our dashboard widget options so we can access later
    }
    
    /*
     * Check if we have set the number of posts previously.
     * If we didn't, then we just set it as empty.
     * This value is used when we create the input field
     */
    $number = isset( $widget_options[$widget_id]['items'] ) ? (int) $widget_options[$widget_id]['items'] : '';
    
    // Create our form fields. Pay very close attention to the name part of the input field.
    echo '<p><label for="shaken-posts-number">' . __('Number of posts to show:') . '</label>';
    echo '<input id="shaken-posts-number" name="'.$form_id.'[items]" type="text" value="' . $number . '" size="3" /></p>';
}

I’m sure that looks like a pile of confusion, so let’s break it down…

We first define our $widget_id, which is the same CSS ID we assigned to our widget, and then we define the $form_id which can be whatever you want:

$widget_id = 'shaken-posts'; // This must be the same ID we set in wp_add_dashboard_widget
$form_id = 'shaken-posts-control'; // Set this to whatever you want

We then check WordPress’s options table to see if there is anything available for the dashboard_widget_options option (which is an array). There likely will since this is where the default widgets store their information. After that, we check if our widget has any information stored in this option.

// Checks whether there are already dashboard widget options in the database
if ( !$widget_options = get_option( 'dashboard_widget_options' ) )
    $widget_options = array(); // If not, we create a new array
    
// Check whether we have information for this form
if ( !isset($widget_options[$widget_id]) )
    $widget_options[$widget_id] = array(); // If not, we create a new array

After this, we need to check if our form was submitted and if it was, grab the values and update our dashboard_widget_options option.

// Check whether our form was just submitted
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST[$form_id]) ) {
      /*
       * Get the value. In this case ['items'] is from the input 
       * field with the name of '.$form_id.'[items]
       */
      $number = absint( $_POST[$form_id]['items'] );
      
      $widget_options[$widget_id]['items'] = $number; // Set the number of items
      update_option( 'dashboard_widget_options', $widget_options ); // Update our dashboard widget options so we can access later
}

We’re almost there! We now need to check if we set the number of posts, and leave the field blank if we didn’t. From there, we create the input fields for the user to set the number of posts.

/*
 * Check if we have set the number of posts previously.
 * If we didn't, then we just set it as empty.
 * This value is used when we create the input field
 */
$number = isset( $widget_options[$widget_id]['items'] ) ? (int) $widget_options[$widget_id]['items'] : '';
    
// Create our form fields. Pay very close attention to the name part of the input field.
echo '<p><label for="shaken-posts-number">' . __('Number of posts to show:') . '</label>';

echo '<input id="shaken-posts-number" name="'.$form_id.'[items]" type="text" value="' . $number . '" size="3" /></p>';

If you implemented that code you should now see the “Configure” link displaying in the right of the title bar on your widget. However, if you try setting the number of posts you’ll find out that it doesn’t have any effect yet. That’s because the number of posts is still hard-coded into our posts query. Let’s change that.

Our original query was this:

$posts_query = new WP_Query( array(
    // Leave this as "post" if you just want blog posts
    'post_type' => 'post', 
    'post_status' => 'publish',
    'posts_per_page' => 5,
    'orderby' => 'date',
    'order' => 'DESC'
) );

What we want to do is check to see if the user set the number of posts to show, and if they didn’t then we need to provide a default number. Here’s how we do that:

$widgets = get_option( 'dashboard_widget_options' ); // Get the dashboard widget options
$widget_id = 'shaken-posts'; // This must be the same ID we set in wp_add_dashboard_widget

/*
 * Check whether we have set the post count through the controls.
 * If we didn't, set the default to 5
 */
     
$total_items =   isset( $widgets[$widget_id] ) && isset( $widgets[$widget_id]['items'] )
      ? absint( $widgets[$widget_id]['items'] ) : 5;

From here, we can replace our hard-coded number with the $total_items variable.

$posts_query = new WP_Query( array(
      'post_type' => 'post', // Leave this as "post" if you just want blog posts
      'post_status' => 'publish',
      'posts_per_page' => $total_items, // Sets the number of posts to the option we collected above
      'orderby' => 'date',
      'order' => 'DESC'
) );

That should do the trick!

A Few Things to Note

I’ve tried to keep this article as to the point and brief as possible (so much for that, right?) in order to give you a taste of what’s possible. One thing that you may want to do with your widgets is to check the user’s permissions before displaying your widget to them. For example, if the user doesn’t have permission to edit a post, then you probably shouldn’t provide an edit link in your posts query.

Download Source Code

Sawyer is a web designer and founder of ShakenAndStirredWeb.com. He wishes he had a time machine so that he could travel back to the time when everyone wore suits and cool hats. He's also on Twitter - @sawyerh

26 Comments on "An In-Depth Look at the Dashboard Widgets API"

  1. Eddie says:

    Killer tutorial! This is a great way to customize the Dashboard when creating sits for clients.

  2. Marc Buurke says:

    Awesome read! Very nice tut, keep it up. And yes, most of the default wordpress dash widgets go into the do not show state upon installation when I do client work myself. They are not that useful imo.

    • Yup, I forgot to mention that you can hide the widgets in the “Screen Options” tab (which is what you probably do), but some people may want to fully disable them for whatever reason. Thanks for reading!

  3. Thanks for this writeup. Bookmarked for future reference! :)

  4. Dan says:

    Thank you so much for this, its very well written. It took me all of 3 minutes to put up my own RSS widget up for the first time and it looks great.

  5. Hal Borland says:

    Awesome Job! Love this tutorial. Thank you for sharing it us.

  6. Michael says:

    Sawyer. Great job… really well laid out and an excellent reference for future needs.. thanks for sharing I can now replace the 6 or so bookmarks I have on this topic and gladly replace them with just one great reference point -

  7. Bill says:

    Thanks for posting this. It’s helpful and clear.

  8. Dave Darby says:

    Thanks Sawyer – this helped change the face of my dashboard. With your great layout/commenting, I was able to add a checkbox and create an additional control for show_summary. Nice job!

  9. shae says:

    How can you set the dashboard to be 1 column by default?

  10. Duane says:

    Great tutorial Sawyer! I like your style. I know this isn’t a big deal, but the link to Dashboard Widget API is broken. It’s missing an underscore.

  11. Pancho says:

    Hey, great tutorial, thanx for the tips :)

  12. killaBottof says:

    Бродя по огромным просторам интернета, я наткнулся на один интереснейший ресурс, в котором было это:
    автопросмотр. И еще много разных интересных материалов.

  13. daiv says:

    Thank you for posting this great tip.
    I’ve used some of it in my theme functions file to do a couple of things:
    1) changed the colors of the bar and links to match the front end. These are the items to change:
    #wpadminbar .ab-top-secondary
    #wpadminbar *
    #wpadminbar .quicklinks > ul > li
    #wpadminbar .quicklinks > ul > li a
    #wpadminbar .quicklinks .ab-top-secondary > li
    #wpadminbar .quicklinks .ab-top-secondary > li a
    #wpadminbar ul li:last-child

    2) set the display of the WP logo to none.
    #wp-admin-bar-wp-logo

    As novice users can become easily disoriented, this can be helpful with grounding them into the site at hand.

  14. tiliriuff says:

    Посоветовали мне тут один неплохой сайт. Я посетил его и обнаружил столько всего полезного для себя, а именно:
    фильмы 2012 года смотреть без регистрации. Вы будете приятно удивлены. Всего доброго!

  15. liveme says:

    Hello!Can you sent me the complete code of the “Query Posts ” and give me some more tips? your code are segmented, I do not know how to add and make it word.I really need it! Please..Thank you very much!

Trackbacks for This Post

  1. Quick-tip: Add Screencast Tutorials to Your Dashboard Widgets | Theme.it
  2. 15 Wordpress Theme Development Tutorials | Wordpress | iDesignow
  3. Quick-tip: Add Screencast Tutorials to Your Dashboard Widgets | Pros Global TV
  4. Quick Tip: Add Screencast Tutorials to Your Dashboard Widgets | Pros Global TV

Got something to say? Go for it!