#8441 closed task (blessed) (fixed)
New Widget API based on MultiWidget Class
Reported by: | thornomad | Owned by: | azaozz |
---|---|---|---|
Milestone: | 2.8 | Priority: | normal |
Severity: | normal | Version: | 2.8 |
Component: | Widgets | Keywords: | widget, plugin, multi-widget |
Focuses: | Cc: |
Description
I have a feature enhancement for consideration in the trunk -- not sure how to go about getting it in there, so I thought I would post it here.
This came about when I tried to add mult-widget functionaility to a plugin I was contributing to ... I found the process to be far from easy and, although I got it to work, it required a lot of clunky coding.
Alex Tingle (lead devloper on the plugin) agreed, and created this enhancement to reduce the clunk: a php class called MultiWidget. The class handles all the nefarious multi-widget "tricks" required to implement a multi-widget plugin.
The benefit is that it will provide widget developers with a clean way to extend their plugins to multi-widget functionality without things "getting ugly" (as I see it).
See blog post explaining this at: http://blog.firetree.net/2008/11/30/wordpress-multi-widget/
See the code for this at: http://ec3.pastebin.com/fccfe0a0
See example usage of this class at: http://ec3.pastebin.com/f56a1539d
Again, not sure how to get it considered for inclusion in the Wordpress trunk, but I think it would benefit countless widget developers who, like me, run into some barriers when implementing a multi-widget.
Thanks,
Damon
Attachments (3)
Change History (59)
#3
@
16 years ago
I added (array)
to Line 228 to clear up an error with with wrong data type.
This class or something similar should be added to WordPress 2.8. I don't see a point to having a single use widget and this Class make creating Multi-Widgets a breeze.
Whom ever is in charge of the Widgets for WordPress 2.8 should be assigned to this ticket.
#5
@
16 years ago
- Cc westi added
- Component changed from General to Widgets
- Milestone changed from 2.8 to 2.9
- Owner anonymous deleted
A good idea in theory.
For this to go into core I would prefer something lighterweight and something which gets used by the core widget code.
#6
@
16 years ago
+1 brilliant. A base class for multiple-instance widgets is a dang good idea. A base class for a single instance widget would be a dang good idea as well.
#13
@
16 years ago
This is really amazing. While changing widgets to support this class add a standard way for widgets to be (optionally) displayed without a title.
#14
@
16 years ago
- Milestone changed from 2.9 to 2.8
Great to see this made it into 2.8
However, since the class can't be used by itself, wouldn't it be better to rename the constructor setup() ?
Another alternative would be to make setup() another method that needs to be overritten.
Thus, instead of
class WP_Widget_Pages extends WP_Widget { function WP_Widget_Pages() { $widget_ops = array('classname' => 'widget_pages', 'description' => __( "Your blog's WordPress Pages") ); $this->WP_Widget('pages', __('Pages'), $widget_ops); }
we would have something much cleaner:
class WP_Widget_Pages extends WP_Widget { function setup() { $this->id_base = 'pages'; $this->name = __('Pages'); $this->widget_ops = array('classname' => 'widget_pages', 'description' => __( "Your blog's WordPress Pages") ); }
setup() would be called form the WP_Widget constructor.
I'll write a patch for it if there are no objections.
#23
@
16 years ago
- Summary changed from Add "MultiWidget" Class for Plugin Development to New Widget API based on MultiWidget Class
- Type changed from enhancement to task (blessed)
#25
@
16 years ago
Some PHP notices displayed in my sidebar and on the widgets page:
{{{Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 722
Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 723
Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 728
Notice: Undefined index: links in [...]\wp-admin\includes\widgets.php on line 207
Notice: Undefined index: links in D:\Webserver\htdocs\wordpress-dev\wp-includes\widgets.php on line 739}}}
The error goes away if I hit Save Changes, so it looks to be an issue with the upgrade process or something.
#26
@
16 years ago
Erm, paste screwed up:
Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 722 Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 723 Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 728 Notice: Undefined index: links in [...]\wp-admin\includes\widgets.php on line 207 Notice: Undefined index: links in [...]\wp-includes\widgets.php on line 739
#27
@
16 years ago
widget_setup.patch adds a new mandatory method called setup() where the widget args are defined.
This method is then called from the WP_Widget constructor.
The advantage is that you don't have to deal with messy constructors for each widget. Also, the code is easier to read.
#28
@
16 years ago
Does calling a child method from the parent constructor work in all versions of PHP? Other languages won't allow that.
#29
@
16 years ago
I'm not sure. Can somebody test this code in PHP4?
class parent_class { function parent_class() { $this->method(); } } class child_class extends parent_class { function method() { echo "howdy"; } } new child_class();
#31
@
16 years ago
That is so wrong and so ug.
I much prefer the cleanliness of constructors anyway.
-1 to setup function.
#32
@
16 years ago
Id like to add A Title Option to the Search Widget as well an Option to not display the label for the Search Box. Any Problems with that?
#33
follow-up:
↓ 37
@
15 years ago
- Priority changed from normal to high
- Type changed from task (blessed) to defect (bug)
There seems to be no 'safe' way to unregister default widgets and register new theme specif ones with the same name(overwriting).
I was able (with the use of 'do_action('widgets_init');) to register my pages widgets with register_widget(). But the widget is using the options array ($instance) from the default widget in the front end. In the backend everything is good.
#36
follow-up:
↓ 38
@
15 years ago
I added unregister_widget(). Does that help? You should be able to unregister_widget('WP_Widget_Pages') and then register_widget('My_Widget_Pages') from a function hooked onto widgets_init.
#37
in reply to:
↑ 33
@
15 years ago
Replying to kretzschmar:
There seems to be no 'safe' way to unregister default widgets and register new theme specif ones with the same name(overwriting).
You can actually do that if you use different id_base
. This is the bit used to identify the widget, save the settings, etc. and is set independently from the widget name. As long as the id_base is different, the widget is different.
#38
in reply to:
↑ 36
@
15 years ago
Replying to ryan:
I added unregister_widget(). Does that help? You should be able to unregister_widget('WP_Widget_Pages') and then register_widget('My_Widget_Pages') from a function hooked onto widgets_init.
This doesn't work here. There is no error showing up but the widgets aren't unregistered.
#39
follow-up:
↓ 40
@
15 years ago
This doesn't work here. There is no error showing up but the widgets aren't unregistered.
Try hooking later:
add_action('init', 'my_func', 20); //10 is default priority, higher runs later
#40
in reply to:
↑ 39
;
follow-up:
↓ 42
@
15 years ago
Replying to DD32:
This doesn't work here. There is no error showing up but the widgets aren't unregistered.
Try hooking later:
add_action('init', 'my_func', 20); //10 is default priority, higher runs later
I already did this. And changing the id_base (as long as you meant the 'classname') also doesn't work.
#42
in reply to:
↑ 40
@
15 years ago
Replying to kretzschmar:
I already did this. And changing the id_base (as long as you meant the 'classname') also doesn't work.
The id_base
is not the classname. It's the first arg passed to WP_Widget. Check some of the widgets subclasses in default-widgets.php:
class WP_Widget_Links extends WP_Widget { function WP_Widget_Links() { $widget_ops = array('description' => __( "Your blogroll" ) ); $this->WP_Widget('links', __('Links'), $widget_ops); }
In this case 'links'
is the id_base.
#44
@
15 years ago
Hi,
I was wondering if it was possible to add a couple of hooks to the new widget api to allow for filtering the display of individual widgets based on options or criteria from a plugin, and secondly allow individual widgets to have applied to them unique options or display criteria applied to them by plugin.
This would be achieved by adding two filter hooks and one action hook to the update_callback, display_callback, and form_callback functions. I have included my recommendation.
New Filters:
display_widget_test
default_widget_options_update
New Action:
default_widget_options_display
function update_callback( $widget_args = 1 ) { global $wp_registered_widgets; if ( is_numeric($widget_args) ) $widget_args = array( 'number' => $widget_args ); $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) ); $all_instances = $this->get_settings(); // We need to update the data if ( !$this->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(); if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) { // Delete the settings for this instance of the widget if ( isset($_POST['widget-id']) ) $del_id = $_POST['widget-id']; else return; if ( $this->_get_display_callback() == $wp_registered_widgets[$del_id]['callback'] && isset($wp_registered_widgets[$del_id]['params'][0]['number']) ) { $number = $wp_registered_widgets[$del_id]['params'][0]['number']; if ( $this->id_base . '-' . $number == $del_id ) { unset($all_instances[$number]); } } } else { foreach ( (array) $_POST['widget-' . $this->id_base] as $number => $new_instance ) { $new_instance = stripslashes_deep($new_instance); $this->_set($number); if ( isset($all_instances[$number]) ) $instance = $this->update($new_instance, $all_instances[$number]); else $instance = $this->update($new_instance, array()); if ( false !== $instance ) { $widget_details_compare = array($new_instance,$instance); $instance = apply_filters('default_widget_options_update',$widget_details_compare); //Callback to apply default or other options to be added to list of widget options to store in database $all_instances[$number] = $instance; } } } $this->save_settings($all_instances); $this->updated = true; } }
function display_callback( $args, $widget_args = 1 ) { if ( is_numeric($widget_args) ) $widget_args = array( 'number' => $widget_args ); $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) ); $this->_set( $widget_args['number'] ); $settings = $this->get_settings(); if ( array_key_exists( $this->number, $settings ) ) { $hide_widget = apply_filters('widget_display_test', $settings[$this->number]); //Add hook to test if widget should be displayed. if (!$hide_widget) $this->widget($args, $settings[$this->number]); //Test and display widget. $this->widget($args, $settings[$this->number]); } }
function form_callback( $widget_args = 1 ) { if ( is_numeric($widget_args) ) $widget_args = array( 'number' => $widget_args ); $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) ); $all_instances = $this->get_settings(); if ( -1 == $widget_args['number'] ) { // We echo out a form where 'number' can be set later $this->_set('__i__'); $instance = array(); } else { $this->_set($widget_args['number']); $instance = $all_instances[ $widget_args['number'] ]; } $this->form($instance); $widget_details = array($this->id_base,$this->number,$instance); do_action('default_widget_options_display', $widget_details); //Add action hook to display default widget information or options }
Please let me know what you think. I believe that these three small changes would eaisly allow for the adding of plugins to filter the display of individual widgets and add widget options to unique instances of multiwidgets.
Many thanks,
Chris
#46
@
15 years ago
- Resolution set to fixed
- Status changed from new to closed
Closing this tracker bug. New tickets for further issues.
#55
@
15 years ago
- Priority changed from high to low
- Severity changed from normal to minor
- Type changed from task (blessed) to defect (bug)
I really like the new widgets administration. I've remade all my widgets with the new API (fairly trivial since I was already utilizing the MultiWidget Class), and I've recently come across one bit of strange behavior while using the nightly build:
When attempting to display widgets via returning the cached contents in the way that the default recent posts widget does, my 3rd sidebar (always the 3rd registered, no matter which is registered 3rd, but possibly other sidebars also) will only display additional instances (past the first instance in the sidebar) of any given widget. That is to say, I would have to place 2 recent posts widgets in my 3rd sidebar to get just 1 to output.
In order to counter the problem, I've changed my own widgets that use similar caching methods, so that this bit of code:
if (isset($cache[$args['widget_id']])) return $cache[$args['widget_id']];
reads instead as such:
if (isset($cache[$args['widget_id']])) echo substr($cache[$args['widget_id']][1], 1);
Is this a bug that could/should be easily fixed before the official 2.8 release?
Multi-Widget Class with commented Example