WordPress 4.2
[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 https://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
10  * @link https://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         $id_is_empty = empty( $args['id'] );
807
808         $defaults = array(
809                 'name' => sprintf(__('Sidebar %d'), $i ),
810                 'id' => "sidebar-$i",
811                 'description' => '',
812                 'class' => '',
813                 'before_widget' => '<li id="%1$s" class="widget %2$s">',
814                 'after_widget' => "</li>\n",
815                 'before_title' => '<h2 class="widgettitle">',
816                 'after_title' => "</h2>\n",
817         );
818
819         $sidebar = wp_parse_args( $args, $defaults );
820
821         if ( $id_is_empty ) {
822                 /* translators: 1: the id argument, 2: sidebar name, 3: recommended id value */
823                 _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' );
824         }
825
826         $wp_registered_sidebars[$sidebar['id']] = $sidebar;
827
828         add_theme_support('widgets');
829
830         /**
831          * Fires once a sidebar has been registered.
832          *
833          * @since 3.0.0
834          *
835          * @param array $sidebar Parsed arguments for the registered sidebar.
836          */
837         do_action( 'register_sidebar', $sidebar );
838
839         return $sidebar['id'];
840 }
841
842 /**
843  * Removes a sidebar from the list.
844  *
845  * @since 2.2.0
846  *
847  * @uses $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
848  *
849  * @param string $name The ID of the sidebar when it was added.
850  */
851 function unregister_sidebar( $name ) {
852         global $wp_registered_sidebars;
853
854         if ( isset( $wp_registered_sidebars[$name] ) )
855                 unset( $wp_registered_sidebars[$name] );
856 }
857
858 /**
859  * Register an instance of a widget.
860  *
861  * The default widget option is 'classname' that can be overridden.
862  *
863  * The function can also be used to un-register widgets when `$output_callback`
864  * parameter is an empty string.
865  *
866  * @since 2.2.0
867  *
868  * @global array $wp_registered_widgets       Uses stored registered widgets.
869  * @global array $wp_register_widget_defaults Retrieves widget defaults.
870  *
871  * @param int|string $id              Widget ID.
872  * @param string     $name            Widget display title.
873  * @param callback   $output_callback Run when widget is called.
874  * @param array      $options {
875  *     Optional. An array of supplementary widget options for the instance.
876  *
877  *     @type string $classname   Class name for the widget's HTML container. Default is a shortened
878  *                               version of the output callback name.
879  *     @type string $description Widget description for display in the widget administration
880  *                               panel and/or theme.
881  * }
882  * @return null Will return if `$output_callback` is empty after removing widget.
883  */
884 function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
885         global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
886
887         $id = strtolower($id);
888
889         if ( empty($output_callback) ) {
890                 unset($wp_registered_widgets[$id]);
891                 return;
892         }
893
894         $id_base = _get_widget_id_base($id);
895         if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
896                 if ( isset($wp_registered_widget_controls[$id]) )
897                         unset($wp_registered_widget_controls[$id]);
898
899                 if ( isset($wp_registered_widget_updates[$id_base]) )
900                         unset($wp_registered_widget_updates[$id_base]);
901
902                 return;
903         }
904
905         $defaults = array('classname' => $output_callback);
906         $options = wp_parse_args($options, $defaults);
907         $widget = array(
908                 'name' => $name,
909                 'id' => $id,
910                 'callback' => $output_callback,
911                 'params' => array_slice(func_get_args(), 4)
912         );
913         $widget = array_merge($widget, $options);
914
915         if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
916
917                 /**
918                  * Fires once for each registered widget.
919                  *
920                  * @since 3.0.0
921                  *
922                  * @param array $widget An array of default widget arguments.
923                  */
924                 do_action( 'wp_register_sidebar_widget', $widget );
925                 $wp_registered_widgets[$id] = $widget;
926         }
927 }
928
929 /**
930  * Retrieve description for widget.
931  *
932  * When registering widgets, the options can also include 'description' that
933  * describes the widget for display on the widget administration panel or
934  * in the theme.
935  *
936  * @since 2.5.0
937  *
938  * @param int|string $id Widget ID.
939  * @return string Widget description, if available. Null on failure to retrieve description.
940  */
941 function wp_widget_description( $id ) {
942         if ( !is_scalar($id) )
943                 return;
944
945         global $wp_registered_widgets;
946
947         if ( isset($wp_registered_widgets[$id]['description']) )
948                 return esc_html( $wp_registered_widgets[$id]['description'] );
949 }
950
951 /**
952  * Retrieve description for a sidebar.
953  *
954  * When registering sidebars a 'description' parameter can be included that
955  * describes the sidebar for display on the widget administration panel.
956  *
957  * @since 2.9.0
958  *
959  * @param string $id sidebar ID.
960  * @return string Sidebar description, if available. Null on failure to retrieve description.
961  */
962 function wp_sidebar_description( $id ) {
963         if ( !is_scalar($id) )
964                 return;
965
966         global $wp_registered_sidebars;
967
968         if ( isset($wp_registered_sidebars[$id]['description']) )
969                 return esc_html( $wp_registered_sidebars[$id]['description'] );
970 }
971
972 /**
973  * Remove widget from sidebar.
974  *
975  * @since 2.2.0
976  *
977  * @param int|string $id Widget ID.
978  */
979 function wp_unregister_sidebar_widget($id) {
980
981         /**
982          * Fires just before a widget is removed from a sidebar.
983          *
984          * @since 3.0.0
985          *
986          * @param int $id The widget ID.
987          */
988         do_action( 'wp_unregister_sidebar_widget', $id );
989
990         wp_register_sidebar_widget($id, '', '');
991         wp_unregister_widget_control($id);
992 }
993
994 /**
995  * Registers widget control callback for customizing options.
996  *
997  * The options contains the 'height', 'width', and 'id_base' keys. The 'height'
998  * option is never used. The 'width' option is the width of the fully expanded
999  * control form, but try hard to use the default width. The 'id_base' is for
1000  * multi-widgets (widgets which allow multiple instances such as the text
1001  * widget), an id_base must be provided. The widget id will end up looking like
1002  * `{$id_base}-{$unique_number}`.
1003  *
1004  * @since 2.2.0
1005  *
1006  * @todo Document `$options` as a hash notation, re: WP_Widget::__construct() cross-reference.
1007  * @todo `$params` parameter?
1008  *
1009  * @param int|string   $id               Sidebar ID.
1010  * @param string       $name             Sidebar display name.
1011  * @param callback     $control_callback Run when sidebar is displayed.
1012  * @param array|string $options          Optional. Widget options. See description above. Default empty array.
1013  */
1014 function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
1015         global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
1016
1017         $id = strtolower($id);
1018         $id_base = _get_widget_id_base($id);
1019
1020         if ( empty($control_callback) ) {
1021                 unset($wp_registered_widget_controls[$id]);
1022                 unset($wp_registered_widget_updates[$id_base]);
1023                 return;
1024         }
1025
1026         if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
1027                 if ( isset($wp_registered_widgets[$id]) )
1028                         unset($wp_registered_widgets[$id]);
1029
1030                 return;
1031         }
1032
1033         if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1034                 return;
1035
1036         $defaults = array('width' => 250, 'height' => 200 ); // height is never used
1037         $options = wp_parse_args($options, $defaults);
1038         $options['width'] = (int) $options['width'];
1039         $options['height'] = (int) $options['height'];
1040
1041         $widget = array(
1042                 'name' => $name,
1043                 'id' => $id,
1044                 'callback' => $control_callback,
1045                 'params' => array_slice(func_get_args(), 4)
1046         );
1047         $widget = array_merge($widget, $options);
1048
1049         $wp_registered_widget_controls[$id] = $widget;
1050
1051         if ( isset($wp_registered_widget_updates[$id_base]) )
1052                 return;
1053
1054         if ( isset($widget['params'][0]['number']) )
1055                 $widget['params'][0]['number'] = -1;
1056
1057         unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
1058         $wp_registered_widget_updates[$id_base] = $widget;
1059 }
1060
1061 /**
1062  *
1063  * @global array $wp_registered_widget_updates
1064  * @param string   $id_base
1065  * @param callable $update_callback
1066  * @param array    $options
1067  */
1068 function _register_widget_update_callback($id_base, $update_callback, $options = array()) {
1069         global $wp_registered_widget_updates;
1070
1071         if ( isset($wp_registered_widget_updates[$id_base]) ) {
1072                 if ( empty($update_callback) )
1073                         unset($wp_registered_widget_updates[$id_base]);
1074                 return;
1075         }
1076
1077         $widget = array(
1078                 'callback' => $update_callback,
1079                 'params' => array_slice(func_get_args(), 3)
1080         );
1081
1082         $widget = array_merge($widget, $options);
1083         $wp_registered_widget_updates[$id_base] = $widget;
1084 }
1085
1086 /**
1087  *
1088  * @global array $wp_registered_widget_controls
1089  * @param int|string $id
1090  * @param string     $name
1091  * @param callable   $form_callback
1092  * @param array      $options
1093  * @return null
1094  */
1095 function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
1096         global $wp_registered_widget_controls;
1097
1098         $id = strtolower($id);
1099
1100         if ( empty($form_callback) ) {
1101                 unset($wp_registered_widget_controls[$id]);
1102                 return;
1103         }
1104
1105         if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
1106                 return;
1107
1108         $defaults = array('width' => 250, 'height' => 200 );
1109         $options = wp_parse_args($options, $defaults);
1110         $options['width'] = (int) $options['width'];
1111         $options['height'] = (int) $options['height'];
1112
1113         $widget = array(
1114                 'name' => $name,
1115                 'id' => $id,
1116                 'callback' => $form_callback,
1117                 'params' => array_slice(func_get_args(), 4)
1118         );
1119         $widget = array_merge($widget, $options);
1120
1121         $wp_registered_widget_controls[$id] = $widget;
1122 }
1123
1124 /**
1125  * Remove control callback for widget.
1126  *
1127  * @since 2.2.0
1128  *
1129  * @param int|string $id Widget ID.
1130  */
1131 function wp_unregister_widget_control($id) {
1132         return wp_register_widget_control($id, '', '');
1133 }
1134
1135 /**
1136  * Display dynamic sidebar.
1137  *
1138  * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
1139  * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
1140  * Otherwise, you can pass in a numerical index to display the sidebar at that index.
1141  *
1142  * @since 2.2.0
1143  *
1144  * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
1145  * @return bool True, if widget sidebar was found and called. False if not found or not called.
1146  */
1147 function dynamic_sidebar($index = 1) {
1148         global $wp_registered_sidebars, $wp_registered_widgets;
1149
1150         if ( is_int($index) ) {
1151                 $index = "sidebar-$index";
1152         } else {
1153                 $index = sanitize_title($index);
1154                 foreach ( (array) $wp_registered_sidebars as $key => $value ) {
1155                         if ( sanitize_title($value['name']) == $index ) {
1156                                 $index = $key;
1157                                 break;
1158                         }
1159                 }
1160         }
1161
1162         $sidebars_widgets = wp_get_sidebars_widgets();
1163         if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
1164                 /** This action is documented in wp-includes/widgets.php */
1165                 do_action( 'dynamic_sidebar_before', $index, false );
1166                 /** This action is documented in wp-includes/widgets.php */
1167                 do_action( 'dynamic_sidebar_after',  $index, false );
1168                 /** This filter is documented in wp-includes/widgets.php */
1169                 return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
1170         }
1171
1172         /**
1173          * Fires before widgets are rendered in a dynamic sidebar.
1174          *
1175          * Note: The action also fires for empty sidebars, and on both the front-end
1176          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1177          *
1178          * @since 3.9.0
1179          *
1180          * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1181          * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1182          *                                Default true.
1183          */
1184         do_action( 'dynamic_sidebar_before', $index, true );
1185         $sidebar = $wp_registered_sidebars[$index];
1186
1187         $did_one = false;
1188         foreach ( (array) $sidebars_widgets[$index] as $id ) {
1189
1190                 if ( !isset($wp_registered_widgets[$id]) ) continue;
1191
1192                 $params = array_merge(
1193                         array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
1194                         (array) $wp_registered_widgets[$id]['params']
1195                 );
1196
1197                 // Substitute HTML id and class attributes into before_widget
1198                 $classname_ = '';
1199                 foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
1200                         if ( is_string($cn) )
1201                                 $classname_ .= '_' . $cn;
1202                         elseif ( is_object($cn) )
1203                                 $classname_ .= '_' . get_class($cn);
1204                 }
1205                 $classname_ = ltrim($classname_, '_');
1206                 $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
1207
1208                 /**
1209                  * Filter the parameters passed to a widget's display callback.
1210                  *
1211                  * Note: The filter is evaluated on both the front-end and back-end,
1212                  * including for the Inactive Widgets sidebar on the Widgets screen.
1213                  *
1214                  * @since 2.5.0
1215                  *
1216                  * @see register_sidebar()
1217                  *
1218                  * @param array $params {
1219                  *     @type array $args  {
1220                  *         An array of widget display arguments.
1221                  *
1222                  *         @type string $name          Name of the sidebar the widget is assigned to.
1223                  *         @type string $id            ID of the sidebar the widget is assigned to.
1224                  *         @type string $description   The sidebar description.
1225                  *         @type string $class         CSS class applied to the sidebar container.
1226                  *         @type string $before_widget HTML markup to prepend to each widget in the sidebar.
1227                  *         @type string $after_widget  HTML markup to append to each widget in the sidebar.
1228                  *         @type string $before_title  HTML markup to prepend to the widget title when displayed.
1229                  *         @type string $after_title   HTML markup to append to the widget title when displayed.
1230                  *         @type string $widget_id     ID of the widget.
1231                  *         @type string $widget_name   Name of the widget.
1232                  *     }
1233                  *     @type array $widget_args {
1234                  *         An array of multi-widget arguments.
1235                  *
1236                  *         @type int $number Number increment used for multiples of the same widget.
1237                  *     }
1238                  * }
1239                  */
1240                 $params = apply_filters( 'dynamic_sidebar_params', $params );
1241
1242                 $callback = $wp_registered_widgets[$id]['callback'];
1243
1244                 /**
1245                  * Fires before a widget's display callback is called.
1246                  *
1247                  * Note: The action fires on both the front-end and back-end, including
1248                  * for widgets in the Inactive Widgets sidebar on the Widgets screen.
1249                  *
1250                  * The action is not fired for empty sidebars.
1251                  *
1252                  * @since 3.0.0
1253                  *
1254                  * @param array $widget_id {
1255                  *     An associative array of widget arguments.
1256                  *
1257                  *     @type string $name                Name of the widget.
1258                  *     @type string $id                  Widget ID.
1259                  *     @type array|callback $callback    When the hook is fired on the front-end, $callback is an array
1260                  *                                       containing the widget object. Fired on the back-end, $callback
1261                  *                                       is 'wp_widget_control', see $_callback.
1262                  *     @type array          $params      An associative array of multi-widget arguments.
1263                  *     @type string         $classname   CSS class applied to the widget container.
1264                  *     @type string         $description The widget description.
1265                  *     @type array          $_callback   When the hook is fired on the back-end, $_callback is populated
1266                  *                                       with an array containing the widget object, see $callback.
1267                  * }
1268                  */
1269                 do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
1270
1271                 if ( is_callable($callback) ) {
1272                         call_user_func_array($callback, $params);
1273                         $did_one = true;
1274                 }
1275         }
1276
1277         /**
1278          * Fires after widgets are rendered in a dynamic sidebar.
1279          *
1280          * Note: The action also fires for empty sidebars, and on both the front-end
1281          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1282          *
1283          * @since 3.9.0
1284          *
1285          * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1286          * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1287          *                                Default true.
1288          */
1289         do_action( 'dynamic_sidebar_after', $index, true );
1290
1291         /**
1292          * Filter whether a sidebar has widgets.
1293          *
1294          * Note: The filter is also evaluated for empty sidebars, and on both the front-end
1295          * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1296          *
1297          * @since 3.9.0
1298          *
1299          * @param bool       $did_one Whether at least one widget was rendered in the sidebar.
1300          *                            Default false.
1301          * @param int|string $index   Index, name, or ID of the dynamic sidebar.
1302          */
1303
1304         $did_one = apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
1305
1306         return $did_one;
1307 }
1308
1309 /**
1310  * Whether widget is displayed on the front-end.
1311  *
1312  * Either $callback or $id_base can be used
1313  * $id_base is the first argument when extending WP_Widget class
1314  * Without the optional $widget_id parameter, returns the ID of the first sidebar
1315  * in which the first instance of the widget with the given callback or $id_base is found.
1316  * With the $widget_id parameter, returns the ID of the sidebar where
1317  * the widget with that callback/$id_base AND that ID is found.
1318  *
1319  * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
1320  * this function has to run after widgets have initialized, at action 'init' or later.
1321  *
1322  * @since 2.2.0
1323  *
1324  * @param string $callback Optional, Widget callback to check.
1325  * @param int $widget_id Optional, but needed for checking. Widget ID.
1326  * @param string $id_base Optional, the base ID of a widget created by extending WP_Widget.
1327  * @param bool $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
1328  * @return mixed false if widget is not active or id of sidebar in which the widget is active.
1329  */
1330 function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
1331         global $wp_registered_widgets;
1332
1333         $sidebars_widgets = wp_get_sidebars_widgets();
1334
1335         if ( is_array($sidebars_widgets) ) {
1336                 foreach ( $sidebars_widgets as $sidebar => $widgets ) {
1337                         if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
1338                                 continue;
1339                         }
1340
1341                         if ( is_array($widgets) ) {
1342                                 foreach ( $widgets as $widget ) {
1343                                         if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
1344                                                 if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
1345                                                         return $sidebar;
1346                                         }
1347                                 }
1348                         }
1349                 }
1350         }
1351         return false;
1352 }
1353
1354 /**
1355  * Whether the dynamic sidebar is enabled and used by theme.
1356  *
1357  * @since 2.2.0
1358  *
1359  * @return bool True, if using widgets. False, if not using widgets.
1360  */
1361 function is_dynamic_sidebar() {
1362         global $wp_registered_widgets, $wp_registered_sidebars;
1363         $sidebars_widgets = get_option('sidebars_widgets');
1364         foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
1365                 if ( count($sidebars_widgets[$index]) ) {
1366                         foreach ( (array) $sidebars_widgets[$index] as $widget )
1367                                 if ( array_key_exists($widget, $wp_registered_widgets) )
1368                                         return true;
1369                 }
1370         }
1371         return false;
1372 }
1373
1374 /**
1375  * Whether a sidebar is in use.
1376  *
1377  * @since 2.8.0
1378  *
1379  * @param string|int $index Sidebar name, id or number to check.
1380  * @return bool true if the sidebar is in use, false otherwise.
1381  */
1382 function is_active_sidebar( $index ) {
1383         $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
1384         $sidebars_widgets = wp_get_sidebars_widgets();
1385         $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
1386
1387         /**
1388          * Filter whether a dynamic sidebar is considered "active".
1389          *
1390          * @since 3.9.0
1391          *
1392          * @param bool       $is_active_sidebar Whether or not the sidebar should be considered "active".
1393          *                                      In other words, whether the sidebar contains any widgets.
1394          * @param int|string $index             Index, name, or ID of the dynamic sidebar.
1395          */
1396         return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
1397 }
1398
1399 /* Internal Functions */
1400
1401 /**
1402  * Retrieve full list of sidebars and their widget instance IDs.
1403  *
1404  * Will upgrade sidebar widget list, if needed. Will also save updated list, if
1405  * needed.
1406  *
1407  * @since 2.2.0
1408  * @access private
1409  *
1410  * @param bool $deprecated Not used (argument deprecated).
1411  * @return array Upgraded list of widgets to version 3 array format when called from the admin.
1412  */
1413 function wp_get_sidebars_widgets( $deprecated = true ) {
1414         if ( $deprecated !== true )
1415                 _deprecated_argument( __FUNCTION__, '2.8.1' );
1416
1417         global $_wp_sidebars_widgets, $sidebars_widgets;
1418
1419         // If loading from front page, consult $_wp_sidebars_widgets rather than options
1420         // to see if wp_convert_widget_settings() has made manipulations in memory.
1421         if ( !is_admin() ) {
1422                 if ( empty($_wp_sidebars_widgets) )
1423                         $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
1424
1425                 $sidebars_widgets = $_wp_sidebars_widgets;
1426         } else {
1427                 $sidebars_widgets = get_option('sidebars_widgets', array());
1428         }
1429
1430         if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
1431                 unset($sidebars_widgets['array_version']);
1432
1433         /**
1434          * Filter the list of sidebars and their widgets.
1435          *
1436          * @since 2.7.0
1437          *
1438          * @param array $sidebars_widgets An associative array of sidebars and their widgets.
1439          */
1440         $sidebars_widgets = apply_filters( 'sidebars_widgets', $sidebars_widgets );
1441         return $sidebars_widgets;
1442 }
1443
1444 /**
1445  * Set the sidebar widget option to update sidebars.
1446  *
1447  * @since 2.2.0
1448  * @access private
1449  *
1450  * @param array $sidebars_widgets Sidebar widgets and their settings.
1451  */
1452 function wp_set_sidebars_widgets( $sidebars_widgets ) {
1453         if ( !isset( $sidebars_widgets['array_version'] ) )
1454                 $sidebars_widgets['array_version'] = 3;
1455         update_option( 'sidebars_widgets', $sidebars_widgets );
1456 }
1457
1458 /**
1459  * Retrieve default registered sidebars list.
1460  *
1461  * @since 2.2.0
1462  * @access private
1463  *
1464  * @return array
1465  */
1466 function wp_get_widget_defaults() {
1467         global $wp_registered_sidebars;
1468
1469         $defaults = array();
1470
1471         foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
1472                 $defaults[$index] = array();
1473
1474         return $defaults;
1475 }
1476
1477 /**
1478  * Convert the widget settings from single to multi-widget format.
1479  *
1480  * @since 2.8.0
1481  *
1482  * @param string $base_name
1483  * @param string $option_name
1484  * @param array  $settings
1485  * @return array
1486  */
1487 function wp_convert_widget_settings($base_name, $option_name, $settings) {
1488         // This test may need expanding.
1489         $single = $changed = false;
1490         if ( empty($settings) ) {
1491                 $single = true;
1492         } else {
1493                 foreach ( array_keys($settings) as $number ) {
1494                         if ( 'number' == $number )
1495                                 continue;
1496                         if ( !is_numeric($number) ) {
1497                                 $single = true;
1498                                 break;
1499                         }
1500                 }
1501         }
1502
1503         if ( $single ) {
1504                 $settings = array( 2 => $settings );
1505
1506                 // If loading from the front page, update sidebar in memory but don't save to options
1507                 if ( is_admin() ) {
1508                         $sidebars_widgets = get_option('sidebars_widgets');
1509                 } else {
1510                         if ( empty($GLOBALS['_wp_sidebars_widgets']) )
1511                                 $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
1512                         $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
1513                 }
1514
1515                 foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
1516                         if ( is_array($sidebar) ) {
1517                                 foreach ( $sidebar as $i => $name ) {
1518                                         if ( $base_name == $name ) {
1519                                                 $sidebars_widgets[$index][$i] = "$name-2";
1520                                                 $changed = true;
1521                                                 break 2;
1522                                         }
1523                                 }
1524                         }
1525                 }
1526
1527                 if ( is_admin() && $changed )
1528                         update_option('sidebars_widgets', $sidebars_widgets);
1529         }
1530
1531         $settings['_multiwidget'] = 1;
1532         if ( is_admin() )
1533                 update_option( $option_name, $settings );
1534
1535         return $settings;
1536 }
1537
1538 /**
1539  * Output an arbitrary widget as a template tag.
1540  *
1541  * @since 2.8.0
1542  *
1543  * @param string $widget   The widget's PHP class name (see default-widgets.php).
1544  * @param array  $instance Optional. The widget's instance settings. Default empty array.
1545  * @param array  $args {
1546  *     Optional. Array of arguments to configure the display of the widget.
1547  *
1548  *     @type string $before_widget HTML content that will be prepended to the widget's HTML output.
1549  *                                 Default `<div class="widget %s">`, where `%s` is the widget's class name.
1550  *     @type string $after_widget  HTML content that will be appended to the widget's HTML output.
1551  *                                 Default `</div>`.
1552  *     @type string $before_title  HTML content that will be prepended to the widget's title when displayed.
1553  *                                 Default `<h2 class="widgettitle">`.
1554  *     @type string $after_title   HTML content that will be appended to the widget's title when displayed.
1555  *                                 Default `</h2>`.
1556  * }
1557  */
1558 function the_widget( $widget, $instance = array(), $args = array() ) {
1559         global $wp_widget_factory;
1560
1561         $widget_obj = $wp_widget_factory->widgets[$widget];
1562         if ( ! ( $widget_obj instanceof WP_Widget ) ) {
1563                 return;
1564         }
1565
1566         $before_widget = sprintf('<div class="widget %s">', $widget_obj->widget_options['classname'] );
1567         $default_args = array( 'before_widget' => $before_widget, 'after_widget' => "</div>", 'before_title' => '<h2 class="widgettitle">', 'after_title' => '</h2>' );
1568
1569         $args = wp_parse_args($args, $default_args);
1570         $instance = wp_parse_args($instance);
1571
1572         /**
1573          * Fires before rendering the requested widget.
1574          *
1575          * @since 3.0.0
1576          *
1577          * @param string $widget   The widget's class name.
1578          * @param array  $instance The current widget instance's settings.
1579          * @param array  $args     An array of the widget's sidebar arguments.
1580          */
1581         do_action( 'the_widget', $widget, $instance, $args );
1582
1583         $widget_obj->_set(-1);
1584         $widget_obj->widget($args, $instance);
1585 }
1586
1587 /**
1588  * Private
1589  */
1590 function _get_widget_id_base($id) {
1591         return preg_replace( '/-[0-9]+$/', '', $id );
1592 }
1593
1594 /**
1595  * Handle sidebars config after theme change
1596  *
1597  * @access private
1598  * @since 3.3.0
1599  */
1600 function _wp_sidebars_changed() {
1601         global $sidebars_widgets;
1602
1603         if ( ! is_array( $sidebars_widgets ) )
1604                 $sidebars_widgets = wp_get_sidebars_widgets();
1605
1606         retrieve_widgets(true);
1607 }
1608
1609 /**
1610  * Look for "lost" widgets, this has to run at least on each theme change.
1611  *
1612  * @since 2.8.0
1613  *
1614  * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
1615  *                                   of 'customize' defers updates for the Customizer.
1616  * @return array
1617  */
1618 function retrieve_widgets( $theme_changed = false ) {
1619         global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
1620
1621         $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
1622         $orphaned = 0;
1623
1624         $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
1625         if ( is_array( $old_sidebars_widgets ) ) {
1626                 // time() that sidebars were stored is in $old_sidebars_widgets['time']
1627                 $_sidebars_widgets = $old_sidebars_widgets['data'];
1628
1629                 if ( 'customize' !== $theme_changed ) {
1630                         remove_theme_mod( 'sidebars_widgets' );
1631                 }
1632
1633                 foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1634                         if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
1635                                 continue;
1636                         }
1637
1638                         if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
1639                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
1640                                 unset( $_sidebars_widgets[$sidebar] );
1641                         }
1642                 }
1643         } else {
1644                 if ( empty( $sidebars_widgets ) )
1645                         return;
1646
1647                 unset( $sidebars_widgets['array_version'] );
1648
1649                 $old = array_keys($sidebars_widgets);
1650                 sort($old);
1651                 sort($registered_sidebar_keys);
1652
1653                 if ( $old == $registered_sidebar_keys )
1654                         return;
1655
1656                 $_sidebars_widgets = array(
1657                         'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
1658                 );
1659
1660                 unset( $sidebars_widgets['wp_inactive_widgets'] );
1661
1662                 foreach ( $wp_registered_sidebars as $id => $settings ) {
1663                         if ( $theme_changed ) {
1664                                 $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
1665                         } else {
1666                                 // no theme change, grab only sidebars that are currently registered
1667                                 if ( isset( $sidebars_widgets[$id] ) ) {
1668                                         $_sidebars_widgets[$id] = $sidebars_widgets[$id];
1669                                         unset( $sidebars_widgets[$id] );
1670                                 }
1671                         }
1672                 }
1673
1674                 foreach ( $sidebars_widgets as $val ) {
1675                         if ( is_array($val) && ! empty( $val ) )
1676                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
1677                 }
1678         }
1679
1680         // discard invalid, theme-specific widgets from sidebars
1681         $shown_widgets = array();
1682
1683         foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1684                 if ( !is_array($widgets) )
1685                         continue;
1686
1687                 $_widgets = array();
1688                 foreach ( $widgets as $widget ) {
1689                         if ( isset($wp_registered_widgets[$widget]) )
1690                                 $_widgets[] = $widget;
1691                 }
1692
1693                 $_sidebars_widgets[$sidebar] = $_widgets;
1694                 $shown_widgets = array_merge($shown_widgets, $_widgets);
1695         }
1696
1697         $sidebars_widgets = $_sidebars_widgets;
1698         unset($_sidebars_widgets, $_widgets);
1699
1700         // find hidden/lost multi-widget instances
1701         $lost_widgets = array();
1702         foreach ( $wp_registered_widgets as $key => $val ) {
1703                 if ( in_array($key, $shown_widgets, true) )
1704                         continue;
1705
1706                 $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
1707
1708                 if ( 2 > (int) $number )
1709                         continue;
1710
1711                 $lost_widgets[] = $key;
1712         }
1713
1714         $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
1715         if ( 'customize' !== $theme_changed ) {
1716                 wp_set_sidebars_widgets( $sidebars_widgets );
1717         }
1718
1719         return $sidebars_widgets;
1720 }