Custom WP_Widget not appearing in admin panel

In order to apply some changes to the default Recent Posts Widget I have copied and modified it to my needs. It is included and registered in my template’s functions.php and when loading the widget-section in the admin panel its constructor is run.

The problem is that it does not appear in the list of available widgets and thus can not be used. The problem also occurs when I try the minimal example from http://www.darrenhoyt.com/2009/12/22/creating-custom-wordpress-widgets/, so I think I missed something important, but don’t know what.

Read More
  • Until now my theme is a modified clone of https://wordpress.org/extend/themes/toolbox.
    This explains the many occurrences of toolbox_ in the code.
  • Following are code excerpts from functions.php and
    inc/widgets.php. I can provide more code if needed. Please let me
    know in the comments.

Code:

<?php

// BEGIN functions.php

if ( ! function_exists( 'toolbox_setup' ) ):
/**
 * Sets up theme defaults and registers support for various WordPress features.
 *
 * Note that this function is hooked into the after_setup_theme hook, which runs
 * before the init hook. The init hook is too late for some features, such as indicating
 * support post thumbnails.
 *
 * To override toolbox_setup() in a child theme, add your own toolbox_setup to your child theme's
 * functions.php file.
 */
function toolbox_setup() {
    // [...]

    require( get_template_directory() . '/inc/widgets.php' );

    // [...]
}
endif; // toolbox_setup

/**
 * Tell WordPress to run toolbox_setup() when the 'after_setup_theme' hook is run.
 */
add_action( 'after_setup_theme', 'toolbox_setup' );

/**
 * Register widgetized area and update sidebar with default widgets
 */
function toolbox_widgets_init() {

  register_widget("My_Widget_Recent_Posts");

    register_sidebar( array(
        'name' => __( 'Sidebar 1', 'toolbox' ),
        'id' => 'sidebar-1',
        'before_widget' => '<aside id="%1$s" class="widget %2$s">',
        'after_widget' => "</aside>",
        'before_title' => '<h1 class="widget-title small">',
        'after_title' => '</h1>',
    ) );
}
add_action( 'init', 'toolbox_widgets_init' );

// END functions.php

?>
<?php

// BEGIN inc/widgets.php

/**
 * Recent_Posts widget class
 *
 * @since 2.8.0
 */

class My_Widget_Recent_Posts extends WP_Widget {

  function __construct() {
        $widget_ops = array('classname' => 'my_widget_recent_entries', 'description' => __( "The most recent posts on your site (modified)") );
        parent::__construct('my-recent-posts', __('Recent Posts (modified)'), $widget_ops);
        $this->alt_option_name = 'my_widget_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('my_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();
        extract($args);

        $title = apply_filters('widget_title', empty($instance['title']) ? __('Recent Posts') : $instance['title'], $instance, $this->id_base);
        if ( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
             $number = 10;

        $r = new WP_Query( apply_filters( 'widget_posts_args', array( 'posts_per_page' => $number, 'no_found_rows' => true, 'post_status' => 'publish', 'ignore_sticky_posts' => true ) ) );
        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>
        <?php the_content(); ?></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('my_widget_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['my_widget_recent_entries']) )
            delete_option('my_widget_recent_entries');

        return $instance;
    }

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

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

// END inc/widgets.php

?>

Related posts

Leave a Reply

3 comments

  1. You have to hook register_widget to the widgets_init action, NOT to the init action. In your case it is something like this:

    <?php
    add_action( 'widgets_init', 'toolbox_widgets_init' );
    
    function toolbox_widgets_init() {
      register_widget("My_Widget_Recent_Posts");
    }
    ?>
    
  2. Ok lets do some debugging test:

    echo "include widget file";
    require( get_template_directory() . '/inc/widgets.php' );
    

    Also:

    echo "register widget";
    register_widget("My_Widget_Recent_Posts");
    

    Then:

    function __construct() {
    echo "widget class";
    

    Check your HTML to see what parts echoed and what parts didn’t you may get Headers already sent error, which you can ignore. Hopefully we can see which part is not being triggered to narrow down where the problem is.

  3. Registering the widget as follows solved the problem for me, but is not very elegant in my opinion. Right behind the class definition I inserted the line

    <?php
    add_action( 'widgets_init', create_function('',
        'return register_widget("My_Widget_Recent_Posts");') );
    ?>
    

    And removed the registration from toolbox_widgets_init() where I think it belongs.

    In case somebody can explain why it has to be this way round or what I could change so that it would work like I intended to do it before I would highly appreciate comments. But for now the actual problem is solved.