WordPress.org

Make WordPress Core

Ticket #33915: dynamic_sidebar_index.diff

File dynamic_sidebar_index.diff, 53.9 KB (added by fahidjavid, 6 years ago)
Line 
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 */
24class 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 ( array_key_exists( $this->number, $instances ) ) {
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 */
564class 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 */
635global $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 */
717function 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 */
737function 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 */
769function 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 */
842function 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 */
892function 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 */
925function 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 */
980function 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 */
1003function 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 */
1020function 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 */
1060function 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 */
1112function _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 */
1139function _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 */
1175function 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 */
1194function 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        $index = apply_filters( 'dynamic_sidebar_index', $index );
1210       
1211        $sidebars_widgets = wp_get_sidebars_widgets();
1212        if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
1213                /** This action is documented in wp-includes/widgets.php */
1214                do_action( 'dynamic_sidebar_before', $index, false );
1215                /** This action is documented in wp-includes/widgets.php */
1216                do_action( 'dynamic_sidebar_after',  $index, false );
1217                /** This filter is documented in wp-includes/widgets.php */
1218                return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
1219        }
1220
1221        /**
1222         * Fires before widgets are rendered in a dynamic sidebar.
1223         *
1224         * Note: The action also fires for empty sidebars, and on both the front-end
1225         * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1226         *
1227         * @since 3.9.0
1228         *
1229         * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1230         * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1231         *                                Default true.
1232         */
1233        do_action( 'dynamic_sidebar_before', $index, true );
1234        $sidebar = $wp_registered_sidebars[$index];
1235
1236        $did_one = false;
1237        foreach ( (array) $sidebars_widgets[$index] as $id ) {
1238
1239                if ( !isset($wp_registered_widgets[$id]) ) continue;
1240
1241                $params = array_merge(
1242                        array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
1243                        (array) $wp_registered_widgets[$id]['params']
1244                );
1245
1246                // Substitute HTML id and class attributes into before_widget
1247                $classname_ = '';
1248                foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
1249                        if ( is_string($cn) )
1250                                $classname_ .= '_' . $cn;
1251                        elseif ( is_object($cn) )
1252                                $classname_ .= '_' . get_class($cn);
1253                }
1254                $classname_ = ltrim($classname_, '_');
1255                $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
1256
1257                /**
1258                 * Filter the parameters passed to a widget's display callback.
1259                 *
1260                 * Note: The filter is evaluated on both the front-end and back-end,
1261                 * including for the Inactive Widgets sidebar on the Widgets screen.
1262                 *
1263                 * @since 2.5.0
1264                 *
1265                 * @see register_sidebar()
1266                 *
1267                 * @param array $params {
1268                 *     @type array $args  {
1269                 *         An array of widget display arguments.
1270                 *
1271                 *         @type string $name          Name of the sidebar the widget is assigned to.
1272                 *         @type string $id            ID of the sidebar the widget is assigned to.
1273                 *         @type string $description   The sidebar description.
1274                 *         @type string $class         CSS class applied to the sidebar container.
1275                 *         @type string $before_widget HTML markup to prepend to each widget in the sidebar.
1276                 *         @type string $after_widget  HTML markup to append to each widget in the sidebar.
1277                 *         @type string $before_title  HTML markup to prepend to the widget title when displayed.
1278                 *         @type string $after_title   HTML markup to append to the widget title when displayed.
1279                 *         @type string $widget_id     ID of the widget.
1280                 *         @type string $widget_name   Name of the widget.
1281                 *     }
1282                 *     @type array $widget_args {
1283                 *         An array of multi-widget arguments.
1284                 *
1285                 *         @type int $number Number increment used for multiples of the same widget.
1286                 *     }
1287                 * }
1288                 */
1289                $params = apply_filters( 'dynamic_sidebar_params', $params );
1290
1291                $callback = $wp_registered_widgets[$id]['callback'];
1292
1293                /**
1294                 * Fires before a widget's display callback is called.
1295                 *
1296                 * Note: The action fires on both the front-end and back-end, including
1297                 * for widgets in the Inactive Widgets sidebar on the Widgets screen.
1298                 *
1299                 * The action is not fired for empty sidebars.
1300                 *
1301                 * @since 3.0.0
1302                 *
1303                 * @param array $widget_id {
1304                 *     An associative array of widget arguments.
1305                 *
1306                 *     @type string $name                Name of the widget.
1307                 *     @type string $id                  Widget ID.
1308                 *     @type array|callback $callback    When the hook is fired on the front-end, $callback is an array
1309                 *                                       containing the widget object. Fired on the back-end, $callback
1310                 *                                       is 'wp_widget_control', see $_callback.
1311                 *     @type array          $params      An associative array of multi-widget arguments.
1312                 *     @type string         $classname   CSS class applied to the widget container.
1313                 *     @type string         $description The widget description.
1314                 *     @type array          $_callback   When the hook is fired on the back-end, $_callback is populated
1315                 *                                       with an array containing the widget object, see $callback.
1316                 * }
1317                 */
1318                do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
1319
1320                if ( is_callable($callback) ) {
1321                        call_user_func_array($callback, $params);
1322                        $did_one = true;
1323                }
1324        }
1325
1326        /**
1327         * Fires after widgets are rendered in a dynamic sidebar.
1328         *
1329         * Note: The action also fires for empty sidebars, and on both the front-end
1330         * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1331         *
1332         * @since 3.9.0
1333         *
1334         * @param int|string $index       Index, name, or ID of the dynamic sidebar.
1335         * @param bool       $has_widgets Whether the sidebar is populated with widgets.
1336         *                                Default true.
1337         */
1338        do_action( 'dynamic_sidebar_after', $index, true );
1339
1340        /**
1341         * Filter whether a sidebar has widgets.
1342         *
1343         * Note: The filter is also evaluated for empty sidebars, and on both the front-end
1344         * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
1345         *
1346         * @since 3.9.0
1347         *
1348         * @param bool       $did_one Whether at least one widget was rendered in the sidebar.
1349         *                            Default false.
1350         * @param int|string $index   Index, name, or ID of the dynamic sidebar.
1351         */
1352        return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
1353}
1354
1355/**
1356 * Whether widget is displayed on the front-end.
1357 *
1358 * Either $callback or $id_base can be used
1359 * $id_base is the first argument when extending WP_Widget class
1360 * Without the optional $widget_id parameter, returns the ID of the first sidebar
1361 * in which the first instance of the widget with the given callback or $id_base is found.
1362 * With the $widget_id parameter, returns the ID of the sidebar where
1363 * the widget with that callback/$id_base AND that ID is found.
1364 *
1365 * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
1366 * this function has to run after widgets have initialized, at action 'init' or later.
1367 *
1368 * @since 2.2.0
1369 *
1370 * @global array $wp_registered_widgets
1371 *
1372 * @param string $callback      Optional, Widget callback to check.
1373 * @param int    $widget_id     Optional, but needed for checking. Widget ID.
1374 * @param string $id_base       Optional, the base ID of a widget created by extending WP_Widget.
1375 * @param bool   $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
1376 * @return string|false False if widget is not active or id of sidebar in which the widget is active.
1377 */
1378function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
1379        global $wp_registered_widgets;
1380
1381        $sidebars_widgets = wp_get_sidebars_widgets();
1382
1383        if ( is_array($sidebars_widgets) ) {
1384                foreach ( $sidebars_widgets as $sidebar => $widgets ) {
1385                        if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
1386                                continue;
1387                        }
1388
1389                        if ( is_array($widgets) ) {
1390                                foreach ( $widgets as $widget ) {
1391                                        if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
1392                                                if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
1393                                                        return $sidebar;
1394                                        }
1395                                }
1396                        }
1397                }
1398        }
1399        return false;
1400}
1401
1402/**
1403 * Whether the dynamic sidebar is enabled and used by theme.
1404 *
1405 * @since 2.2.0
1406 *
1407 * @global array $wp_registered_widgets
1408 * @global array $wp_registered_sidebars
1409 *
1410 * @return bool True, if using widgets. False, if not using widgets.
1411 */
1412function is_dynamic_sidebar() {
1413        global $wp_registered_widgets, $wp_registered_sidebars;
1414        $sidebars_widgets = get_option('sidebars_widgets');
1415        foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
1416                if ( count($sidebars_widgets[$index]) ) {
1417                        foreach ( (array) $sidebars_widgets[$index] as $widget )
1418                                if ( array_key_exists($widget, $wp_registered_widgets) )
1419                                        return true;
1420                }
1421        }
1422        return false;
1423}
1424
1425/**
1426 * Whether a sidebar is in use.
1427 *
1428 * @since 2.8.0
1429 *
1430 * @param string|int $index Sidebar name, id or number to check.
1431 * @return bool true if the sidebar is in use, false otherwise.
1432 */
1433function is_active_sidebar( $index ) {
1434        $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
1435        $sidebars_widgets = wp_get_sidebars_widgets();
1436        $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
1437
1438        /**
1439         * Filter whether a dynamic sidebar is considered "active".
1440         *
1441         * @since 3.9.0
1442         *
1443         * @param bool       $is_active_sidebar Whether or not the sidebar should be considered "active".
1444         *                                      In other words, whether the sidebar contains any widgets.
1445         * @param int|string $index             Index, name, or ID of the dynamic sidebar.
1446         */
1447        return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
1448}
1449
1450/* Internal Functions */
1451
1452/**
1453 * Retrieve full list of sidebars and their widget instance IDs.
1454 *
1455 * Will upgrade sidebar widget list, if needed. Will also save updated list, if
1456 * needed.
1457 *
1458 * @since 2.2.0
1459 * @access private
1460 *
1461 * @global array $_wp_sidebars_widgets
1462 * @global array $sidebars_widgets
1463 *
1464 * @param bool $deprecated Not used (argument deprecated).
1465 * @return array Upgraded list of widgets to version 3 array format when called from the admin.
1466 */
1467function wp_get_sidebars_widgets( $deprecated = true ) {
1468        if ( $deprecated !== true )
1469                _deprecated_argument( __FUNCTION__, '2.8.1' );
1470
1471        global $_wp_sidebars_widgets, $sidebars_widgets;
1472
1473        // If loading from front page, consult $_wp_sidebars_widgets rather than options
1474        // to see if wp_convert_widget_settings() has made manipulations in memory.
1475        if ( !is_admin() ) {
1476                if ( empty($_wp_sidebars_widgets) )
1477                        $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
1478
1479                $sidebars_widgets = $_wp_sidebars_widgets;
1480        } else {
1481                $sidebars_widgets = get_option('sidebars_widgets', array());
1482        }
1483
1484        if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
1485                unset($sidebars_widgets['array_version']);
1486
1487        /**
1488         * Filter the list of sidebars and their widgets.
1489         *
1490         * @since 2.7.0
1491         *
1492         * @param array $sidebars_widgets An associative array of sidebars and their widgets.
1493         */
1494        return apply_filters( 'sidebars_widgets', $sidebars_widgets );
1495}
1496
1497/**
1498 * Set the sidebar widget option to update sidebars.
1499 *
1500 * @since 2.2.0
1501 * @access private
1502 *
1503 * @param array $sidebars_widgets Sidebar widgets and their settings.
1504 */
1505function wp_set_sidebars_widgets( $sidebars_widgets ) {
1506        if ( !isset( $sidebars_widgets['array_version'] ) )
1507                $sidebars_widgets['array_version'] = 3;
1508        update_option( 'sidebars_widgets', $sidebars_widgets );
1509}
1510
1511/**
1512 * Retrieve default registered sidebars list.
1513 *
1514 * @since 2.2.0
1515 * @access private
1516 *
1517 * @global array $wp_registered_sidebars
1518 *
1519 * @return array
1520 */
1521function wp_get_widget_defaults() {
1522        global $wp_registered_sidebars;
1523
1524        $defaults = array();
1525
1526        foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
1527                $defaults[$index] = array();
1528
1529        return $defaults;
1530}
1531
1532/**
1533 * Convert the widget settings from single to multi-widget format.
1534 *
1535 * @since 2.8.0
1536 *
1537 * @global array $_wp_sidebars_widgets
1538 *
1539 * @param string $base_name
1540 * @param string $option_name
1541 * @param array  $settings
1542 * @return array
1543 */
1544function wp_convert_widget_settings($base_name, $option_name, $settings) {
1545        // This test may need expanding.
1546        $single = $changed = false;
1547        if ( empty($settings) ) {
1548                $single = true;
1549        } else {
1550                foreach ( array_keys($settings) as $number ) {
1551                        if ( 'number' == $number )
1552                                continue;
1553                        if ( !is_numeric($number) ) {
1554                                $single = true;
1555                                break;
1556                        }
1557                }
1558        }
1559
1560        if ( $single ) {
1561                $settings = array( 2 => $settings );
1562
1563                // If loading from the front page, update sidebar in memory but don't save to options
1564                if ( is_admin() ) {
1565                        $sidebars_widgets = get_option('sidebars_widgets');
1566                } else {
1567                        if ( empty($GLOBALS['_wp_sidebars_widgets']) )
1568                                $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
1569                        $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
1570                }
1571
1572                foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
1573                        if ( is_array($sidebar) ) {
1574                                foreach ( $sidebar as $i => $name ) {
1575                                        if ( $base_name == $name ) {
1576                                                $sidebars_widgets[$index][$i] = "$name-2";
1577                                                $changed = true;
1578                                                break 2;
1579                                        }
1580                                }
1581                        }
1582                }
1583
1584                if ( is_admin() && $changed )
1585                        update_option('sidebars_widgets', $sidebars_widgets);
1586        }
1587
1588        $settings['_multiwidget'] = 1;
1589        if ( is_admin() )
1590                update_option( $option_name, $settings );
1591
1592        return $settings;
1593}
1594
1595/**
1596 * Output an arbitrary widget as a template tag.
1597 *
1598 * @since 2.8.0
1599 *
1600 * @global WP_Widget_Factory $wp_widget_factory
1601 *
1602 * @param string $widget   The widget's PHP class name (see default-widgets.php).
1603 * @param array  $instance Optional. The widget's instance settings. Default empty array.
1604 * @param array  $args {
1605 *     Optional. Array of arguments to configure the display of the widget.
1606 *
1607 *     @type string $before_widget HTML content that will be prepended to the widget's HTML output.
1608 *                                 Default `<div class="widget %s">`, where `%s` is the widget's class name.
1609 *     @type string $after_widget  HTML content that will be appended to the widget's HTML output.
1610 *                                 Default `</div>`.
1611 *     @type string $before_title  HTML content that will be prepended to the widget's title when displayed.
1612 *                                 Default `<h2 class="widgettitle">`.
1613 *     @type string $after_title   HTML content that will be appended to the widget's title when displayed.
1614 *                                 Default `</h2>`.
1615 * }
1616 */
1617function the_widget( $widget, $instance = array(), $args = array() ) {
1618        global $wp_widget_factory;
1619
1620        $widget_obj = $wp_widget_factory->widgets[$widget];
1621        if ( ! ( $widget_obj instanceof WP_Widget ) ) {
1622                return;
1623        }
1624
1625        $before_widget = sprintf('<div class="widget %s">', $widget_obj->widget_options['classname'] );
1626        $default_args = array( 'before_widget' => $before_widget, 'after_widget' => "</div>", 'before_title' => '<h2 class="widgettitle">', 'after_title' => '</h2>' );
1627
1628        $args = wp_parse_args($args, $default_args);
1629        $instance = wp_parse_args($instance);
1630
1631        /**
1632         * Fires before rendering the requested widget.
1633         *
1634         * @since 3.0.0
1635         *
1636         * @param string $widget   The widget's class name.
1637         * @param array  $instance The current widget instance's settings.
1638         * @param array  $args     An array of the widget's sidebar arguments.
1639         */
1640        do_action( 'the_widget', $widget, $instance, $args );
1641
1642        $widget_obj->_set(-1);
1643        $widget_obj->widget($args, $instance);
1644}
1645
1646/**
1647 * Private
1648 *
1649 * @return string
1650 */
1651function _get_widget_id_base($id) {
1652        return preg_replace( '/-[0-9]+$/', '', $id );
1653}
1654
1655/**
1656 * Handle sidebars config after theme change
1657 *
1658 * @access private
1659 * @since 3.3.0
1660 *
1661 * @global array $sidebars_widgets
1662 */
1663function _wp_sidebars_changed() {
1664        global $sidebars_widgets;
1665
1666        if ( ! is_array( $sidebars_widgets ) )
1667                $sidebars_widgets = wp_get_sidebars_widgets();
1668
1669        retrieve_widgets(true);
1670}
1671
1672/**
1673 * Look for "lost" widgets, this has to run at least on each theme change.
1674 *
1675 * @since 2.8.0
1676 *
1677 * @global array $wp_registered_sidebars
1678 * @global array $sidebars_widgets
1679 * @global array $wp_registered_widgets
1680 *
1681 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
1682 *                                   of 'customize' defers updates for the Customizer.
1683 * @return array|void
1684 */
1685function retrieve_widgets( $theme_changed = false ) {
1686        global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
1687
1688        $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
1689        $orphaned = 0;
1690
1691        $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
1692        if ( is_array( $old_sidebars_widgets ) ) {
1693                // time() that sidebars were stored is in $old_sidebars_widgets['time']
1694                $_sidebars_widgets = $old_sidebars_widgets['data'];
1695
1696                if ( 'customize' !== $theme_changed ) {
1697                        remove_theme_mod( 'sidebars_widgets' );
1698                }
1699
1700                foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1701                        if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
1702                                continue;
1703                        }
1704
1705                        if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
1706                                $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
1707                                unset( $_sidebars_widgets[$sidebar] );
1708                        }
1709                }
1710        } else {
1711                if ( empty( $sidebars_widgets ) )
1712                        return;
1713
1714                unset( $sidebars_widgets['array_version'] );
1715
1716                $old = array_keys($sidebars_widgets);
1717                sort($old);
1718                sort($registered_sidebar_keys);
1719
1720                if ( $old == $registered_sidebar_keys )
1721                        return;
1722
1723                $_sidebars_widgets = array(
1724                        'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
1725                );
1726
1727                unset( $sidebars_widgets['wp_inactive_widgets'] );
1728
1729                foreach ( $wp_registered_sidebars as $id => $settings ) {
1730                        if ( $theme_changed ) {
1731                                $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
1732                        } else {
1733                                // no theme change, grab only sidebars that are currently registered
1734                                if ( isset( $sidebars_widgets[$id] ) ) {
1735                                        $_sidebars_widgets[$id] = $sidebars_widgets[$id];
1736                                        unset( $sidebars_widgets[$id] );
1737                                }
1738                        }
1739                }
1740
1741                foreach ( $sidebars_widgets as $val ) {
1742                        if ( is_array($val) && ! empty( $val ) )
1743                                $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
1744                }
1745        }
1746
1747        // discard invalid, theme-specific widgets from sidebars
1748        $shown_widgets = array();
1749
1750        foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
1751                if ( !is_array($widgets) )
1752                        continue;
1753
1754                $_widgets = array();
1755                foreach ( $widgets as $widget ) {
1756                        if ( isset($wp_registered_widgets[$widget]) )
1757                                $_widgets[] = $widget;
1758                }
1759
1760                $_sidebars_widgets[$sidebar] = $_widgets;
1761                $shown_widgets = array_merge($shown_widgets, $_widgets);
1762        }
1763
1764        $sidebars_widgets = $_sidebars_widgets;
1765        unset($_sidebars_widgets, $_widgets);
1766
1767        // find hidden/lost multi-widget instances
1768        $lost_widgets = array();
1769        foreach ( $wp_registered_widgets as $key => $val ) {
1770                if ( in_array($key, $shown_widgets, true) )
1771                        continue;
1772
1773                $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
1774
1775                if ( 2 > (int) $number )
1776                        continue;
1777
1778                $lost_widgets[] = $key;
1779        }
1780
1781        $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
1782        if ( 'customize' !== $theme_changed ) {
1783                wp_set_sidebars_widgets( $sidebars_widgets );
1784        }
1785
1786        return $sidebars_widgets;
1787}