A Reusable Widget

Creating a reusable widget requires a bit more code than a one time use widget. I’ll walk you through the code that you need to add/change to make a widget reusable. A term that I’m going to use in the tutorial is instance. If you add 5 of this widget to your sidebars, then you have 5 instances of the widget.

The function definition for a one time use widget looks like

function widget_links_by_cat( $args )

A reusable widget has a second argument.

function widget_links_by_cat( $args, $widget_args = 1 )

The name I gave the second parameter is $widget_args which I will use through the remainder of the tutorial. If you want to use a different variable name then you have to make that change everywhere. The purpose of the second parameter is to tell the widget function which instance of the widget it is to display during this call of the widget.

The image badge widget has a couple lines which lookup the options for the widget.

extract($args);
$options = get_option('widget_raimage');

Inside a reusable widget function, there is code which evaluates what it should be displayed based on what instance has been requested.

extract( $args, EXTR_SKIP );
if ( is_numeric($widget_args) )
$widget_args = array( 'number' => $widget_args );
$widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );

$options = get_option('widget_links_by_cat');
if ( isset($options[$number]) ) {

The first 4 lines create a variable $number with a default value of -1. $number is a unique number which identifies an instance of the widget. The last line checks to ensure that the instance requested has options stored in the blog options before displaying content. From there it continues on using similar logic to display the content of the widget where the widget options are elements in the $options[$number] array (ex. $options[$number]['some-setting']).

The widget control function which is used in the widget admin area also needs to be changed to manage multiple instances of a widget. The image badge widget has a typical single instance widget control program which checks to see if the widget options were POSTed back to the server. If they are POSTed then the control program saves the settings to the blog options.

function widget_raimage_control() {
$options = get_option('widget_raimage');
if (!is_array($options)) {
$options = array('title' => __('My Badge', 'raimage-widget'), 'width' => '0');
}
if ($_POST['raimage_submit']) {
$options['title'] = strip_tags(addslashes($_POST['raimage_title']));
$options['imgbadge'] = strip_tags($_POST['raimage_badge']);
$options['link'] = strip_tags($_POST['raimage_link']);
$options['width'] = strip_tags($_POST['raimage_width']);
update_option('widget_raimage', $options);
} ?>

This portion of the control program has to be a bit more complicated for multi-instance widgets for 2 reasons. First, there may be more than one instance of the widget in that sidebar, so it has to check for options POSTed for all instances of the widget. Second, there may be instances of the widget in other sidebars which are not being POSTed, so it has to ensure it doesn’t blow away the settings for widgets in other sidebars.

function widget_links_by_cat_control( $widget_args = 1 ) {
global $wp_registered_widgets, $wpdb;
static $updated = false; // Whether or not we have already updated the data after a POST submit
static $link_cats = false; // only retrieve them once
if ( is_numeric($widget_args) )
$widget_args = array( 'number' => $widget_args );
$widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
extract( $widget_args, EXTR_SKIP );
// Data should be stored as array: array( number => data for that instance of the widget, ... )
$options = get_option('widget_links_by_cat');
if ( !is_array($options) )
$options = array();
// We need to update the data
if ( !$updated && !empty($_POST['sidebar']) ) {
// Tells us what sidebar to put the data in
$sidebar = (string) $_POST['sidebar'];
$sidebars_widgets = wp_get_sidebars_widgets();
if ( isset($sidebars_widgets[$sidebar]) )
$this_sidebar =& $sidebars_widgets[$sidebar];
else
$this_sidebar = array();
foreach ( $this_sidebar as $_widget_id ) {
if ( 'widget_links_by_cat' == $wp_registered_widgets[$_widget_id]['callback'] && isset($wp_registered_widgets[$_widget_id]['params'][0]['number']) ) {
$widget_number = $wp_registered_widgets[$_widget_id]['params'][0]['number'];
if ( !in_array( "links_by_cat-$widget_number", $_POST['widget-id'] ) ) // the widget has been removed.
unset($options[$widget_number]);
}
}
foreach ( (array) $_POST['widget-links_by_cat'] as $widget_number => $widget_links_by_cat_instance ) {
// compile data from $widget_links_by_cat_instance
if ( !isset($widget_links_by_cat_instance['link_cat']) && isset($options[$widget_number]) ) // user clicked cancel
continue;
$link_cat = wp_specialchars( $widget_links_by_cat_instance['link_cat'] );
$options[$widget_number] = array( 'link_cat' => $link_cat ); // Even simple widgets should store stuff in array, rather than in scalar
}
update_option('widget_links_by_cat', $options);

I’m not going to go through that line by line. Essentially what the code does on each request for the widgets admin page is:

  • check to see if it’s the first time the control program has been called
  • skips updating the options if it is NOT the first time
  • gets a handle to the un-updated sidebar settings and compares that to what the user has POSTed
  • if the user has removed an instance from the sidebar, it removes that instances settings from the widget options
  • skips making changes to the settings of any instances not in this sidebar
  • if the user has added or changed an instance from this sidebar, it adds/updates that instance’s settings in the widget options

From there it shows the control program for that instance of the widget. The final piece is registering the widget functions. The image badge widget does this with 2 lines of code:

register_sidebar_widget('Image Badge', 'widget_raimage');
register_widget_control('Image Badge', 'widget_raimage_control', 300, 100);

A multi-instance widget requires registering a widget function and control function for each instance of the widget.

function widget_links_by_cat_register() {
if ( !$options = get_option('widget_links_by_cat') )
$options = array();
$widget_ops = array('classname' => 'widget_links_by_cat', 'description' => __('List Links from a single category'));
$control_ops = array('width' => 400, 'height' => 350, 'id_base' => 'links_by_cat');
$name = __('Links by Category');
$registered = false;
foreach ( array_keys($options) as $o ) {
// Old widgets can have null values for some reason
if (isset($options[$o]['link_cat']) ) {
$id = "links_by_cat-$o"; // Never never never translate an id
$registered = true;
wp_register_sidebar_widget( $id, $name, 'widget_links_by_cat', $widget_ops, array( 'number' => $o ) );
wp_register_widget_control( $id, $name, 'widget_links_by_cat_control', $control_ops, array( 'number' => $o ) );
}
}
// If there are none, we register the widget's existence with a generic template
if ( !$registered ) {
wp_register_sidebar_widget( 'links_by_cat-1', $name, 'widget_links_by_cat', $widget_ops, array( 'number' => -1 ) );
wp_register_widget_control( 'links_by_cat-1', $name, 'widget_links_by_cat_control', $control_ops, array( 'number' => -1 ) );
}
}

Basically, the register function loops through the widget options array registering a call to each function with the instance number as the last parameter. If the widget is not currently in use on the blog a default widget is registered so the widget is listed in the available widgets. Here is the full code for the links by category widget: Reusable Links By Category Widget (248).

[Post to Twitter] Tweet This Post 


bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark bookmark
tabs-top  banner ad


4 Responses to “A Reusable Widget”

  1. Make Money Online says:

    Wow!… Thats great post. I don’t know much about PHP Programming but still I can understand how to do it.

    • Atle says:

      I must say I’m kind of shocked you actually understood anything of this guide. Maybe it’s just me who doesn’t understand shit of this, but I guess I just have to keep experimenting with the widget I’m trying to use multiple times. :)
      [rq=,,][/rq]

  2. ghprod says:

    wow…

    it’s really tutz i’ve been looking for :)

    now i can add multiple widget in my sidebar :D

    regards

    thnx

  3. Cheers for the help, wouldn’t have been able to figure it out without this post. It would have helped *a lot* if your code examples were properly formatted. Could have saved me at least an hour. Might want to add wp-syntax to your plugin list (and use the pre field).

    http://wordpress.org/extend/plugins/wp-syntax/

Leave a Reply