When I started this answer it should be just a small note. Well, I failed. Sorry! Stay with me, there is a goody hidden deep down …
How WordPress widgets are stored
The list of widget is stored in an option named 'sidebars_widgets'
. A var_export()
may give something like the following:
array (
'wp_inactive_widgets' =>
array (
),
'top-widget' =>
array (
),
'bottom-widget' =>
array (
),
'array_version' => 3,
)
Ignore 'wp_inactive_widgets'
and 'array_version'
. We don’t have to care about those.
The other keys are identifier for registered sidebars. In this case the sidebars may have been registered with this code:
// Register two sidebars.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
By default the sidebars are empty after registration. Of course.
For each registered widget class a separate option is created, containing all necessary options. The option is prefixed by the string widget_
. To get the options for all active RSS widgets we have to look into …
get_option( 'widget_rss' );
Possible output:
array (
2 =>
array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 5,
'show_summary' => 1,
'show_author' => 0,
'show_date' => 0,
),
)
Note the number 2. The arguments for multiple instances are all stored in this one option sorted by numbers.
To see which widget classes are already known to WordPress go to wp-admin/options.php
and scroll down until you see something like this:
Yes, serialized data. No, you can’t read those here. Don’t worry, you don’t have to.
A demo widget
To illustrate the inner workings better I have written a very simple demo widget:
/**
* Super simple widget.
*/
class T5_Demo_Widget extends WP_Widget
{
public function __construct()
{ // id_base , visible name
parent::__construct( 't5_demo_widget', 'T5 Demo Widget' );
}
public function widget( $args, $instance )
{
echo $args['before_widget'], wpautop( $instance['text'] ), $args['after_widget'];
}
public function form( $instance )
{
$text = isset ( $instance['text'] )
? esc_textarea( $instance['text'] ) : '';
printf(
'<textarea class="widefat" rows="7" cols="20" id="%1$s" name="%2$s">%3$s</textarea>',
$this->get_field_id( 'text' ),
$this->get_field_name( 'text' ),
$text
);
}
}
Note the constructor: 't5_demo_widget'
is the $id_base
, the identifier for this widget. As you can see in the screen shot its arguments are stored in the option widget_t5_demo_widget
. All your custom widgets will be treated like this. You don’t have to guess the name. And since you have written your widgets (probably) you know all the arguments from your class’ $instance
parameters.
Theme basics
First you have to register some sidebars and the custom widget. The proper action for this is easy to remember: 'widgets_init'
. Put everything into a container – a class or a function. For simplicity I’ll use a function named t5_default_widget_demo()
.
All of the following code goes into the functions.php
. The class T5_Demo_Widget
should be loaded already. I just put it into the same file …
add_action( 'widgets_init', 't5_default_widget_demo' );
function t5_default_widget_demo()
{
// Register our own widget.
register_widget( 'T5_Demo_Widget' );
// Register two sidebars.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
So far, so simple. Our theme is now widget ready, the demo widget is known. Now the fun.
$active_widgets = get_option( 'sidebars_widgets' );
if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{ // Okay, no fun anymore. There is already some content.
return;
}
You really don’t want to destroy the user settings. If there is already some content in the sidebars your code should not run over it. That’s why we stop in this case.
Okay, assumed the sidebars are empty … we need a counter:
$counter = 1;
Widgets are numbered. These numbers are second identifiers for WordPress.
Let’s get the array to change it:
$active_widgets = get_option( 'sidebars_widgets' );
We need a counter too (more on that later):
$counter = 1;
And here is how we use the counter, the sidebar names and the the widget arguments (well, we have just one argument: text
).
// Add a 'demo' widget to the top sidebar …
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// … and write some text into it:
$demo_widget_content[ $counter ] = array ( 'text' => "This works!\n\nAmazing!" );
$counter++;
Note how the widget identifier is created: the id_base
, a minus -
and the counter. The content of the widget is stored in another variable $demo_widget_content
. Here is the counter the key and the widget arguments are stored in an array.
We increment the counter by one when we are done to avoid collisions.
That was easy. Now a RSS widget. More fields, more fun!
$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// The latest 15 questions from WordPress Stack Exchange.
$rss_content[ $counter ] = array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 15,
'show_summary' => 0,
'show_author' => 1,
'show_date' => 1,
);
update_option( 'widget_rss', $rss_content );
$counter++;
Here is something new: update_option()
this will store the RSS widget argument in a separate option. WordPress will find these automatically later.
We didn’t save the demo widget arguments because we add a second instance to our second sidebar now …
// Okay, now to our second sidebar. We make it short.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'The second instance of our amazing demo widget.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );
… and save all arguments for the t5_demo_widget
in one rush. No need to update the same option two times.
Well, enough widgets for today, let’s save the sidebars_widgets
too:
update_option( 'sidebars_widgets', $active_widgets );
Now WordPress will know that there are some registered widgets and where the arguments for each widget are stored. A var_export()
on the sidebar_widgets will look like this:
array (
'wp_inactive_widgets' =>
array (
),
'top-widget' =>
array (
0 => 't5_demo_widget-1',
1 => 'rss-2',
),
'bottom-widget' =>
array (
0 => 't5_demo_widget-3',
),
'array_version' => 3,
)
add_action( 'widgets_init', 't5_default_widget_demo' );
function t5_default_widget_demo()
{
// Register our own widget.
register_widget( 'T5_Demo_Widget' );
// Register two sidebars.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
// Okay, now the funny part.
// We don't want to undo user changes, so we look for changes first.
$active_widgets = get_option( 'sidebars_widgets' );
if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{ // Okay, no fun anymore. There is already some content.
return;
}
// The sidebars are empty, let's put something into them.
// How about a RSS widget and two instances of our demo widget?
// Note that widgets are numbered. We need a counter:
$counter = 1;
// Add a 'demo' widget to the top sidebar …
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// … and write some text into it:
$demo_widget_content[ $counter ] = array ( 'text' => "This works!\n\nAmazing!" );
#update_option( 'widget_t5_demo_widget', $demo_widget_content );
$counter++;
// That was easy. Now a RSS widget. More fields, more fun!
$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// The latest 15 questions from WordPress Stack Exchange.
$rss_content[ $counter ] = array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 15,
'show_summary' => 0,
'show_author' => 1,
'show_date' => 1,
);
update_option( 'widget_rss', $rss_content );
$counter++;
// Okay, now to our second sidebar. We make it short.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'The second instance of our amazing demo widget.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );
// Now save the $active_widgets array.
update_option( 'sidebars_widgets', $active_widgets );
}
If you go to wp-admin/widgets.php
now you will see three pre-set widgets:
And that’s it. Use …
dynamic_sidebar( 'top-widget' );
dynamic_sidebar( 'bottom-widget' );
… to print the widgets out.
There is a small glitch: You have to load the front-end two times for the initial registration. If anyone can help out here I’ll be very grateful.