WordPress 4.1.4-scripts
[autoinstalls/wordpress.git] / wp-includes / widgets.php
1 <?php
2 /**
3  * API for creating dynamic sidebar without hardcoding functionality into
4  * themes. Includes both internal WordPress routines and theme use routines.
5  *
6  * This functionality was found in a plugin before WordPress 2.2 release which
7  * included it in the core from that point on.
8  *
9  * @link http://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
10  * @link http://codex.wordpress.org/Plugins/WordPress_Widgets_Api Widgets API
11  *
12  * @package WordPress
13  * @subpackage Widgets
14  */
15
16 /**
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.
19  *
20  * @package WordPress
21  * @subpackage Widgets
22  * @since 2.8.0
23  */
24 class WP_Widget {
25
26         /**
27          * Root ID for all widgets of this type.
28          *
29          * @since 2.8.0
30          * @access public
31          * @var mixed|string
32          */
33         public $id_base;
34
35         /**
36          * Name for this widget type.
37          *
38          * @since 2.8.0
39          * @access public
40          * @var string
41          */
42         public $name;
43
44         /**
45          * Option array passed to {@see wp_register_sidebar_widget()}.
46          *
47          * @since 2.8.0
48          * @access public
49          * @var array
50          */
51         public $widget_options;
52
53         /**
54          * Option array passed to {@see wp_register_widget_control()}.
55          *
56          * @since 2.8.0
57          * @access public
58          * @var array
59          */
60         public $control_options;
61
62         /**
63          * Unique ID number of the current instance.
64          *
65          * @since 2.8.0
66          * @access public
67          * @var bool|int
68          */
69         public $number = false;
70
71         /**
72          * Unique ID string of the current instance (id_base-number).
73          *
74          * @since 2.8.0
75          * @access public
76          * @var bool|string
77          */
78         public $id = false;
79
80         /**
81          * Whether the widget data has been updated.
82          *
83          * Set to true when the data is updated after a POST submit - ensures it does
84          * not happen twice.
85          *
86          * @since 2.8.0
87          * @access public
88          * @var bool
89          */
90         public $updated = false;
91
92         // Member functions that you must over-ride.
93
94         /**
95          * Echo the widget content.
96          *
97          * Subclasses should over-ride this function to generate their widget code.
98          *
99          * @since 2.8.0
100          * @access public
101          *
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.
105          */
106         public function widget( $args, $instance ) {
107                 die('function WP_Widget::widget() must be over-ridden in a sub-class.');
108         }
109
110         /**
111          * Update a particular instance.
112          *
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
115          * saved/updated.
116          *
117          * @since 2.8.0
118          * @access public
119          *
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.
124          */
125         public function update( $new_instance, $old_instance ) {
126                 return $new_instance;
127         }
128
129         /**
130          * Output the settings update form.
131          *
132          * @since 2.8.0
133          * @access public
134          *
135          * @param array $instance Current settings.
136          * @return string Default return is 'noform'.
137          */
138         public function form($instance) {
139                 echo '<p class="no-options-widget">' . __('There are no options for this widget.') . '</p>';
140                 return 'noform';
141         }
142
143         // Functions you'll need to call.
144
145         /**
146          * PHP5 constructor.
147          *
148          * @since 2.8.0
149          * @access public
150          *
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.
158          */
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);
161                 $this->name = $name;
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) );
165         }
166
167         /**
168          * PHP4 constructor
169          * 
170          * @param string $id_base
171          * @param string $name
172          * @param array  $widget_options
173          * @param array  $control_options
174          */
175         public function WP_Widget( $id_base, $name, $widget_options = array(), $control_options = array() ) {
176                 WP_Widget::__construct( $id_base, $name, $widget_options, $control_options );
177         }
178
179         /**
180          * Constructs name attributes for use in form() fields
181          *
182          * This function should be used in form() methods to create name attributes for fields to be saved by update()
183          *
184          * @param string $field_name Field name
185          * @return string Name attribute for $field_name
186          */
187         public function get_field_name($field_name) {
188                 return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field_name . ']';
189         }
190
191         /**
192          * Constructs id attributes for use in {@see WP_Widget::form()} fields.
193          *
194          * This function should be used in form() methods to create id attributes
195          * for fields to be saved by {@see WP_Widget::update()}.
196          *
197          * @since 2.8.0
198          * @access public
199          *
200          * @param string $field_name Field name.
201          * @return string ID attribute for `$field_name`.
202          */
203         public function get_field_id( $field_name ) {
204                 return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field_name;
205         }
206
207         /**
208          * Register all widget instances of this widget class.
209          *
210          * @since 2.8.0
211          * @access private
212          */
213         public function _register() {
214                 $settings = $this->get_settings();
215                 $empty = true;
216
217                 if ( is_array($settings) ) {
218                         foreach ( array_keys($settings) as $number ) {
219                                 if ( is_numeric($number) ) {
220                                         $this->_set($number);
221                                         $this->_register_one($number);
222                                         $empty = false;
223                                 }
224                         }
225                 }
226
227                 if ( $empty ) {
228                         // If there are none, we register the widget's existence with a
229                         // generic template
230                         $this->_set(1);
231                         $this->_register_one();
232                 }
233         }
234
235         /**
236          * Set the internal order number for the widget instance.
237          *
238          * @since 2.8.0
239          * @access private
240          *
241          * @param int $number The unique order number of this widget instance compared to other
242          *                    instances of the same class.
243          */
244         public function _set($number) {
245                 $this->number = $number;
246                 $this->id = $this->id_base . '-' . $number;
247         }
248
249         public function _get_display_callback() {
250                 return array($this, 'display_callback');
251         }
252
253         public function _get_update_callback() {
254                 return array($this, 'update_callback');
255         }
256
257         public function _get_form_callback() {
258                 return array($this, 'form_callback');
259         }
260
261         /**
262          * Determine whether the current request is inside the Customizer preview.
263          *
264          * If true -- the current request is inside the Customizer preview, then
265          * the object cache gets suspended and widgets should check this to decide
266          * whether they should store anything persistently to the object cache,
267          * to transients, or anywhere else.
268          *
269          * @since 3.9.0
270          * @access public
271          *
272          * @return bool True if within the Customizer preview, false if not.
273          */
274         public function is_preview() {
275                 global $wp_customize;
276                 return ( isset( $wp_customize ) && $wp_customize->is_preview() ) ;
277         }
278
279         /**
280          * Generate the actual widget content (Do NOT override).
281          *
282          * Finds the instance and calls {@see WP_Widget::widget()}.
283          *
284          * @since 2.8.0
285          * @access public
286          *
287          * @param array     $args        Display arguments. See {@see WP_Widget::widget()} for information
288          *                               on accepted arguments.
289          * @param int|array $widget_args {
290          *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
291          *     Default 1.
292          *
293          *     @type int $number Number increment used for multiples of the same widget.
294          * }
295          */
296         public function display_callback( $args, $widget_args = 1 ) {
297                 if ( is_numeric($widget_args) )
298                         $widget_args = array( 'number' => $widget_args );
299
300                 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
301                 $this->_set( $widget_args['number'] );
302                 $instance = $this->get_settings();
303
304                 if ( array_key_exists( $this->number, $instance ) ) {
305                         $instance = $instance[$this->number];
306
307                         /**
308                          * Filter the settings for a particular widget instance.
309                          *
310                          * Returning false will effectively short-circuit display of the widget.
311                          *
312                          * @since 2.8.0
313                          *
314                          * @param array     $instance The current widget instance's settings.
315                          * @param WP_Widget $this     The current widget instance.
316                          * @param array     $args     An array of default widget arguments.
317                          */
318                         $instance = apply_filters( 'widget_display_callback', $instance, $this, $args );
319
320                         if ( false === $instance ) {
321                                 return;
322                         }
323
324                         $was_cache_addition_suspended = wp_suspend_cache_addition();
325                         if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
326                                 wp_suspend_cache_addition( true );
327                         }
328
329                         $this->widget( $args, $instance );
330
331                         if ( $this->is_preview() ) {
332                                 wp_suspend_cache_addition( $was_cache_addition_suspended );
333                         }
334                 }
335         }
336
337         /**
338          * Deal with changed settings (Do NOT override).
339          *
340          * @since 2.8.0
341          * @access public
342          *
343          * @param int $deprecated Not used.
344          */
345         public function update_callback( $deprecated = 1 ) {
346                 global $wp_registered_widgets;
347
348                 $all_instances = $this->get_settings();
349
350                 // We need to update the data
351                 if ( $this->updated )
352                         return;
353
354                 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
355                         // Delete the settings for this instance of the widget
356                         if ( isset($_POST['the-widget-id']) )
357                                 $del_id = $_POST['the-widget-id'];
358                         else
359                                 return;
360
361                         if ( isset($wp_registered_widgets[$del_id]['params'][0]['number']) ) {
362                                 $number = $wp_registered_widgets[$del_id]['params'][0]['number'];
363
364                                 if ( $this->id_base . '-' . $number == $del_id )
365                                         unset($all_instances[$number]);
366                         }
367                 } else {
368                         if ( isset($_POST['widget-' . $this->id_base]) && is_array($_POST['widget-' . $this->id_base]) ) {
369                                 $settings = $_POST['widget-' . $this->id_base];
370                         } elseif ( isset($_POST['id_base']) && $_POST['id_base'] == $this->id_base ) {
371                                 $num = $_POST['multi_number'] ? (int) $_POST['multi_number'] : (int) $_POST['widget_number'];
372                                 $settings = array( $num => array() );
373                         } else {
374                                 return;
375                         }
376
377                         foreach ( $settings as $number => $new_instance ) {
378                                 $new_instance = stripslashes_deep($new_instance);
379                                 $this->_set($number);
380
381                                 $old_instance = isset($all_instances[$number]) ? $all_instances[$number] : array();
382
383                                 $was_cache_addition_suspended = wp_suspend_cache_addition();
384                                 if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
385                                         wp_suspend_cache_addition( true );
386                                 }
387
388                                 $instance = $this->update( $new_instance, $old_instance );
389
390                                 if ( $this->is_preview() ) {
391                                         wp_suspend_cache_addition( $was_cache_addition_suspended );
392                                 }
393
394                                 /**
395                                  * Filter a widget's settings before saving.
396                                  *
397                                  * Returning false will effectively short-circuit the widget's ability
398                                  * to update settings.
399                                  *
400                                  * @since 2.8.0
401                                  *
402                                  * @param array     $instance     The current widget instance's settings.
403                                  * @param array     $new_instance Array of new widget settings.
404                                  * @param array     $old_instance Array of old widget settings.
405                                  * @param WP_Widget $this         The current widget instance.
406                                  */
407                                 $instance = apply_filters( 'widget_update_callback', $instance, $new_instance, $old_instance, $this );
408                                 if ( false !== $instance ) {
409                                         $all_instances[$number] = $instance;
410                                 }
411
412                                 break; // run only once
413                         }
414                 }
415
416                 $this->save_settings($all_instances);
417                 $this->updated = true;
418         }
419
420         /**
421          * Generate the widget control form (Do NOT override).
422          *
423          * @since 2.8.0
424          * @access public
425          *
426          * @param int|array $widget_args Widget instance number or array of widget arguments.
427          */
428         public function form_callback( $widget_args = 1 ) {
429                 if ( is_numeric($widget_args) )
430                         $widget_args = array( 'number' => $widget_args );
431
432                 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
433                 $all_instances = $this->get_settings();
434
435                 if ( -1 == $widget_args['number'] ) {
436                         // We echo out a form where 'number' can be set later
437                         $this->_set('__i__');
438                         $instance = array();
439                 } else {
440                         $this->_set($widget_args['number']);
441                         $instance = $all_instances[ $widget_args['number'] ];
442                 }
443
444                 /**
445                  * Filter the widget instance's settings before displaying the control form.
446                  *
447                  * Returning false effectively short-circuits display of the control form.
448                  *
449                  * @since 2.8.0
450                  *
451                  * @param array     $instance The current widget instance's settings.
452                  * @param WP_Widget $this     The current widget instance.
453                  */
454                 $instance = apply_filters( 'widget_form_callback', $instance, $this );
455
456                 $return = null;
457                 if ( false !== $instance ) {
458                         $return = $this->form($instance);
459
460                         /**
461                          * Fires at the end of the widget control form.
462                          *
463                          * Use this hook to add extra fields to the widget form. The hook
464                          * is only fired if the value passed to the 'widget_form_callback'
465                          * hook is not false.
466                          *
467                          * Note: If the widget has no form, the text echoed from the default
468                          * form method can be hidden using CSS.
469                          *
470                          * @since 2.8.0
471                          *
472                          * @param WP_Widget $this     The widget instance, passed by reference.
473                          * @param null      $return   Return null if new fields are added.
474                          * @param array     $instance An array of the widget's settings.
475                          */
476                         do_action_ref_array( 'in_widget_form', array( &$this, &$return, $instance ) );
477                 }
478                 return $return;
479         }
480
481         /**
482          * Register an instance of the widget class.
483          *
484          * @since 2.8.0
485          * @access private
486          *
487          * @param integer $number Optional. The unique order number of this widget instance
488          *                        compared to other instances of the same class. Default -1.
489          */
490         public function _register_one( $number = -1 ) {
491                 wp_register_sidebar_widget(     $this->id, $this->name, $this->_get_display_callback(), $this->widget_options, array( 'number' => $number ) );
492                 _register_widget_update_callback( $this->id_base, $this->_get_update_callback(), $this->control_options, array( 'number' => -1 ) );
493                 _register_widget_form_callback( $this->id, $this->name, $this->_get_form_callback(), $this->control_options, array( 'number' => $number ) );
494         }
495
496         /**
497          * Save the settings for all instances of the widget class.
498          *
499          * @since 2.8.0
500          * @access public
501          *
502          * @param array $settings Multi-dimensional array of widget instance settings.
503          */
504         public function save_settings( $settings ) {
505                 $settings['_multiwidget'] = 1;
506                 update_option( $this->option_name, $settings );
507         }
508
509         /**
510          * Get the settings for all instances of the widget class.
511          *
512          * @since 2.8.0
513          * @access public
514          *
515          * @return array Multi-dimensional array of widget instance settings.
516          */
517         public function get_settings() {
518
519                 $settings = get_option($this->option_name);
520
521                 if ( false === $settings && isset($this->alt_option_name) )
522                         $settings = get_option($this->alt_option_name);
523
524                 if ( !is_array($settings) )
525                         $settings = array();
526
527                 if ( !empty($settings) && !array_key_exists('_multiwidget', $settings) ) {
528                         // old format, convert if single widget
529                         $settings = wp_convert_widget_settings($this->id_base, $this->option_name, $settings);
530                 }
531
532                 unset($settings['_multiwidget'], $settings['__i__']);
533                 return $settings;
534         }
535 }
536
537 /**
538  * Singleton that registers and instantiates WP_Widget classes.
539  *
540  * @package WordPress
541  * @subpackage Widgets
542  * @since 2.8.0
543  */
544 class WP_Widget_Factory {
545         public $widgets = array();
546
547         public function WP_Widget_Factory() {
548                 add_action( 'widgets_init', array( $this, '_register_widgets' ), 100 );
549         }
550
551         /**
552          * Register a widget subclass.
553          *
554          * @since 2.8.0
555          * @access public
556          *
557          * @param string $widget_class The name of a {@see WP_Widget} subclass.
558          */
559         public function register( $widget_class ) {
560                 $this->widgets[$widget_class] = new $widget_class();
561         }
562
563         /**
564          * Un-register a widget subclass.
565          *
566          * @since 2.8.0
567          * @access public
568          *
569          * @param string $widget_class The name of a {@see WP_Widget} subclass.
570          */
571         public function unregister( $widget_class ) {
572                 if ( isset($this->widgets[$widget_class]) )
573                         unset($this->widgets[$widget_class]);
574         }
575
576         /**
577          * Utility method for adding widgets to the registered widgets global.
578          *
579          * @since 2.8.0
580          * @access public
581          */
582         public function _register_widgets() {
583                 global $wp_registered_widgets;
584                 $keys = array_keys($this->widgets);
585                 $registered = array_keys($wp_registered_widgets);
586                 $registered = array_map('_get_widget_id_base', $registered);
587
588                 foreach ( $keys as $key ) {
589                         // don't register new widget if old widget with the same id is already registered
590                         if ( in_array($this->widgets[$key]->id_base, $registered, true) ) {
591                                 unset($this->widgets[$key]);
592                                 continue;
593                         }
594
595                         $this->widgets[$key]->_register();
596                 }
597         }
598 }
599
600 /* Global Variables */
601
602 /** @ignore */
603 global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
604
605 /**
606  * Stores the sidebars, since many themes can have more than one.
607  *
608  * @global array $wp_registered_sidebars
609  * @since 2.2.0
610  */
611 $wp_registered_sidebars = array();
612
613 /**
614  * Stores the registered widgets.
615  *
616  * @global array $wp_registered_widgets
617  * @since 2.2.0
618  */
619 $wp_registered_widgets = array();
620
621 /**
622  * Stores the registered widget control (options).
623  *
624  * @global array $wp_registered_widget_controls
625  * @since 2.2.0
626  */
627 $wp_registered_widget_controls = array();
628 $wp_registered_widget_updates = array();
629
630 /**
631  * Private
632  */
633 $_wp_sidebars_widgets = array();
634
635 /**
636  * Private
637  */
638 $GLOBALS['_wp_deprecated_widgets_callbacks'] = array(
639         'wp_widget_pages',
640         'wp_widget_pages_control',
641         'wp_widget_calendar',
642         'wp_widget_calendar_control',
643         'wp_widget_archives',
644         'wp_widget_archives_control',
645         'wp_widget_links',
646         'wp_widget_meta',
647         'wp_widget_meta_control',
648         'wp_widget_search',
649         'wp_widget_recent_entries',
650         'wp_widget_recent_entries_control',
651         'wp_widget_tag_cloud',
652         'wp_widget_tag_cloud_control',
653         'wp_widget_categories',
654         'wp_widget_categories_control',
655         'wp_widget_text',
656         'wp_widget_text_control',
657         'wp_widget_rss',
658         'wp_widget_rss_control',
659         'wp_widget_recent_comments',
660         'wp_widget_recent_comments_control'
661 );
662
663 /* Template tags & API functions */
664
665 /**
666  * Register a widget
667  *
668  * Registers a WP_Widget widget
669  *
670  * @since 2.8.0
671  *
672  * @see WP_Widget
673  * @see WP_Widget_Factory
674  * @uses WP_Widget_Factory
675  *
676  * @param string $widget_class The name of a class that extends WP_Widget
677  */
678 function register_widget($widget_class) {
679         global $wp_widget_factory;
680
681         $wp_widget_factory->register($widget_class);
682 }
683
684 /**
685  * Unregister a widget
686  *
687  * Unregisters a WP_Widget widget. Useful for unregistering default widgets.
688  * Run within a function hooked to the widgets_init action.
689  *
690  * @since 2.8.0
691  *
692  * @see WP_Widget
693  * @see WP_Widget_Factory
694  * @uses WP_Widget_Factory
695  *
696  * @param string $widget_class The name of a class that extends WP_Widget
697  */
698 function unregister_widget($widget_class) {
699         global $wp_widget_factory;
700
701         $wp_widget_factory->unregister($widget_class);
702 }
703
704 /**
705  * Creates multiple sidebars.
706  *
707  * If you wanted to quickly create multiple sidebars for a theme or internally.
708  * This function will allow you to do so. If you don't pass the 'name' and/or
709  * 'id' in `$args`, then they will be built for you.
710  *
711  * @since 2.2.0
712  *
713  * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here.
714  *
715  * @param int          $number Optional. Number of sidebars to create. Default 1.
716  * @param array|string $args {
717  *     Optional. Array or string of arguments for building a sidebar.
718  *
719  *     @type string $id   The base string of the unique identifier for each sidebar. If provided, and multiple
720  *                        sidebars are being defined, the id will have "-2" appended, and so on.
721  *                        Default 'sidebar-' followed by the number the sidebar creation is currently at.
722  *     @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering
723  *                        more than one sidebar, include '%d' in the string as a placeholder for the uniquely
724  *                        assigned number for each sidebar.
725  *                        Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'.
726  * }
727  */
728 function register_sidebars( $number = 1, $args = array() ) {
729         global $wp_registered_sidebars;
730         $number = (int) $number;
731
732         if ( is_string($args) )
733                 parse_str($args, $args);
734
735         for ( $i = 1; $i <= $number; $i++ ) {
736                 $_args = $args;
737
738                 if ( $number > 1 )
739                         $_args['name'] = isset($args['name']) ? sprintf($args['name'], $i) : sprintf(__('Sidebar %d'), $i);
740                 else
741                         $_args['name'] = isset($args['name']) ? $args['name'] : __('Sidebar');
742
743                 // Custom specified ID's are suffixed if they exist already.
744                 // Automatically generated sidebar names need to be suffixed regardless starting at -0
745                 if ( isset($args['id']) ) {
746                         $_args['id'] = $args['id'];
747                         $n = 2; // Start at -2 for conflicting custom ID's
748                         while ( isset($wp_registered_sidebars[$_args['id']]) )
749                                 $_args['id'] = $args['id'] . '-' . $n++;
750                 } else {
751                         $n = count($wp_registered_sidebars);
752                         do {
753                                 $_args['id'] = 'sidebar-' . ++$n;
754                         } while ( isset($wp_registered_sidebars[$_args['id']]) );
755                 }
756                 register_sidebar($_args);
757         }
758 }
759
760 /**
761  * Builds the definition for a single sidebar and returns the ID.
762  *
763  * Accepts either a string or an array and then parses that against a set
764  * of default arguments for the new sidebar. WordPress will automatically
765  * generate a sidebar ID and name based on the current number of registered
766  * sidebars if those arguments are not included.
767  *
768  * When allowing for automatic generation of the name and ID parameters, keep
769  * in mind that the incrementor for your sidebar can change over time depending
770  * on what other plugins and themes are installed.
771  *
772  * If theme support for 'widgets' has not yet been added when this function is
773  * called, it will be automatically enabled through the use of add_theme_support()
774  *
775  * @since 2.2.0
776  *
777  * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
778  *
779  * @param array|string $args {
780  *     Optional. Array or string of arguments for the sidebar being registered.
781  *
782  *     @type string $name          The name or title of the sidebar displayed in the Widgets
783  *                                 interface. Default 'Sidebar $instance'.
784  *     @type string $id            The unique identifier by which the sidebar will be called.
785  *                                 Default 'sidebar-$instance'.
786  *     @type string $description   Description of the sidebar, displayed in the Widgets interface.
787  *                                 Default empty string.
788  *     @type string $class         Extra CSS class to assign to the sidebar in the Widgets interface.
789  *                                 Default empty.
790  *     @type string $before_widget HTML content to prepend to each widget's HTML output when
791  *                                 assigned to this sidebar. Default is an opening list item element.
792  *     @type string $after_widget  HTML content to append to each widget's HTML output when
793  *                                 assigned to this sidebar. Default is a closing list item element.
794  *     @type string $before_title  HTML content to prepend to the sidebar title when displayed.
795  *                                 Default is an opening h2 element.
796  *     @type string $after_title   HTML content to append to the sidebar title when displayed.
797  *                                 Default is a closing h2 element.
798  * }
799  * @return string Sidebar ID added to $wp_registered_sidebars global.
800  */
801 function register_sidebar($args = array()) {
802         global $wp_registered_sidebars;
803
804         $i = count($wp_registered_sidebars) + 1;
805
806         $defaults = array(
807                 'name' => sprintf(__('Sidebar %d'), $i ),
808                 'id' => "sidebar-$i",
809                 'description' => '',
810                 'class' => '',
811                 'before_widget' => '<li id="%1$s" class="widget %2$s">',
812                 'after_widget' => "</li>\n",
813                 'before_title' => '<h2 class="widgettitle">',
814                 'after_title' => "</h2>\n",
815         );
816
817         $sidebar = wp_parse_args( $args, $defaults );
818
819         $wp_registered_sidebars[$sidebar['id']] = $sidebar;
820
821         add_theme_support('widgets');
822
823         /**
824          * Fires once a sidebar has been registered.
825          *
826          * @since 3.0.0
827          *
828          * @param array $sidebar Parsed arguments for the registered sidebar.
829          */
830         do_action( 'register_sidebar', $sidebar );
831
832         return $sidebar['id'];
833 }
834
835 /**
836  * Removes a sidebar from the list.
837  *
838  * @since 2.2.0
839  *
840  * @uses $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
841  *
842  * @param string $name The ID of the sidebar when it was added.
843  */
844 function unregister_sidebar( $name ) {
845         global $wp_registered_sidebars;
846
847         if ( isset( $wp_registered_sidebars[$name] ) )
848                 unset( $wp_registered_sidebars[$name] );
849 }
850
851 /**
852  * Register an instance of a widget.
853  *
854  * The default widget option is 'classname' that can be overridden.
855  *
856  * The function can also be used to un-register widgets when `$output_callback`
857  * parameter is an empty string.
858  *
859  * @since 2.2.0
860  *
861  * @global array $wp_registered_widgets       Uses stored registered widgets.
862  * @global array $wp_register_widget_defaults Retrieves widget defaults.
863  *
864  * @param int|string $id              Widget ID.
865  * @param string     $name            Widget display title.
866  * @param callback   $output_callback Run when widget is called.
867  * @param array      $options {
868  *     Optional. An array of supplementary widget options for the instance.
869  *
870  *     @type string $classname   Class name for the widget's HTML container. Default is a shortened
871  *                               version of the output callback name.
872  *     @type string $description Widget description for display in the widget administration
873  *                               panel and/or theme.
874  * }
875  * @return null Will return if `$output_callback` is empty after removing widget.
876  */
877 function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
878         global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
879
880         $id = strtolower($id);
881
882         if ( empty($output_callback) ) {
883                 unset($wp_registered_widgets[$id]);
884                 return;
885         }
886
887         $id_base = _get_widget_id_base($id);
888         if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
889                 if ( isset($wp_registered_widget_controls[$id]) )
890                         unset($wp_registered_widget_controls[$id]);
891
892                 if ( isset($wp_registered_widget_updates[$id_base]) )
893                         unset($wp_registered_widget_updates[$id_base]);
894
895                 return;
896         }
897
898         $defaults = array('classname' => $output_callback);
899         $options = wp_parse_args($options, $defaults);
900         $widget = array(
901                 'name' => $name,
902                 'id' => $id,
903                 'callback' => $output_callback,
904                 'params' => array_slice(func_get_args(), 4)
905         );
906         $widget = array_merge($widget, $options);
907
908         if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
909
910                 /**
911                  * Fires once for each registered widget.
912                  *
913                  * @since 3.0.0
914                  *
915                  * @param array $widget An array of default widget arguments.
916                  */
917                 do_action( 'wp_register_sidebar_widget', $widget );
918                 $wp_registered_widgets[$id] = $widget;
919         }
920 }
921
922 /**
923  * Retrieve description for widget.
924  *
925  * When registering widgets, the options can also include 'description' that
926  * describes the widget for display on the widget administration panel or
927  * in the theme.
928  *
929  * @since 2.5.0
930  *
931  * @param int|string $id Widget ID.
932  * @return string Widget description, if available. Null on failure to retrieve description.
933  */
934 function wp_widget_description( $id ) {
935         if ( !is_scalar($id) )
936                 return;
937
938         global $wp_registered_widgets;
939
940         if ( isset($wp_registered_widgets[$id]['description']) )
941                 return esc_html( $wp_registered_widgets[$id]['description'] );
942 }
943
944 /**
945  * Retrieve description for a sidebar.
946  *
947  * When registering sidebars a 'description' parameter can be included that
948  * describes the sidebar for display on the widget administration panel.
949  *
950  * @since 2.9.0
951  *
952  * @param string $id sidebar ID.
953  * @return string Sidebar description, if available. Null on failure to retrieve description.
954  */
955 function wp_sidebar_description( $id ) {
956         if ( !is_scalar($id) )
957                 return;
958
959         global $wp_registered_sidebars;
960
961         if ( isset($wp_registered_sidebars[$id]['description']) )
962                 return esc_html( $wp_registered_sidebars[$id]['description'] );
963 }
964
965 /**
966  * Remove widget from sidebar.
967  *
968  * @since 2.2.0
969  *
970  * @param int|string $id Widget ID.
971  */
972 function wp_unregister_sidebar_widget($id) {
973
974         /**
975          * Fires just before a widget is removed from a sidebar.
976          *
977          * @since 3.0.0
978          *
979          * @param int $id The widget ID.
980          */
981         do_action( 'wp_unregister_sidebar_widget', $id );
982
983         wp_register_sidebar_widget($id, '', '');
984         wp_unregister_widget_control($id);
985 }
986
987 /**
988  * Registers widget control callback for customizing options.
989  *
990  * The options contains the 'height', 'width', and 'id_base' keys. The 'height'
991  * option is never used. The 'width' option is the width of the fully expanded
992  * control form, but try hard to use the default width. The 'id_base' is for
993  * multi-widgets (widgets which allow multiple instances such as the text
994  * widget), an id_base must be provided. The widget id will end up looking like
995  * `{$id_base}-{$unique_number}`.
996  *
997  * @since 2.2.0
998  *
999  * @todo Document `$options` as a hash notation, re: WP_Widget::__construct() cross-reference.
1000  * @todo `$params` parameter?
1001  *
1002  * @param int|string   $id               Sidebar ID.
1003  * @param string       $name             Sidebar display name.
1004  * @param callback     $control_callback Run when sidebar is displayed.
1005  * @param array|string $options          Optional. Widget options. See description above. Default empty array.
1006  */
1007 function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
1008         global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
1009
1010         $id = strtolower($id);
1011         $id_base = _get_widget_id_base($id);
1012
1013         if ( empty($control_callback) ) {
1014                 unset($wp_registered_widget_controls[$id]);
1015                 unset($wp_registered_widget_updates[$id_base]);
1016                 return;
1017         }
1018
1019         if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
1020                 if ( isset($wp_registered_widgets[$id]) )
1021                         unset($wp_registered_widgets[$id]);
1022
1023                 return;
1024         }
1025
1026         if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1027                 return;
1028
1029         $defaults = array('width' => 250, 'height' => 200 ); // height is never used
1030         $options = wp_parse_args($options, $defaults);
1031         $options['width'] = (int) $options['width'];
1032         $options['height'] = (int) $options['height'];
1033
1034         $widget = array(
1035                 'name' => $name,
1036                 'id' => $id,
1037                 'callback' => $control_callback,
1038                 'params' => array_slice(func_get_args(), 4)
1039         );
1040         $widget = array_merge($widget, $options);
1041
1042         $wp_registered_widget_controls[$id] = $widget;
1043
1044         if ( isset($wp_registered_widget_updates[$id_base]) )
1045                 return;
1046
1047         if ( isset($widget['params'][0]['number']) )
1048                 $widget['params'][0]['number'] = -1;
1049
1050         unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
1051         $wp_registered_widget_updates[$id_base] = $widget;
1052 }
1053
1054 /**
1055  *
1056  * @global array $wp_registered_widget_updates
1057  * @param string   $id_base
1058  * @param callable $update_callback
1059  * @param array    $options
1060  */
1061 function _register_widget_update_callback($id_base, $update_callback, $options = array()) {
1062         global $wp_registered_widget_updates;
1063
1064         if ( isset($wp_registered_widget_updates[$id_base]) ) {
1065                 if ( empty($update_callback) )
1066                         unset($wp_registered_widget_updates[$id_base]);
1067                 return;
1068         }
1069
1070         $widget = array(
1071                 'callback' => $update_callback,
1072                 'params' => array_slice(func_get_args(), 3)
1073         );
1074
1075         $widget = array_merge($widget, $options);
1076         $wp_registered_widget_updates[$id_base] = $widget;
1077 }
1078
1079 /**
1080  *
1081  * @global array $wp_registered_widget_controls
1082  * @param int|string $id
1083  * @param string     $name
1084  * @param callable   $form_callback
1085  * @param array      $options
1086  * @return null
1087  */
1088 function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
1089         global $wp_registered_widget_controls;
1090
1091         $id = strtolower($id);
1092
1093         if ( empty($form_callback) ) {
1094                 unset($wp_registered_widget_controls[$id]);
1095                 return;
1096         }
1097
1098         if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1099                 return;
1100
1101         $defaults = array('width' => 250, 'height' => 200 );
1102         $options = wp_parse_args($options, $defaults);
1103         $options['width'] = (int) $options['width'];
1104         $options['height'] = (int) $options['height'];
1105
1106         $widget = array(
1107                 'name' => $name,
1108                 'id' => $id,
1109                 'callback' => $form_callback,
1110                 'params' => array_slice(func_get_args(), 4)
1111         );
1112         $widget = array_merge($widget, $options);
1113
1114         $wp_registered_widget_controls[$id] = $widget;
1115 }
1116
1117 /**
1118  * Remove control callback for widget.
1119  *
1120  * @since 2.2.0
1121  *
1122  * @param int|string $id Widget ID.
1123  */
1124 function wp_unregister_widget_control($id) {
1125         return wp_register_widget_control($id, '', '');
1126 }
1127
1128 /**
1129  * Display dynamic sidebar.
1130  *
1131  * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
1132  * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
1133  * Otherwise, you can pass in a numerical index to display the sidebar at that index.
1134  *
1135  * @since 2.2.0
1136  *
1137  * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
1138  * @return bool True, if widget sidebar was found and called. False if not found or not called.
1139  */
1140 function dynamic_sidebar($index = 1) {
1141         global $wp_registered_sidebars, $wp_registered_widgets;
1142
1143         if ( is_int($index) ) {
1144                 $index = "sidebar-$index";
1145         } else {
1146                 $index = sanitize_title($index);
1147                 foreach ( (array) $wp_registered_sidebars as $key => $value ) {
1148                         if ( sanitize_title($value['name']) == $index ) {
1149                                 $index = $key;
1150                                 break;
1151                         }
1152                 }
1153         }
1154
1155         $sidebars_widgets = wp_get_sidebars_widgets();
1156         if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
1157                 /** This action is documented in wp-includes/widgets.php */
1158                 do_action( 'dynamic_sidebar_before', $index, false );
1159                 /** This action is documented in wp-includes/widgets.php */
1160                 do_action( 'dynamic_sidebar_after',  $index, false );
1161                 /** This filter is documented in wp-includes/widgets.php */
1162                 return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
1163         }
1164
1165         /**
1166          * Fires before widgets are rendered in a dynamic sidebar.
1167          *
1168          * Note: The action also fires for empty sidebars, and on both the front-end
1169          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1170          *
1171          * @since 3.9.0
1172          *
1173          * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1174          * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1175          *                                Default true.
1176          */
1177         do_action( 'dynamic_sidebar_before', $index, true );
1178         $sidebar = $wp_registered_sidebars[$index];
1179
1180         $did_one = false;
1181         foreach ( (array) $sidebars_widgets[$index] as $id ) {
1182
1183                 if ( !isset($wp_registered_widgets[$id]) ) continue;
1184
1185                 $params = array_merge(
1186                         array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
1187                         (array) $wp_registered_widgets[$id]['params']
1188                 );
1189
1190                 // Substitute HTML id and class attributes into before_widget
1191                 $classname_ = '';
1192                 foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
1193                         if ( is_string($cn) )
1194                                 $classname_ .= '_' . $cn;
1195                         elseif ( is_object($cn) )
1196                                 $classname_ .= '_' . get_class($cn);
1197                 }
1198                 $classname_ = ltrim($classname_, '_');
1199                 $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
1200
1201                 /**
1202                  * Filter the parameters passed to a widget's display callback.
1203                  *
1204                  * Note: The filter is evaluated on both the front-end and back-end,
1205                  * including for the Inactive Widgets sidebar on the Widgets screen.
1206                  *
1207                  * @since 2.5.0
1208                  *
1209                  * @see register_sidebar()
1210                  *
1211                  * @param array $params {
1212                  *     @type array $args  {
1213                  *         An array of widget display arguments.
1214                  *
1215                  *         @type string $name          Name of the sidebar the widget is assigned to.
1216                  *         @type string $id            ID of the sidebar the widget is assigned to.
1217                  *         @type string $description   The sidebar description.
1218                  *         @type string $class         CSS class applied to the sidebar container.
1219                  *         @type string $before_widget HTML markup to prepend to each widget in the sidebar.
1220                  *         @type string $after_widget  HTML markup to append to each widget in the sidebar.
1221                  *         @type string $before_title  HTML markup to prepend to the widget title when displayed.
1222                  *         @type string $after_title   HTML markup to append to the widget title when displayed.
1223                  *         @type string $widget_id     ID of the widget.
1224                  *         @type string $widget_name   Name of the widget.
1225                  *     }
1226                  *     @type array $widget_args {
1227                  *         An array of multi-widget arguments.
1228                  *
1229                  *         @type int $number Number increment used for multiples of the same widget.
1230                  *     }
1231                  * }
1232                  */
1233                 $params = apply_filters( 'dynamic_sidebar_params', $params );
1234
1235                 $callback = $wp_registered_widgets[$id]['callback'];
1236
1237                 /**
1238                  * Fires before a widget's display callback is called.
1239                  *
1240                  * Note: The action fires on both the front-end and back-end, including
1241                  * for widgets in the Inactive Widgets sidebar on the Widgets screen.
1242                  *
1243                  * The action is not fired for empty sidebars.
1244                  *
1245                  * @since 3.0.0
1246                  *
1247                  * @param array $widget_id {
1248                  *     An associative array of widget arguments.
1249                  *
1250                  *     @type string $name                Name of the widget.
1251                  *     @type string $id                  Widget ID.
1252                  *     @type array|callback $callback    When the hook is fired on the front-end, $callback is an array
1253                  *                                       containing the widget object. Fired on the back-end, $callback
1254                  *                                       is 'wp_widget_control', see $_callback.
1255                  *     @type array          $params      An associative array of multi-widget arguments.
1256                  *     @type string         $classname   CSS class applied to the widget container.
1257                  *     @type string         $description The widget description.
1258                  *     @type array          $_callback   When the hook is fired on the back-end, $_callback is populated
1259                  *                                       with an array containing the widget object, see $callback.
1260                  * }
1261                  */
1262                 do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
1263
1264                 if ( is_callable($callback) ) {
1265                         call_user_func_array($callback, $params);
1266                         $did_one = true;
1267                 }
1268         }
1269
1270         /**
1271          * Fires after widgets are rendered in a dynamic sidebar.
1272          *
1273          * Note: The action also fires for empty sidebars, and on both the front-end
1274          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1275          *
1276          * @since 3.9.0
1277          *
1278          * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1279          * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1280          *                                Default true.
1281          */
1282         do_action( 'dynamic_sidebar_after', $index, true );
1283
1284         /**
1285          * Filter whether a sidebar has widgets.
1286          *
1287          * Note: The filter is also evaluated for empty sidebars, and on both the front-end
1288          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1289          *
1290          * @since 3.9.0
1291          *
1292          * @param bool       $did_one Whether at least one widget was rendered in the sidebar.
1293          *                            Default false.
1294          * @param int|string $index   Index, name, or ID of the dynamic sidebar.
1295          */
1296
1297         $did_one = apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
1298
1299         return $did_one;
1300 }
1301
1302 /**
1303  * Whether widget is displayed on the front-end.
1304  *
1305  * Either $callback or $id_base can be used
1306  * $id_base is the first argument when extending WP_Widget class
1307  * Without the optional $widget_id parameter, returns the ID of the first sidebar
1308  * in which the first instance of the widget with the given callback or $id_base is found.
1309  * With the $widget_id parameter, returns the ID of the sidebar where
1310  * the widget with that callback/$id_base AND that ID is found.
1311  *
1312  * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
1313  * this function has to run after widgets have initialized, at action 'init' or later.
1314  *
1315  * @since 2.2.0
1316  *
1317  * @param string $callback Optional, Widget callback to check.
1318  * @param int $widget_id Optional, but needed for checking. Widget ID.
1319  * @param string $id_base Optional, the base ID of a widget created by extending WP_Widget.
1320  * @param bool $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
1321  * @return mixed false if widget is not active or id of sidebar in which the widget is active.
1322  */
1323 function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
1324         global $wp_registered_widgets;
1325
1326         $sidebars_widgets = wp_get_sidebars_widgets();
1327
1328         if ( is_array($sidebars_widgets) ) {
1329                 foreach ( $sidebars_widgets as $sidebar => $widgets ) {
1330                         if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
1331                                 continue;
1332                         }
1333
1334                         if ( is_array($widgets) ) {
1335                                 foreach ( $widgets as $widget ) {
1336                                         if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
1337                                                 if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
1338                                                         return $sidebar;
1339                                         }
1340                                 }
1341                         }
1342                 }
1343         }
1344         return false;
1345 }
1346
1347 /**
1348  * Whether the dynamic sidebar is enabled and used by theme.
1349  *
1350  * @since 2.2.0
1351  *
1352  * @return bool True, if using widgets. False, if not using widgets.
1353  */
1354 function is_dynamic_sidebar() {
1355         global $wp_registered_widgets, $wp_registered_sidebars;
1356         $sidebars_widgets = get_option('sidebars_widgets');
1357         foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
1358                 if ( count($sidebars_widgets[$index]) ) {
1359                         foreach ( (array) $sidebars_widgets[$index] as $widget )
1360                                 if ( array_key_exists($widget, $wp_registered_widgets) )
1361                                         return true;
1362                 }
1363         }
1364         return false;
1365 }
1366
1367 /**
1368  * Whether a sidebar is in use.
1369  *
1370  * @since 2.8.0
1371  *
1372  * @param string|int $index Sidebar name, id or number to check.
1373  * @return bool true if the sidebar is in use, false otherwise.
1374  */
1375 function is_active_sidebar( $index ) {
1376         $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
1377         $sidebars_widgets = wp_get_sidebars_widgets();
1378         $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
1379
1380         /**
1381          * Filter whether a dynamic sidebar is considered "active".
1382          *
1383          * @since 3.9.0
1384          *
1385          * @param bool       $is_active_sidebar Whether or not the sidebar should be considered "active".
1386          *                                      In other words, whether the sidebar contains any widgets.
1387          * @param int|string $index             Index, name, or ID of the dynamic sidebar.
1388          */
1389         return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
1390 }
1391
1392 /* Internal Functions */
1393
1394 /**
1395  * Retrieve full list of sidebars and their widget instance IDs.
1396  *
1397  * Will upgrade sidebar widget list, if needed. Will also save updated list, if
1398  * needed.
1399  *
1400  * @since 2.2.0
1401  * @access private
1402  *
1403  * @param bool $deprecated Not used (argument deprecated).
1404  * @return array Upgraded list of widgets to version 3 array format when called from the admin.
1405  */
1406 function wp_get_sidebars_widgets( $deprecated = true ) {
1407         if ( $deprecated !== true )
1408                 _deprecated_argument( __FUNCTION__, '2.8.1' );
1409
1410         global $_wp_sidebars_widgets, $sidebars_widgets;
1411
1412         // If loading from front page, consult $_wp_sidebars_widgets rather than options
1413         // to see if wp_convert_widget_settings() has made manipulations in memory.
1414         if ( !is_admin() ) {
1415                 if ( empty($_wp_sidebars_widgets) )
1416                         $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
1417
1418                 $sidebars_widgets = $_wp_sidebars_widgets;
1419         } else {
1420                 $sidebars_widgets = get_option('sidebars_widgets', array());
1421         }
1422
1423         if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
1424                 unset($sidebars_widgets['array_version']);
1425
1426         /**
1427          * Filter the list of sidebars and their widgets.
1428          *
1429          * @since 2.7.0
1430          *
1431          * @param array $sidebars_widgets An associative array of sidebars and their widgets.
1432          */
1433         $sidebars_widgets = apply_filters( 'sidebars_widgets', $sidebars_widgets );
1434         return $sidebars_widgets;
1435 }
1436
1437 /**
1438  * Set the sidebar widget option to update sidebars.
1439  *
1440  * @since 2.2.0
1441  * @access private
1442  *
1443  * @param array $sidebars_widgets Sidebar widgets and their settings.
1444  */
1445 function wp_set_sidebars_widgets( $sidebars_widgets ) {
1446         if ( !isset( $sidebars_widgets['array_version'] ) )
1447                 $sidebars_widgets['array_version'] = 3;
1448         update_option( 'sidebars_widgets', $sidebars_widgets );
1449 }
1450
1451 /**
1452  * Retrieve default registered sidebars list.
1453  *
1454  * @since 2.2.0
1455  * @access private
1456  *
1457  * @return array
1458  */
1459 function wp_get_widget_defaults() {
1460         global $wp_registered_sidebars;
1461
1462         $defaults = array();
1463
1464         foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
1465                 $defaults[$index] = array();
1466
1467         return $defaults;
1468 }
1469
1470 /**
1471  * Convert the widget settings from single to multi-widget format.
1472  *
1473  * @since 2.8.0
1474  *
1475  * @param string $base_name
1476  * @param string $option_name
1477  * @param array  $settings
1478  * @return array
1479  */
1480 function wp_convert_widget_settings($base_name, $option_name, $settings) {
1481         // This test may need expanding.
1482         $single = $changed = false;
1483         if ( empty($settings) ) {
1484                 $single = true;
1485         } else {
1486                 foreach ( array_keys($settings) as $number ) {
1487                         if ( 'number' == $number )
1488                                 continue;
1489                         if ( !is_numeric($number) ) {
1490                                 $single = true;
1491                                 break;
1492                         }
1493                 }
1494         }
1495
1496         if ( $single ) {
1497                 $settings = array( 2 => $settings );
1498
1499                 // If loading from the front page, update sidebar in memory but don't save to options
1500                 if ( is_admin() ) {
1501                         $sidebars_widgets = get_option('sidebars_widgets');
1502                 } else {
1503                         if ( empty($GLOBALS['_wp_sidebars_widgets']) )
1504                                 $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
1505                         $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
1506                 }
1507
1508                 foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
1509                         if ( is_array($sidebar) ) {
1510                                 foreach ( $sidebar as $i => $name ) {
1511                                         if ( $base_name == $name ) {
1512                                                 $sidebars_widgets[$index][$i] = "$name-2";
1513                                                 $changed = true;
1514                                                 break 2;
1515                                         }
1516                                 }
1517                         }
1518                 }
1519
1520                 if ( is_admin() && $changed )
1521                         update_option('sidebars_widgets', $sidebars_widgets);
1522         }
1523
1524         $settings['_multiwidget'] = 1;
1525         if ( is_admin() )
1526                 update_option( $option_name, $settings );
1527
1528         return $settings;
1529 }
1530
1531 /**
1532  * Output an arbitrary widget as a template tag.
1533  *
1534  * @since 2.8.0
1535  *
1536  * @param string $widget   The widget's PHP class name (see default-widgets.php).
1537  * @param array  $instance Optional. The widget's instance settings. Default empty array.
1538  * @param array  $args {
1539  *     Optional. Array of arguments to configure the display of the widget.
1540  *
1541  *     @type string $before_widget HTML content that will be prepended to the widget's HTML output.
1542  *                                 Default `<div class="widget %s">`, where `%s` is the widget's class name.
1543  *     @type string $after_widget  HTML content that will be appended to the widget's HTML output.
1544  *                                 Default `</div>`.
1545  *     @type string $before_title  HTML content that will be prepended to the widget's title when displayed.
1546  *                                 Default `<h2 class="widgettitle">`.
1547  *     @type string $after_title   HTML content that will be appended to the widget's title when displayed.
1548  *                                 Default `</h2>`.
1549  * }
1550  */
1551 function the_widget( $widget, $instance = array(), $args = array() ) {
1552         global $wp_widget_factory;
1553
1554         $widget_obj = $wp_widget_factory->widgets[$widget];
1555         if ( !is_a($widget_obj, 'WP_Widget') )
1556                 return;
1557
1558         $before_widget = sprintf('<div class="widget %s">', $widget_obj->widget_options['classname'] );
1559         $default_args = array( 'before_widget' => $before_widget, 'after_widget' => "</div>", 'before_title' => '<h2 class="widgettitle">', 'after_title' => '</h2>' );
1560
1561         $args = wp_parse_args($args, $default_args);
1562         $instance = wp_parse_args($instance);
1563
1564         /**
1565          * Fires before rendering the requested widget.
1566          *
1567          * @since 3.0.0
1568          *
1569          * @param string $widget   The widget's class name.
1570          * @param array  $instance The current widget instance's settings.
1571          * @param array  $args     An array of the widget's sidebar arguments.
1572          */
1573         do_action( 'the_widget', $widget, $instance, $args );
1574
1575         $widget_obj->_set(-1);
1576         $widget_obj->widget($args, $instance);
1577 }
1578
1579 /**
1580  * Private
1581  */
1582 function _get_widget_id_base($id) {
1583         return preg_replace( '/-[0-9]+$/', '', $id );
1584 }
1585
1586 /**
1587  * Handle sidebars config after theme change
1588  *
1589  * @access private
1590  * @since 3.3.0
1591  */
1592 function _wp_sidebars_changed() {
1593         global $sidebars_widgets;
1594
1595         if ( ! is_array( $sidebars_widgets ) )
1596                 $sidebars_widgets = wp_get_sidebars_widgets();
1597
1598         retrieve_widgets(true);
1599 }
1600
1601 /**
1602  * Look for "lost" widgets, this has to run at least on each theme change.
1603  *
1604  * @since 2.8.0
1605  *
1606  * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
1607  *                                   of 'customize' defers updates for the Customizer.
1608  * @return array
1609  */
1610 function retrieve_widgets( $theme_changed = false ) {
1611         global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
1612
1613         $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
1614         $orphaned = 0;
1615
1616         $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
1617         if ( is_array( $old_sidebars_widgets ) ) {
1618                 // time() that sidebars were stored is in $old_sidebars_widgets['time']
1619                 $_sidebars_widgets = $old_sidebars_widgets['data'];
1620
1621                 if ( 'customize' !== $theme_changed ) {
1622                         remove_theme_mod( 'sidebars_widgets' );
1623                 }
1624
1625                 foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1626                         if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
1627                                 continue;
1628                         }
1629
1630                         if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
1631                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
1632                                 unset( $_sidebars_widgets[$sidebar] );
1633                         }
1634                 }
1635         } else {
1636                 if ( empty( $sidebars_widgets ) )
1637                         return;
1638
1639                 unset( $sidebars_widgets['array_version'] );
1640
1641                 $old = array_keys($sidebars_widgets);
1642                 sort($old);
1643                 sort($registered_sidebar_keys);
1644
1645                 if ( $old == $registered_sidebar_keys )
1646                         return;
1647
1648                 $_sidebars_widgets = array(
1649                         'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
1650                 );
1651
1652                 unset( $sidebars_widgets['wp_inactive_widgets'] );
1653
1654                 foreach ( $wp_registered_sidebars as $id => $settings ) {
1655                         if ( $theme_changed ) {
1656                                 $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
1657                         } else {
1658                                 // no theme change, grab only sidebars that are currently registered
1659                                 if ( isset( $sidebars_widgets[$id] ) ) {
1660                                         $_sidebars_widgets[$id] = $sidebars_widgets[$id];
1661                                         unset( $sidebars_widgets[$id] );
1662                                 }
1663                         }
1664                 }
1665
1666                 foreach ( $sidebars_widgets as $val ) {
1667                         if ( is_array($val) && ! empty( $val ) )
1668                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
1669                 }
1670         }
1671
1672         // discard invalid, theme-specific widgets from sidebars
1673         $shown_widgets = array();
1674
1675         foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1676                 if ( !is_array($widgets) )
1677                         continue;
1678
1679                 $_widgets = array();
1680                 foreach ( $widgets as $widget ) {
1681                         if ( isset($wp_registered_widgets[$widget]) )
1682                                 $_widgets[] = $widget;
1683                 }
1684
1685                 $_sidebars_widgets[$sidebar] = $_widgets;
1686                 $shown_widgets = array_merge($shown_widgets, $_widgets);
1687         }
1688
1689         $sidebars_widgets = $_sidebars_widgets;
1690         unset($_sidebars_widgets, $_widgets);
1691
1692         // find hidden/lost multi-widget instances
1693         $lost_widgets = array();
1694         foreach ( $wp_registered_widgets as $key => $val ) {
1695                 if ( in_array($key, $shown_widgets, true) )
1696                         continue;
1697
1698                 $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
1699
1700                 if ( 2 > (int) $number )
1701                         continue;
1702
1703                 $lost_widgets[] = $key;
1704         }
1705
1706         $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
1707         if ( 'customize' !== $theme_changed ) {
1708                 wp_set_sidebars_widgets( $sidebars_widgets );
1709         }
1710
1711         return $sidebars_widgets;
1712 }