6

I'm able to easily include my custom post types into my main loop by making small adjustments with query_posts(), but I'm not sure how I would go about including custom post types in the "Recent Posts" sidebar widget (or any of the other widgets, for that matter).

How should I go about expanding "Recent Posts" scope to include more than just the native post type?

1
  • 1
    Never use query_posts() Jul 24, 2014 at 22:17

7 Answers 7

9

You'll have to edit the code for the Recent Posts widget or create your own version based on the default. The code is in the wp-includes/default-widgets.php file around line 513. But since you should never make modifications to core, my recommendation would be to copy the code to create your own My Custom Recent Posts widget and use that on your site. Just drop the new widget class into your theme's functions.php file or use it in a plugin.

The only real modification you need to make are to the widget's class name and encapuslated functions and options (so that there aren't any naming conflicts with the original Recent Posts widget. After that, you'll need to edit the call to WP_Query in the widget() constructor so that it includes your custom post type.

For this example, I've set post_type equal to array('post, 'page', 'custom-post-type') ... you'll need to modify that to fit your specific use case.

Here's the widget's full code for reference:

/**
  * My_Custom_Recent_Posts widget class
  *
  */
class WP_Widget_My_Custom_Recent_Posts extends WP_Widget {

    function __construct() {
        $widget_ops = array('classname' => 'widget_my_custom_recent_entries', 'description' => __( "The most recent posts on your site") );
        $this->WP_Widget('my-custom-recent-posts', __('My Custom Recent Posts'), $widget_ops);
        $this->alt_option_name = 'widget_my_custom_recent_entries';

        add_action( 'save_post', array(&$this, 'flush_widget_cache') );
        add_action( 'deleted_post', array(&$this, 'flush_widget_cache') );
        add_action( 'switch_theme', array(&$this, 'flush_widget_cache') );
    }

    function widget($args, $instance) {
        $cache = wp_cache_get('widget_my_custom_recent_posts', 'widget');

        if ( !is_array($cache) )
            $cache = array();

        if ( isset($cache[$args['widget_id']]) ) {
            echo $cache[$args['widget_id']];
            return;
        }

        ob_start();
        extract($args);

        $title = apply_filters('widget_title', empty($instance['title']) ? __('My Custom Recent Posts') : $instance['title'], $instance, $this->id_base);
        if ( !$number = (int) $instance['number'] )
            $number = 10;
        else if ( $number < 1 )
            $number = 1;
        else if ( $number > 15 )
            $number = 15;

        $r = new WP_Query(array('showposts' => $number, 'nopaging' => 0, 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'post_type' => array('post', 'page', 'custom-post-type')));
        if ($r->have_posts()) :
?>
        <?php echo $before_widget; ?>
        <?php if ( $title ) echo $before_title . $title . $after_title; ?>
        <ul>
        <?php  while ($r->have_posts()) : $r->the_post(); ?>
        <li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?></a></li>
        <?php endwhile; ?>
        </ul>
        <?php echo $after_widget; ?>
<?php
        // Reset the global $the_post as this query will have stomped on it
        wp_reset_postdata();

        endif;

        $cache[$args['widget_id']] = ob_get_flush();
        wp_cache_set('widget_my_custom_recent_posts', $cache, 'widget');
    }

    function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = strip_tags($new_instance['title']);
        $instance['number'] = (int) $new_instance['number'];
        $this->flush_widget_cache();

        $alloptions = wp_cache_get( 'alloptions', 'options' );
        if ( isset($alloptions['widget_my_custom_recent_entries']) )
            delete_option('widget_my_custom_recent_entries');

        return $instance;
    }

    function flush_widget_cache() {
        wp_cache_delete('widget_my_custom_recent_posts', 'widget');
    }

    function form( $instance ) {
        $title = isset($instance['title']) ? esc_attr($instance['title']) : '';
        if ( !isset($instance['number']) || !$number = (int) $instance['number'] )
            $number = 5;
?>
        <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>

        <p><label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('Number of posts to show:'); ?></label>
        <input id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo $number; ?>" size="3" /></p>
<?php
    }
}
7
  • Instead of renaming the widget, I think doing unregister_widget to the default widget before registering the new one would be better (less confusing in the admin).
    – sorich87
    Sep 30, 2010 at 15:25
  • 1
    Yes, you could use unregister_widget, but this method gives you access to both should you want to use both (i.e. have a Recent Posts widget to display recent blog posts and a Recent Movie Reviews widget to display only recent entries of a specific custom post type).
    – EAMann
    Sep 30, 2010 at 15:31
  • 3
    just a note to anyone trying out the code above: Don't forget to add this: register_widget('WP_Widget_My_Custom_Recent_Posts'); Feb 24, 2011 at 13:28
  • Thanks for providing the code for the custom widget class and for the explanations. It's come in very useful. However I was just wandering if there was a way that I could alter it to allow for embedded HTML in the title field? Basically I need to turn the widget title into a clickable link but by default embedded HTML is stripped out of widget titles. Any help would be much appreciated.
    – Ian Young
    Apr 29, 2011 at 9:57
  • 1
    I don't know the first thing about PHP but couldn't one just Extend the default class and update the required variables? Rather than a full cut-and-paste?
    – thornomad
    Mar 10, 2012 at 21:02
9

As of at least 3.6, you can use the following code to modify the query used:

add_filter('widget_posts_args', 'widget_posts_args_add_custom_type'); 
function widget_posts_args_add_custom_type($params) {
   $params['post_type'] = array('post','custom_type');
   return $params;
}

Just add the types you want in the array for post_type and they should appear.

Update: According to http://core.trac.wordpress.org/ticket/16159, this has been available since 3.4

2

I just came across a great plugin where the heavy lifting is already done, and it has great documentation and author support. I've really been impressed.

It allows WP_Query overrides (allowing you to filter by custom post types and anything else you would want) and some clear instructions on how to use it.

Documentation http://www.pjgalbraith.com/2011/08/recent-posts-plus/

Wordpress Plugin URL http://wordpress.org/extend/plugins/recent-posts-plus/

Made my work just that much shorter!

1

You can copy the widget code (see /wp-includes/default-widgets.php) and modify the query line.

2
  • 1
    My thoughts exactly ... but I was already halfway through writing my answer when you posted this ...
    – EAMann
    Sep 30, 2010 at 15:13
  • 1
    Yours is very complete! +1
    – sorich87
    Sep 30, 2010 at 15:26
0

I have also create a widget plugin for this that's more customizable than the Recent Posts widget. If interested you can download it here http://new2wp.com/pro/latest-custom-post-type-posts-sidebar-widget/

0

This Code Creates a New Recent Posts Widget Which Includes Your CPT's

There's 2 steps involved when extending the native recent posts widget:

i. Create a new class for your custom recent posts widget which you can do by copying and renaming the recent posts widget code from the defaults-widgets.php in wp-includes folder.

ii. Then you will need to register the new widget as well and you may choose to de-register the native recent posts widget or use both.

All the code can simply be copied into your functions file using a child theme or create another file and include it in your child themes functions file.

<?php

class WPSites_Recent_Posts extends WP_Widget {

    public function __construct() {
        $widget_ops = array('classname' => 'wpsites_recent_posts', 'description' => __( "Latest CPT's & Posts.") );
        parent::__construct('wpsites-recent-posts', __('WP Sites Recent Posts'), $widget_ops);
        $this->alt_option_name = 'wpsites_recent_posts';

        add_action( 'save_post', array($this, 'flush_widget_cache') );
        add_action( 'deleted_post', array($this, 'flush_widget_cache') );
        add_action( 'switch_theme', array($this, 'flush_widget_cache') );
    }

    public function widget($args, $instance) {
        $cache = array();
        if ( ! $this->is_preview() ) {
            $cache = wp_cache_get( 'wpsites_widget_recent_posts', 'widget' );
        }

        if ( ! is_array( $cache ) ) {
            $cache = array();
        }

        if ( ! isset( $args['widget_id'] ) ) {
            $args['widget_id'] = $this->id;
        }

        if ( isset( $cache[ $args['widget_id'] ] ) ) {
            echo $cache[ $args['widget_id'] ];
            return;
        }

        ob_start();

        $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts' );

        /** This filter is documented in wp-includes/default-widgets.php */
        $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );

        $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5;
        if ( ! $number )
            $number = 5;
        $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : false;


        $r = new WP_Query( apply_filters( 'widget_posts_args', array(
            'posts_per_page'      => $number,
            'no_found_rows'       => true,
            'post_status'         => 'publish',
            'post_type'           => array('post', 'portfolio',
            'ignore_sticky_posts' => true
        ) ) ) );

        if ($r->have_posts()) :
?>
        <?php echo $args['before_widget']; ?>
        <?php if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        } ?>
        <ul>
        <?php while ( $r->have_posts() ) : $r->the_post(); ?>
            <li>
                <a href="<?php the_permalink(); ?>"><?php get_the_title() ? the_title() : the_ID(); ?></a>
            <?php if ( $show_date ) : ?>
                <span class="post-date"><?php echo get_the_date(); ?></span>
            <?php endif; ?>
            </li>
        <?php endwhile; ?>
        </ul>
        <?php echo $args['after_widget']; ?>
<?php

        wp_reset_postdata();

        endif;

        if ( ! $this->is_preview() ) {
            $cache[ $args['widget_id'] ] = ob_get_flush();
            wp_cache_set( 'wpsites_widget_recent_posts', $cache, 'widget' );
        } else {
            ob_end_flush();
        }
    }

    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = strip_tags($new_instance['title']);
        $instance['number'] = (int) $new_instance['number'];
        $instance['show_date'] = isset( $new_instance['show_date'] ) ? (bool) $new_instance['show_date'] : false;
        $this->flush_widget_cache();

        $alloptions = wp_cache_get( 'alloptions', 'options' );
        if ( isset($alloptions['wpsites_recent_posts']) )
            delete_option('wpsites_recent_posts');

        return $instance;
    }

    public function flush_widget_cache() {
        wp_cache_delete('wpsites_widget_recent_posts', 'widget');
    }

    public function form( $instance ) {
        $title     = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
        $number    = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5;
        $show_date = isset( $instance['show_date'] ) ? (bool) $instance['show_date'] : false;
?>
        <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>

        <p><label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of posts to show:' ); ?></label>
        <input id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="text" value="<?php echo $number; ?>" size="3" /></p>

        <p><input class="checkbox" type="checkbox" <?php checked( $show_date ); ?> id="<?php echo $this->get_field_id( 'show_date' ); ?>" name="<?php echo $this->get_field_name( 'show_date' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_date' ); ?>"><?php _e( 'Display post date?' ); ?></label></p>
<?php
    }
}

Register the new custom recent posts widget

function wpsites_widgets_init() {
    if ( !is_blog_installed() )
        return;

    register_widget('WPSites_Recent_Posts');
    do_action( 'widgets_init' );
}

add_action( 'init', 'wpsites_widgets_init', 2 );

The code includes a modified WP_Query which includes an array for post types including the portfolio CPT which you can rename to match your custom post type.

Here's the line of code that needs to be modified:

'post_type'           => array('post', 'portfolio',
0

It's 2020 and I came here to find a solution to the "10 most recent custom post type XYZ". I found the plugin that does that, and more.

Custom Post Type Widgets extends the usual widget standard post functionalities (most recent, monthly archives, used categories, recent comments, search, calendar) to custom post types.

You select the widget you need (in my case "most recent") and you get first a select where you specify the custom post type the widget must target. Default choice is good old 'post', so this plugin is a substitute of the vanilla WP post-related widgets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.