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