3 * API for creating dynamic sidebar without hardcoding functionality into
4 * themes. Includes both internal WordPress routines and theme use routines.
6 * This functionality was found in a plugin before WordPress 2.2 release which
7 * included it in the core from that point on.
9 * @link https://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
10 * @link https://codex.wordpress.org/Plugins/WordPress_Widgets_Api Widgets API
17 * This class must be extended for each widget and WP_Widget::widget(), WP_Widget::update()
18 * and WP_Widget::form() need to be over-ridden.
27 * Root ID for all widgets of this type.
36 * Name for this widget type.
45 * Option array passed to {@see wp_register_sidebar_widget()}.
51 public $widget_options;
54 * Option array passed to {@see wp_register_widget_control()}.
60 public $control_options;
63 * Unique ID number of the current instance.
69 public $number = false;
72 * Unique ID string of the current instance (id_base-number).
81 * Whether the widget data has been updated.
83 * Set to true when the data is updated after a POST submit - ensures it does
90 public $updated = false;
92 // Member functions that you must over-ride.
95 * Echo the widget content.
97 * Subclasses should over-ride this function to generate their widget code.
102 * @param array $args Display arguments including before_title, after_title,
103 * before_widget, and after_widget.
104 * @param array $instance The settings for the particular instance of the widget.
106 public function widget( $args, $instance ) {
107 die('function WP_Widget::widget() must be over-ridden in a sub-class.');
111 * Update a particular instance.
113 * This function should check that $new_instance is set correctly. The newly-calculated
114 * value of `$instance` should be returned. If false is returned, the instance won't be
120 * @param array $new_instance New settings for this instance as input by the user via
121 * {@see WP_Widget::form()}.
122 * @param array $old_instance Old settings for this instance.
123 * @return array Settings to save or bool false to cancel saving.
125 public function update( $new_instance, $old_instance ) {
126 return $new_instance;
130 * Output the settings update form.
135 * @param array $instance Current settings.
136 * @return string Default return is 'noform'.
138 public function form($instance) {
139 echo '<p class="no-options-widget">' . __('There are no options for this widget.') . '</p>';
143 // Functions you'll need to call.
151 * @param string $id_base Optional Base ID for the widget, lowercase and unique. If left empty,
152 * a portion of the widget's class name will be used Has to be unique.
153 * @param string $name Name for the widget displayed on the configuration page.
154 * @param array $widget_options Optional. Widget options. See {@see wp_register_sidebar_widget()} for
155 * information on accepted arguments. Default empty array.
156 * @param array $control_options Optional. Widget control options. See {@see wp_register_widget_control()}
157 * for information on accepted arguments. Default empty array.
159 public function __construct( $id_base, $name, $widget_options = array(), $control_options = array() ) {
160 $this->id_base = empty($id_base) ? preg_replace( '/(wp_)?widget_/', '', strtolower(get_class($this)) ) : strtolower($id_base);
162 $this->option_name = 'widget_' . $this->id_base;
163 $this->widget_options = wp_parse_args( $widget_options, array('classname' => $this->option_name) );
164 $this->control_options = wp_parse_args( $control_options, array('id_base' => $this->id_base) );
170 * @param string $id_base
171 * @param string $name
172 * @param array $widget_options
173 * @param array $control_options
175 public function WP_Widget( $id_base, $name, $widget_options = array(), $control_options = array() ) {
176 _deprecated_constructor( 'WP_Widget', '4.3.0' );
177 WP_Widget::__construct( $id_base, $name, $widget_options, $control_options );
181 * Constructs name attributes for use in form() fields
183 * This function should be used in form() methods to create name attributes for fields to be saved by update()
185 * @param string $field_name Field name
186 * @return string Name attribute for $field_name
188 public function get_field_name($field_name) {
189 return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field_name . ']';
193 * Constructs id attributes for use in {@see WP_Widget::form()} fields.
195 * This function should be used in form() methods to create id attributes
196 * for fields to be saved by {@see WP_Widget::update()}.
201 * @param string $field_name Field name.
202 * @return string ID attribute for `$field_name`.
204 public function get_field_id( $field_name ) {
205 return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field_name;
209 * Register all widget instances of this widget class.
214 public function _register() {
215 $settings = $this->get_settings();
218 // When $settings is an array-like object, get an intrinsic array for use with array_keys().
219 if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
220 $settings = $settings->getArrayCopy();
223 if ( is_array( $settings ) ) {
224 foreach ( array_keys( $settings ) as $number ) {
225 if ( is_numeric( $number ) ) {
226 $this->_set( $number );
227 $this->_register_one( $number );
234 // If there are none, we register the widget's existence with a generic template.
236 $this->_register_one();
241 * Set the internal order number for the widget instance.
246 * @param int $number The unique order number of this widget instance compared to other
247 * instances of the same class.
249 public function _set($number) {
250 $this->number = $number;
251 $this->id = $this->id_base . '-' . $number;
257 public function _get_display_callback() {
258 return array($this, 'display_callback');
263 public function _get_update_callback() {
264 return array($this, 'update_callback');
269 public function _get_form_callback() {
270 return array($this, 'form_callback');
274 * Determine whether the current request is inside the Customizer preview.
276 * If true -- the current request is inside the Customizer preview, then
277 * the object cache gets suspended and widgets should check this to decide
278 * whether they should store anything persistently to the object cache,
279 * to transients, or anywhere else.
284 * @global WP_Customize_Manager $wp_customize
286 * @return bool True if within the Customizer preview, false if not.
288 public function is_preview() {
289 global $wp_customize;
290 return ( isset( $wp_customize ) && $wp_customize->is_preview() ) ;
294 * Generate the actual widget content (Do NOT override).
296 * Finds the instance and calls {@see WP_Widget::widget()}.
301 * @param array $args Display arguments. See {@see WP_Widget::widget()} for information
302 * on accepted arguments.
303 * @param int|array $widget_args {
304 * Optional. Internal order number of the widget instance, or array of multi-widget arguments.
307 * @type int $number Number increment used for multiples of the same widget.
310 public function display_callback( $args, $widget_args = 1 ) {
311 if ( is_numeric( $widget_args ) ) {
312 $widget_args = array( 'number' => $widget_args );
315 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
316 $this->_set( $widget_args['number'] );
317 $instances = $this->get_settings();
319 if ( isset( $instances[ $this->number ] ) ) {
320 $instance = $instances[ $this->number ];
323 * Filter the settings for a particular widget instance.
325 * Returning false will effectively short-circuit display of the widget.
329 * @param array $instance The current widget instance's settings.
330 * @param WP_Widget $this The current widget instance.
331 * @param array $args An array of default widget arguments.
333 $instance = apply_filters( 'widget_display_callback', $instance, $this, $args );
335 if ( false === $instance ) {
339 $was_cache_addition_suspended = wp_suspend_cache_addition();
340 if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
341 wp_suspend_cache_addition( true );
344 $this->widget( $args, $instance );
346 if ( $this->is_preview() ) {
347 wp_suspend_cache_addition( $was_cache_addition_suspended );
353 * Deal with changed settings (Do NOT override).
358 * @global array $wp_registered_widgets
360 * @param int $deprecated Not used.
362 public function update_callback( $deprecated = 1 ) {
363 global $wp_registered_widgets;
365 $all_instances = $this->get_settings();
367 // We need to update the data
368 if ( $this->updated )
371 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
372 // Delete the settings for this instance of the widget
373 if ( isset($_POST['the-widget-id']) )
374 $del_id = $_POST['the-widget-id'];
378 if ( isset($wp_registered_widgets[$del_id]['params'][0]['number']) ) {
379 $number = $wp_registered_widgets[$del_id]['params'][0]['number'];
381 if ( $this->id_base . '-' . $number == $del_id )
382 unset($all_instances[$number]);
385 if ( isset($_POST['widget-' . $this->id_base]) && is_array($_POST['widget-' . $this->id_base]) ) {
386 $settings = $_POST['widget-' . $this->id_base];
387 } elseif ( isset($_POST['id_base']) && $_POST['id_base'] == $this->id_base ) {
388 $num = $_POST['multi_number'] ? (int) $_POST['multi_number'] : (int) $_POST['widget_number'];
389 $settings = array( $num => array() );
394 foreach ( $settings as $number => $new_instance ) {
395 $new_instance = stripslashes_deep($new_instance);
396 $this->_set($number);
398 $old_instance = isset($all_instances[$number]) ? $all_instances[$number] : array();
400 $was_cache_addition_suspended = wp_suspend_cache_addition();
401 if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
402 wp_suspend_cache_addition( true );
405 $instance = $this->update( $new_instance, $old_instance );
407 if ( $this->is_preview() ) {
408 wp_suspend_cache_addition( $was_cache_addition_suspended );
412 * Filter a widget's settings before saving.
414 * Returning false will effectively short-circuit the widget's ability
415 * to update settings.
419 * @param array $instance The current widget instance's settings.
420 * @param array $new_instance Array of new widget settings.
421 * @param array $old_instance Array of old widget settings.
422 * @param WP_Widget $this The current widget instance.
424 $instance = apply_filters( 'widget_update_callback', $instance, $new_instance, $old_instance, $this );
425 if ( false !== $instance ) {
426 $all_instances[$number] = $instance;
429 break; // run only once
433 $this->save_settings($all_instances);
434 $this->updated = true;
438 * Generate the widget control form (Do NOT override).
443 * @param int|array $widget_args Widget instance number or array of widget arguments.
444 * @return string|null
446 public function form_callback( $widget_args = 1 ) {
447 if ( is_numeric($widget_args) )
448 $widget_args = array( 'number' => $widget_args );
450 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
451 $all_instances = $this->get_settings();
453 if ( -1 == $widget_args['number'] ) {
454 // We echo out a form where 'number' can be set later
455 $this->_set('__i__');
458 $this->_set($widget_args['number']);
459 $instance = $all_instances[ $widget_args['number'] ];
463 * Filter the widget instance's settings before displaying the control form.
465 * Returning false effectively short-circuits display of the control form.
469 * @param array $instance The current widget instance's settings.
470 * @param WP_Widget $this The current widget instance.
472 $instance = apply_filters( 'widget_form_callback', $instance, $this );
475 if ( false !== $instance ) {
476 $return = $this->form($instance);
479 * Fires at the end of the widget control form.
481 * Use this hook to add extra fields to the widget form. The hook
482 * is only fired if the value passed to the 'widget_form_callback'
485 * Note: If the widget has no form, the text echoed from the default
486 * form method can be hidden using CSS.
490 * @param WP_Widget $this The widget instance, passed by reference.
491 * @param null $return Return null if new fields are added.
492 * @param array $instance An array of the widget's settings.
494 do_action_ref_array( 'in_widget_form', array( &$this, &$return, $instance ) );
500 * Register an instance of the widget class.
505 * @param integer $number Optional. The unique order number of this widget instance
506 * compared to other instances of the same class. Default -1.
508 public function _register_one( $number = -1 ) {
509 wp_register_sidebar_widget( $this->id, $this->name, $this->_get_display_callback(), $this->widget_options, array( 'number' => $number ) );
510 _register_widget_update_callback( $this->id_base, $this->_get_update_callback(), $this->control_options, array( 'number' => -1 ) );
511 _register_widget_form_callback( $this->id, $this->name, $this->_get_form_callback(), $this->control_options, array( 'number' => $number ) );
515 * Save the settings for all instances of the widget class.
520 * @param array $settings Multi-dimensional array of widget instance settings.
522 public function save_settings( $settings ) {
523 $settings['_multiwidget'] = 1;
524 update_option( $this->option_name, $settings );
528 * Get the settings for all instances of the widget class.
533 * @return array Multi-dimensional array of widget instance settings.
535 public function get_settings() {
537 $settings = get_option( $this->option_name );
539 if ( false === $settings && isset( $this->alt_option_name ) ) {
540 $settings = get_option( $this->alt_option_name );
543 if ( ! is_array( $settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) {
547 if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) {
548 // Old format, convert if single widget.
549 $settings = wp_convert_widget_settings( $this->id_base, $this->option_name, $settings );
552 unset( $settings['_multiwidget'], $settings['__i__'] );
558 * Singleton that registers and instantiates WP_Widget classes.
561 * @subpackage Widgets
564 class WP_Widget_Factory {
565 public $widgets = array();
570 public function __construct() {
571 add_action( 'widgets_init', array( $this, '_register_widgets' ), 100 );
577 public function WP_Widget_Factory() {
578 _deprecated_constructor( 'WP_Widget_Factory', '4.2.0' );
583 * Register a widget subclass.
588 * @param string $widget_class The name of a {@see WP_Widget} subclass.
590 public function register( $widget_class ) {
591 $this->widgets[$widget_class] = new $widget_class();
595 * Un-register a widget subclass.
600 * @param string $widget_class The name of a {@see WP_Widget} subclass.
602 public function unregister( $widget_class ) {
603 unset( $this->widgets[ $widget_class ] );
607 * Utility method for adding widgets to the registered widgets global.
612 * @global array $wp_registered_widgets
614 public function _register_widgets() {
615 global $wp_registered_widgets;
616 $keys = array_keys($this->widgets);
617 $registered = array_keys($wp_registered_widgets);
618 $registered = array_map('_get_widget_id_base', $registered);
620 foreach ( $keys as $key ) {
621 // don't register new widget if old widget with the same id is already registered
622 if ( in_array($this->widgets[$key]->id_base, $registered, true) ) {
623 unset($this->widgets[$key]);
627 $this->widgets[$key]->_register();
632 /* Global Variables */
635 global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
638 * Stores the sidebars, since many themes can have more than one.
640 * @global array $wp_registered_sidebars
643 $wp_registered_sidebars = array();
646 * Stores the registered widgets.
648 * @global array $wp_registered_widgets
651 $wp_registered_widgets = array();
654 * Stores the registered widget control (options).
656 * @global array $wp_registered_widget_controls
659 $wp_registered_widget_controls = array();
661 * @global array $wp_registered_widget_updates
663 $wp_registered_widget_updates = array();
668 * @global array $_wp_sidebars_widgets
670 $_wp_sidebars_widgets = array();
675 * @global array $_wp_deprecated_widgets_callbacks
677 $GLOBALS['_wp_deprecated_widgets_callbacks'] = array(
679 'wp_widget_pages_control',
680 'wp_widget_calendar',
681 'wp_widget_calendar_control',
682 'wp_widget_archives',
683 'wp_widget_archives_control',
686 'wp_widget_meta_control',
688 'wp_widget_recent_entries',
689 'wp_widget_recent_entries_control',
690 'wp_widget_tag_cloud',
691 'wp_widget_tag_cloud_control',
692 'wp_widget_categories',
693 'wp_widget_categories_control',
695 'wp_widget_text_control',
697 'wp_widget_rss_control',
698 'wp_widget_recent_comments',
699 'wp_widget_recent_comments_control'
702 /* Template tags & API functions */
707 * Registers a WP_Widget widget
713 * @global WP_Widget_Factory $wp_widget_factory
715 * @param string $widget_class The name of a class that extends WP_Widget
717 function register_widget($widget_class) {
718 global $wp_widget_factory;
720 $wp_widget_factory->register($widget_class);
724 * Unregister a widget
726 * Unregisters a WP_Widget widget. Useful for unregistering default widgets.
727 * Run within a function hooked to the widgets_init action.
733 * @global WP_Widget_Factory $wp_widget_factory
735 * @param string $widget_class The name of a class that extends WP_Widget
737 function unregister_widget($widget_class) {
738 global $wp_widget_factory;
740 $wp_widget_factory->unregister($widget_class);
744 * Creates multiple sidebars.
746 * If you wanted to quickly create multiple sidebars for a theme or internally.
747 * This function will allow you to do so. If you don't pass the 'name' and/or
748 * 'id' in `$args`, then they will be built for you.
752 * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here.
754 * @global array $wp_registered_sidebars
756 * @param int $number Optional. Number of sidebars to create. Default 1.
757 * @param array|string $args {
758 * Optional. Array or string of arguments for building a sidebar.
760 * @type string $id The base string of the unique identifier for each sidebar. If provided, and multiple
761 * sidebars are being defined, the id will have "-2" appended, and so on.
762 * Default 'sidebar-' followed by the number the sidebar creation is currently at.
763 * @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering
764 * more than one sidebar, include '%d' in the string as a placeholder for the uniquely
765 * assigned number for each sidebar.
766 * Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'.
769 function register_sidebars( $number = 1, $args = array() ) {
770 global $wp_registered_sidebars;
771 $number = (int) $number;
773 if ( is_string($args) )
774 parse_str($args, $args);
776 for ( $i = 1; $i <= $number; $i++ ) {
780 $_args['name'] = isset($args['name']) ? sprintf($args['name'], $i) : sprintf(__('Sidebar %d'), $i);
782 $_args['name'] = isset($args['name']) ? $args['name'] : __('Sidebar');
784 // Custom specified ID's are suffixed if they exist already.
785 // Automatically generated sidebar names need to be suffixed regardless starting at -0
786 if ( isset($args['id']) ) {
787 $_args['id'] = $args['id'];
788 $n = 2; // Start at -2 for conflicting custom ID's
789 while ( isset($wp_registered_sidebars[$_args['id']]) )
790 $_args['id'] = $args['id'] . '-' . $n++;
792 $n = count($wp_registered_sidebars);
794 $_args['id'] = 'sidebar-' . ++$n;
795 } while ( isset($wp_registered_sidebars[$_args['id']]) );
797 register_sidebar($_args);
802 * Builds the definition for a single sidebar and returns the ID.
804 * Accepts either a string or an array and then parses that against a set
805 * of default arguments for the new sidebar. WordPress will automatically
806 * generate a sidebar ID and name based on the current number of registered
807 * sidebars if those arguments are not included.
809 * When allowing for automatic generation of the name and ID parameters, keep
810 * in mind that the incrementor for your sidebar can change over time depending
811 * on what other plugins and themes are installed.
813 * If theme support for 'widgets' has not yet been added when this function is
814 * called, it will be automatically enabled through the use of add_theme_support()
818 * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
820 * @param array|string $args {
821 * Optional. Array or string of arguments for the sidebar being registered.
823 * @type string $name The name or title of the sidebar displayed in the Widgets
824 * interface. Default 'Sidebar $instance'.
825 * @type string $id The unique identifier by which the sidebar will be called.
826 * Default 'sidebar-$instance'.
827 * @type string $description Description of the sidebar, displayed in the Widgets interface.
828 * Default empty string.
829 * @type string $class Extra CSS class to assign to the sidebar in the Widgets interface.
831 * @type string $before_widget HTML content to prepend to each widget's HTML output when
832 * assigned to this sidebar. Default is an opening list item element.
833 * @type string $after_widget HTML content to append to each widget's HTML output when
834 * assigned to this sidebar. Default is a closing list item element.
835 * @type string $before_title HTML content to prepend to the sidebar title when displayed.
836 * Default is an opening h2 element.
837 * @type string $after_title HTML content to append to the sidebar title when displayed.
838 * Default is a closing h2 element.
840 * @return string Sidebar ID added to $wp_registered_sidebars global.
842 function register_sidebar($args = array()) {
843 global $wp_registered_sidebars;
845 $i = count($wp_registered_sidebars) + 1;
847 $id_is_empty = empty( $args['id'] );
850 'name' => sprintf(__('Sidebar %d'), $i ),
851 'id' => "sidebar-$i",
854 'before_widget' => '<li id="%1$s" class="widget %2$s">',
855 'after_widget' => "</li>\n",
856 'before_title' => '<h2 class="widgettitle">',
857 'after_title' => "</h2>\n",
860 $sidebar = wp_parse_args( $args, $defaults );
862 if ( $id_is_empty ) {
863 /* translators: 1: the id argument, 2: sidebar name, 3: recommended id value */
864 _doing_it_wrong( __FUNCTION__, sprintf( __( 'No %1$s was set in the arguments array for the "%2$s" sidebar. Defaulting to "%3$s". Manually set the %1$s to "%3$s" to silence this notice and keep existing sidebar content.' ), '<code>id</code>', $sidebar['name'], $sidebar['id'] ), '4.2.0' );
867 $wp_registered_sidebars[$sidebar['id']] = $sidebar;
869 add_theme_support('widgets');
872 * Fires once a sidebar has been registered.
876 * @param array $sidebar Parsed arguments for the registered sidebar.
878 do_action( 'register_sidebar', $sidebar );
880 return $sidebar['id'];
884 * Removes a sidebar from the list.
888 * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
890 * @param string $name The ID of the sidebar when it was added.
892 function unregister_sidebar( $name ) {
893 global $wp_registered_sidebars;
895 unset( $wp_registered_sidebars[ $name ] );
899 * Register an instance of a widget.
901 * The default widget option is 'classname' that can be overridden.
903 * The function can also be used to un-register widgets when `$output_callback`
904 * parameter is an empty string.
908 * @global array $wp_registered_widgets Uses stored registered widgets.
909 * @global array $wp_register_widget_defaults Retrieves widget defaults.
910 * @global array $wp_registered_widget_updates
911 * @global array $_wp_deprecated_widgets_callbacks
913 * @param int|string $id Widget ID.
914 * @param string $name Widget display title.
915 * @param callback $output_callback Run when widget is called.
916 * @param array $options {
917 * Optional. An array of supplementary widget options for the instance.
919 * @type string $classname Class name for the widget's HTML container. Default is a shortened
920 * version of the output callback name.
921 * @type string $description Widget description for display in the widget administration
922 * panel and/or theme.
925 function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
926 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
928 $id = strtolower($id);
930 if ( empty($output_callback) ) {
931 unset($wp_registered_widgets[$id]);
935 $id_base = _get_widget_id_base($id);
936 if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
937 unset( $wp_registered_widget_controls[ $id ] );
938 unset( $wp_registered_widget_updates[ $id_base ] );
942 $defaults = array('classname' => $output_callback);
943 $options = wp_parse_args($options, $defaults);
947 'callback' => $output_callback,
948 'params' => array_slice(func_get_args(), 4)
950 $widget = array_merge($widget, $options);
952 if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
955 * Fires once for each registered widget.
959 * @param array $widget An array of default widget arguments.
961 do_action( 'wp_register_sidebar_widget', $widget );
962 $wp_registered_widgets[$id] = $widget;
967 * Retrieve description for widget.
969 * When registering widgets, the options can also include 'description' that
970 * describes the widget for display on the widget administration panel or
975 * @global array $wp_registered_widgets
977 * @param int|string $id Widget ID.
978 * @return string|void Widget description, if available.
980 function wp_widget_description( $id ) {
981 if ( !is_scalar($id) )
984 global $wp_registered_widgets;
986 if ( isset($wp_registered_widgets[$id]['description']) )
987 return esc_html( $wp_registered_widgets[$id]['description'] );
991 * Retrieve description for a sidebar.
993 * When registering sidebars a 'description' parameter can be included that
994 * describes the sidebar for display on the widget administration panel.
998 * @global array $wp_registered_sidebars
1000 * @param string $id sidebar ID.
1001 * @return string|void Sidebar description, if available.
1003 function wp_sidebar_description( $id ) {
1004 if ( !is_scalar($id) )
1007 global $wp_registered_sidebars;
1009 if ( isset($wp_registered_sidebars[$id]['description']) )
1010 return esc_html( $wp_registered_sidebars[$id]['description'] );
1014 * Remove widget from sidebar.
1018 * @param int|string $id Widget ID.
1020 function wp_unregister_sidebar_widget($id) {
1023 * Fires just before a widget is removed from a sidebar.
1027 * @param int $id The widget ID.
1029 do_action( 'wp_unregister_sidebar_widget', $id );
1031 wp_register_sidebar_widget($id, '', '');
1032 wp_unregister_widget_control($id);
1036 * Registers widget control callback for customizing options.
1038 * The options contains the 'height', 'width', and 'id_base' keys. The 'height'
1039 * option is never used. The 'width' option is the width of the fully expanded
1040 * control form, but try hard to use the default width. The 'id_base' is for
1041 * multi-widgets (widgets which allow multiple instances such as the text
1042 * widget), an id_base must be provided. The widget id will end up looking like
1043 * `{$id_base}-{$unique_number}`.
1047 * @todo Document `$options` as a hash notation, re: WP_Widget::__construct() cross-reference.
1048 * @todo `$params` parameter?
1050 * @global array $wp_registered_widget_controls
1051 * @global array $wp_registered_widget_updates
1052 * @global array $wp_registered_widgets
1053 * @global array $_wp_deprecated_widgets_callbacks
1055 * @param int|string $id Sidebar ID.
1056 * @param string $name Sidebar display name.
1057 * @param callback $control_callback Run when sidebar is displayed.
1058 * @param array|string $options Optional. Widget options. See description above. Default empty array.
1060 function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
1061 global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
1063 $id = strtolower($id);
1064 $id_base = _get_widget_id_base($id);
1066 if ( empty($control_callback) ) {
1067 unset($wp_registered_widget_controls[$id]);
1068 unset($wp_registered_widget_updates[$id_base]);
1072 if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
1073 unset( $wp_registered_widgets[ $id ] );
1077 if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1080 $defaults = array('width' => 250, 'height' => 200 ); // height is never used
1081 $options = wp_parse_args($options, $defaults);
1082 $options['width'] = (int) $options['width'];
1083 $options['height'] = (int) $options['height'];
1088 'callback' => $control_callback,
1089 'params' => array_slice(func_get_args(), 4)
1091 $widget = array_merge($widget, $options);
1093 $wp_registered_widget_controls[$id] = $widget;
1095 if ( isset($wp_registered_widget_updates[$id_base]) )
1098 if ( isset($widget['params'][0]['number']) )
1099 $widget['params'][0]['number'] = -1;
1101 unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
1102 $wp_registered_widget_updates[$id_base] = $widget;
1106 * @global array $wp_registered_widget_updates
1108 * @param string $id_base
1109 * @param callable $update_callback
1110 * @param array $options
1112 function _register_widget_update_callback($id_base, $update_callback, $options = array()) {
1113 global $wp_registered_widget_updates;
1115 if ( isset($wp_registered_widget_updates[$id_base]) ) {
1116 if ( empty($update_callback) )
1117 unset($wp_registered_widget_updates[$id_base]);
1122 'callback' => $update_callback,
1123 'params' => array_slice(func_get_args(), 3)
1126 $widget = array_merge($widget, $options);
1127 $wp_registered_widget_updates[$id_base] = $widget;
1132 * @global array $wp_registered_widget_controls
1134 * @param int|string $id
1135 * @param string $name
1136 * @param callable $form_callback
1137 * @param array $options
1139 function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
1140 global $wp_registered_widget_controls;
1142 $id = strtolower($id);
1144 if ( empty($form_callback) ) {
1145 unset($wp_registered_widget_controls[$id]);
1149 if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1152 $defaults = array('width' => 250, 'height' => 200 );
1153 $options = wp_parse_args($options, $defaults);
1154 $options['width'] = (int) $options['width'];
1155 $options['height'] = (int) $options['height'];
1160 'callback' => $form_callback,
1161 'params' => array_slice(func_get_args(), 4)
1163 $widget = array_merge($widget, $options);
1165 $wp_registered_widget_controls[$id] = $widget;
1169 * Remove control callback for widget.
1173 * @param int|string $id Widget ID.
1175 function wp_unregister_widget_control($id) {
1176 wp_register_widget_control( $id, '', '' );
1180 * Display dynamic sidebar.
1182 * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
1183 * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
1184 * Otherwise, you can pass in a numerical index to display the sidebar at that index.
1188 * @global array $wp_registered_sidebars
1189 * @global array $wp_registered_widgets
1191 * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
1192 * @return bool True, if widget sidebar was found and called. False if not found or not called.
1194 function dynamic_sidebar($index = 1) {
1195 global $wp_registered_sidebars, $wp_registered_widgets;
1197 if ( is_int($index) ) {
1198 $index = "sidebar-$index";
1200 $index = sanitize_title($index);
1201 foreach ( (array) $wp_registered_sidebars as $key => $value ) {
1202 if ( sanitize_title($value['name']) == $index ) {
1209 $sidebars_widgets = wp_get_sidebars_widgets();
1210 if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
1211 /** This action is documented in wp-includes/widgets.php */
1212 do_action( 'dynamic_sidebar_before', $index, false );
1213 /** This action is documented in wp-includes/widgets.php */
1214 do_action( 'dynamic_sidebar_after', $index, false );
1215 /** This filter is documented in wp-includes/widgets.php */
1216 return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
1220 * Fires before widgets are rendered in a dynamic sidebar.
1222 * Note: The action also fires for empty sidebars, and on both the front-end
1223 * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1227 * @param int|string $index Index, name, or ID of the dynamic sidebar.
1228 * @param bool $has_widgets Whether the sidebar is populated with widgets.
1231 do_action( 'dynamic_sidebar_before', $index, true );
1232 $sidebar = $wp_registered_sidebars[$index];
1235 foreach ( (array) $sidebars_widgets[$index] as $id ) {
1237 if ( !isset($wp_registered_widgets[$id]) ) continue;
1239 $params = array_merge(
1240 array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
1241 (array) $wp_registered_widgets[$id]['params']
1244 // Substitute HTML id and class attributes into before_widget
1246 foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
1247 if ( is_string($cn) )
1248 $classname_ .= '_' . $cn;
1249 elseif ( is_object($cn) )
1250 $classname_ .= '_' . get_class($cn);
1252 $classname_ = ltrim($classname_, '_');
1253 $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
1256 * Filter the parameters passed to a widget's display callback.
1258 * Note: The filter is evaluated on both the front-end and back-end,
1259 * including for the Inactive Widgets sidebar on the Widgets screen.
1263 * @see register_sidebar()
1265 * @param array $params {
1266 * @type array $args {
1267 * An array of widget display arguments.
1269 * @type string $name Name of the sidebar the widget is assigned to.
1270 * @type string $id ID of the sidebar the widget is assigned to.
1271 * @type string $description The sidebar description.
1272 * @type string $class CSS class applied to the sidebar container.
1273 * @type string $before_widget HTML markup to prepend to each widget in the sidebar.
1274 * @type string $after_widget HTML markup to append to each widget in the sidebar.
1275 * @type string $before_title HTML markup to prepend to the widget title when displayed.
1276 * @type string $after_title HTML markup to append to the widget title when displayed.
1277 * @type string $widget_id ID of the widget.
1278 * @type string $widget_name Name of the widget.
1280 * @type array $widget_args {
1281 * An array of multi-widget arguments.
1283 * @type int $number Number increment used for multiples of the same widget.
1287 $params = apply_filters( 'dynamic_sidebar_params', $params );
1289 $callback = $wp_registered_widgets[$id]['callback'];
1292 * Fires before a widget's display callback is called.
1294 * Note: The action fires on both the front-end and back-end, including
1295 * for widgets in the Inactive Widgets sidebar on the Widgets screen.
1297 * The action is not fired for empty sidebars.
1301 * @param array $widget_id {
1302 * An associative array of widget arguments.
1304 * @type string $name Name of the widget.
1305 * @type string $id Widget ID.
1306 * @type array|callback $callback When the hook is fired on the front-end, $callback is an array
1307 * containing the widget object. Fired on the back-end, $callback
1308 * is 'wp_widget_control', see $_callback.
1309 * @type array $params An associative array of multi-widget arguments.
1310 * @type string $classname CSS class applied to the widget container.
1311 * @type string $description The widget description.
1312 * @type array $_callback When the hook is fired on the back-end, $_callback is populated
1313 * with an array containing the widget object, see $callback.
1316 do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
1318 if ( is_callable($callback) ) {
1319 call_user_func_array($callback, $params);
1325 * Fires after widgets are rendered in a dynamic sidebar.
1327 * Note: The action also fires for empty sidebars, and on both the front-end
1328 * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1332 * @param int|string $index Index, name, or ID of the dynamic sidebar.
1333 * @param bool $has_widgets Whether the sidebar is populated with widgets.
1336 do_action( 'dynamic_sidebar_after', $index, true );
1339 * Filter whether a sidebar has widgets.
1341 * Note: The filter is also evaluated for empty sidebars, and on both the front-end
1342 * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1346 * @param bool $did_one Whether at least one widget was rendered in the sidebar.
1348 * @param int|string $index Index, name, or ID of the dynamic sidebar.
1350 return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
1354 * Whether widget is displayed on the front-end.
1356 * Either $callback or $id_base can be used
1357 * $id_base is the first argument when extending WP_Widget class
1358 * Without the optional $widget_id parameter, returns the ID of the first sidebar
1359 * in which the first instance of the widget with the given callback or $id_base is found.
1360 * With the $widget_id parameter, returns the ID of the sidebar where
1361 * the widget with that callback/$id_base AND that ID is found.
1363 * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
1364 * this function has to run after widgets have initialized, at action 'init' or later.
1368 * @global array $wp_registered_widgets
1370 * @param string $callback Optional, Widget callback to check.
1371 * @param int $widget_id Optional, but needed for checking. Widget ID.
1372 * @param string $id_base Optional, the base ID of a widget created by extending WP_Widget.
1373 * @param bool $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
1374 * @return string|false False if widget is not active or id of sidebar in which the widget is active.
1376 function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
1377 global $wp_registered_widgets;
1379 $sidebars_widgets = wp_get_sidebars_widgets();
1381 if ( is_array($sidebars_widgets) ) {
1382 foreach ( $sidebars_widgets as $sidebar => $widgets ) {
1383 if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
1387 if ( is_array($widgets) ) {
1388 foreach ( $widgets as $widget ) {
1389 if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
1390 if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
1401 * Whether the dynamic sidebar is enabled and used by theme.
1405 * @global array $wp_registered_widgets
1406 * @global array $wp_registered_sidebars
1408 * @return bool True, if using widgets. False, if not using widgets.
1410 function is_dynamic_sidebar() {
1411 global $wp_registered_widgets, $wp_registered_sidebars;
1412 $sidebars_widgets = get_option('sidebars_widgets');
1413 foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
1414 if ( count($sidebars_widgets[$index]) ) {
1415 foreach ( (array) $sidebars_widgets[$index] as $widget )
1416 if ( array_key_exists($widget, $wp_registered_widgets) )
1424 * Whether a sidebar is in use.
1428 * @param string|int $index Sidebar name, id or number to check.
1429 * @return bool true if the sidebar is in use, false otherwise.
1431 function is_active_sidebar( $index ) {
1432 $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
1433 $sidebars_widgets = wp_get_sidebars_widgets();
1434 $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
1437 * Filter whether a dynamic sidebar is considered "active".
1441 * @param bool $is_active_sidebar Whether or not the sidebar should be considered "active".
1442 * In other words, whether the sidebar contains any widgets.
1443 * @param int|string $index Index, name, or ID of the dynamic sidebar.
1445 return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
1448 /* Internal Functions */
1451 * Retrieve full list of sidebars and their widget instance IDs.
1453 * Will upgrade sidebar widget list, if needed. Will also save updated list, if
1459 * @global array $_wp_sidebars_widgets
1460 * @global array $sidebars_widgets
1462 * @param bool $deprecated Not used (argument deprecated).
1463 * @return array Upgraded list of widgets to version 3 array format when called from the admin.
1465 function wp_get_sidebars_widgets( $deprecated = true ) {
1466 if ( $deprecated !== true )
1467 _deprecated_argument( __FUNCTION__, '2.8.1' );
1469 global $_wp_sidebars_widgets, $sidebars_widgets;
1471 // If loading from front page, consult $_wp_sidebars_widgets rather than options
1472 // to see if wp_convert_widget_settings() has made manipulations in memory.
1473 if ( !is_admin() ) {
1474 if ( empty($_wp_sidebars_widgets) )
1475 $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
1477 $sidebars_widgets = $_wp_sidebars_widgets;
1479 $sidebars_widgets = get_option('sidebars_widgets', array());
1482 if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
1483 unset($sidebars_widgets['array_version']);
1486 * Filter the list of sidebars and their widgets.
1490 * @param array $sidebars_widgets An associative array of sidebars and their widgets.
1492 return apply_filters( 'sidebars_widgets', $sidebars_widgets );
1496 * Set the sidebar widget option to update sidebars.
1501 * @param array $sidebars_widgets Sidebar widgets and their settings.
1503 function wp_set_sidebars_widgets( $sidebars_widgets ) {
1504 if ( !isset( $sidebars_widgets['array_version'] ) )
1505 $sidebars_widgets['array_version'] = 3;
1506 update_option( 'sidebars_widgets', $sidebars_widgets );
1510 * Retrieve default registered sidebars list.
1515 * @global array $wp_registered_sidebars
1519 function wp_get_widget_defaults() {
1520 global $wp_registered_sidebars;
1522 $defaults = array();
1524 foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
1525 $defaults[$index] = array();
1531 * Convert the widget settings from single to multi-widget format.
1535 * @global array $_wp_sidebars_widgets
1537 * @param string $base_name
1538 * @param string $option_name
1539 * @param array $settings
1542 function wp_convert_widget_settings($base_name, $option_name, $settings) {
1543 // This test may need expanding.
1544 $single = $changed = false;
1545 if ( empty($settings) ) {
1548 foreach ( array_keys($settings) as $number ) {
1549 if ( 'number' == $number )
1551 if ( !is_numeric($number) ) {
1559 $settings = array( 2 => $settings );
1561 // If loading from the front page, update sidebar in memory but don't save to options
1563 $sidebars_widgets = get_option('sidebars_widgets');
1565 if ( empty($GLOBALS['_wp_sidebars_widgets']) )
1566 $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
1567 $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
1570 foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
1571 if ( is_array($sidebar) ) {
1572 foreach ( $sidebar as $i => $name ) {
1573 if ( $base_name == $name ) {
1574 $sidebars_widgets[$index][$i] = "$name-2";
1582 if ( is_admin() && $changed )
1583 update_option('sidebars_widgets', $sidebars_widgets);
1586 $settings['_multiwidget'] = 1;
1588 update_option( $option_name, $settings );
1594 * Output an arbitrary widget as a template tag.
1598 * @global WP_Widget_Factory $wp_widget_factory
1600 * @param string $widget The widget's PHP class name (see default-widgets.php).
1601 * @param array $instance Optional. The widget's instance settings. Default empty array.
1602 * @param array $args {
1603 * Optional. Array of arguments to configure the display of the widget.
1605 * @type string $before_widget HTML content that will be prepended to the widget's HTML output.
1606 * Default `<div class="widget %s">`, where `%s` is the widget's class name.
1607 * @type string $after_widget HTML content that will be appended to the widget's HTML output.
1609 * @type string $before_title HTML content that will be prepended to the widget's title when displayed.
1610 * Default `<h2 class="widgettitle">`.
1611 * @type string $after_title HTML content that will be appended to the widget's title when displayed.
1615 function the_widget( $widget, $instance = array(), $args = array() ) {
1616 global $wp_widget_factory;
1618 $widget_obj = $wp_widget_factory->widgets[$widget];
1619 if ( ! ( $widget_obj instanceof WP_Widget ) ) {
1623 $before_widget = sprintf('<div class="widget %s">', $widget_obj->widget_options['classname'] );
1624 $default_args = array( 'before_widget' => $before_widget, 'after_widget' => "</div>", 'before_title' => '<h2 class="widgettitle">', 'after_title' => '</h2>' );
1626 $args = wp_parse_args($args, $default_args);
1627 $instance = wp_parse_args($instance);
1630 * Fires before rendering the requested widget.
1634 * @param string $widget The widget's class name.
1635 * @param array $instance The current widget instance's settings.
1636 * @param array $args An array of the widget's sidebar arguments.
1638 do_action( 'the_widget', $widget, $instance, $args );
1640 $widget_obj->_set(-1);
1641 $widget_obj->widget($args, $instance);
1649 function _get_widget_id_base($id) {
1650 return preg_replace( '/-[0-9]+$/', '', $id );
1654 * Handle sidebars config after theme change
1659 * @global array $sidebars_widgets
1661 function _wp_sidebars_changed() {
1662 global $sidebars_widgets;
1664 if ( ! is_array( $sidebars_widgets ) )
1665 $sidebars_widgets = wp_get_sidebars_widgets();
1667 retrieve_widgets(true);
1671 * Look for "lost" widgets, this has to run at least on each theme change.
1675 * @global array $wp_registered_sidebars
1676 * @global array $sidebars_widgets
1677 * @global array $wp_registered_widgets
1679 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
1680 * of 'customize' defers updates for the Customizer.
1681 * @return array|void
1683 function retrieve_widgets( $theme_changed = false ) {
1684 global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
1686 $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
1689 $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
1690 if ( is_array( $old_sidebars_widgets ) ) {
1691 // time() that sidebars were stored is in $old_sidebars_widgets['time']
1692 $_sidebars_widgets = $old_sidebars_widgets['data'];
1694 if ( 'customize' !== $theme_changed ) {
1695 remove_theme_mod( 'sidebars_widgets' );
1698 foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1699 if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
1703 if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
1704 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
1705 unset( $_sidebars_widgets[$sidebar] );
1709 if ( empty( $sidebars_widgets ) )
1712 unset( $sidebars_widgets['array_version'] );
1714 $old = array_keys($sidebars_widgets);
1716 sort($registered_sidebar_keys);
1718 if ( $old == $registered_sidebar_keys )
1721 $_sidebars_widgets = array(
1722 'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
1725 unset( $sidebars_widgets['wp_inactive_widgets'] );
1727 foreach ( $wp_registered_sidebars as $id => $settings ) {
1728 if ( $theme_changed ) {
1729 $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
1731 // no theme change, grab only sidebars that are currently registered
1732 if ( isset( $sidebars_widgets[$id] ) ) {
1733 $_sidebars_widgets[$id] = $sidebars_widgets[$id];
1734 unset( $sidebars_widgets[$id] );
1739 foreach ( $sidebars_widgets as $val ) {
1740 if ( is_array($val) && ! empty( $val ) )
1741 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
1745 // discard invalid, theme-specific widgets from sidebars
1746 $shown_widgets = array();
1748 foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1749 if ( !is_array($widgets) )
1752 $_widgets = array();
1753 foreach ( $widgets as $widget ) {
1754 if ( isset($wp_registered_widgets[$widget]) )
1755 $_widgets[] = $widget;
1758 $_sidebars_widgets[$sidebar] = $_widgets;
1759 $shown_widgets = array_merge($shown_widgets, $_widgets);
1762 $sidebars_widgets = $_sidebars_widgets;
1763 unset($_sidebars_widgets, $_widgets);
1765 // find hidden/lost multi-widget instances
1766 $lost_widgets = array();
1767 foreach ( $wp_registered_widgets as $key => $val ) {
1768 if ( in_array($key, $shown_widgets, true) )
1771 $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
1773 if ( 2 > (int) $number )
1776 $lost_widgets[] = $key;
1779 $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
1780 if ( 'customize' !== $theme_changed ) {
1781 wp_set_sidebars_widgets( $sidebars_widgets );
1784 return $sidebars_widgets;