Wordpress 4.6
[autoinstalls/wordpress.git] / wp-includes / class-wp-widget.php
1 <?php
2 /**
3  * Widget API: WP_Widget base class
4  *
5  * @package WordPress
6  * @subpackage Widgets
7  * @since 4.4.0
8  */
9
10 /**
11  * Core base class extended to register widgets.
12  *
13  * This class must be extended for each widget, and WP_Widget::widget() must be overriden.
14  *
15  * If adding widget options, WP_Widget::update() and WP_Widget::form() should also be overridden.
16  *
17  * @since 2.8.0
18  * @since 4.4.0 Moved to its own file from wp-includes/widgets.php
19  */
20 class WP_Widget {
21
22         /**
23          * Root ID for all widgets of this type.
24          *
25          * @since 2.8.0
26          * @access public
27          * @var mixed|string
28          */
29         public $id_base;
30
31         /**
32          * Name for this widget type.
33          *
34          * @since 2.8.0
35          * @access public
36          * @var string
37          */
38         public $name;
39
40         /**
41          * Option array passed to wp_register_sidebar_widget().
42          *
43          * @since 2.8.0
44          * @access public
45          * @var array
46          */
47         public $widget_options;
48
49         /**
50          * Option array passed to wp_register_widget_control().
51          *
52          * @since 2.8.0
53          * @access public
54          * @var array
55          */
56         public $control_options;
57
58         /**
59          * Unique ID number of the current instance.
60          *
61          * @since 2.8.0
62          * @access public
63          * @var bool|int
64          */
65         public $number = false;
66
67         /**
68          * Unique ID string of the current instance (id_base-number).
69          *
70          * @since 2.8.0
71          * @access public
72          * @var bool|string
73          */
74         public $id = false;
75
76         /**
77          * Whether the widget data has been updated.
78          *
79          * Set to true when the data is updated after a POST submit - ensures it does
80          * not happen twice.
81          *
82          * @since 2.8.0
83          * @access public
84          * @var bool
85          */
86         public $updated = false;
87
88         //
89         // Member functions that must be overriden by subclasses.
90         //
91
92         /**
93          * Echoes the widget content.
94          *
95          * Sub-classes should over-ride this function to generate their widget code.
96          *
97          * @since 2.8.0
98          * @access public
99          *
100          * @param array $args     Display arguments including 'before_title', 'after_title',
101          *                        'before_widget', and 'after_widget'.
102          * @param array $instance The settings for the particular instance of the widget.
103          */
104         public function widget( $args, $instance ) {
105                 die('function WP_Widget::widget() must be over-ridden in a sub-class.');
106         }
107
108         /**
109          * Updates a particular instance of a widget.
110          *
111          * This function should check that `$new_instance` is set correctly. The newly-calculated
112          * value of `$instance` should be returned. If false is returned, the instance won't be
113          * saved/updated.
114          *
115          * @since 2.8.0
116          * @access public
117          *
118          * @param array $new_instance New settings for this instance as input by the user via
119          *                            WP_Widget::form().
120          * @param array $old_instance Old settings for this instance.
121          * @return array Settings to save or bool false to cancel saving.
122          */
123         public function update( $new_instance, $old_instance ) {
124                 return $new_instance;
125         }
126
127         /**
128          * Outputs the settings update form.
129          *
130          * @since 2.8.0
131          * @access public
132          *
133          * @param array $instance Current settings.
134          * @return string Default return is 'noform'.
135          */
136         public function form( $instance ) {
137                 echo '<p class="no-options-widget">' . __('There are no options for this widget.') . '</p>';
138                 return 'noform';
139         }
140
141         // Functions you'll need to call.
142
143         /**
144          * PHP5 constructor.
145          *
146          * @since 2.8.0
147          * @access public
148          *
149          * @param string $id_base         Optional Base ID for the widget, lowercase and unique. If left empty,
150          *                                a portion of the widget's class name will be used Has to be unique.
151          * @param string $name            Name for the widget displayed on the configuration page.
152          * @param array  $widget_options  Optional. Widget options. See wp_register_sidebar_widget() for information
153          *                                on accepted arguments. Default empty array.
154          * @param array  $control_options Optional. Widget control options. See wp_register_widget_control() for
155          *                                information on accepted arguments. Default empty array.
156          */
157         public function __construct( $id_base, $name, $widget_options = array(), $control_options = array() ) {
158                 $this->id_base = empty($id_base) ? preg_replace( '/(wp_)?widget_/', '', strtolower(get_class($this)) ) : strtolower($id_base);
159                 $this->name = $name;
160                 $this->option_name = 'widget_' . $this->id_base;
161                 $this->widget_options = wp_parse_args( $widget_options, array( 'classname' => $this->option_name, 'customize_selective_refresh' => false ) );
162                 $this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base ) );
163         }
164
165         /**
166          * PHP4 constructor.
167          *
168          * @since 2.8.0
169          * @access public
170          *
171          * @see __construct()
172          *
173          * @param string $id_base         Optional Base ID for the widget, lowercase and unique. If left empty,
174          *                                a portion of the widget's class name will be used Has to be unique.
175          * @param string $name            Name for the widget displayed on the configuration page.
176          * @param array  $widget_options  Optional. Widget options. See wp_register_sidebar_widget() for information
177          *                                on accepted arguments. Default empty array.
178          * @param array  $control_options Optional. Widget control options. See wp_register_widget_control() for
179          *                                information on accepted arguments. Default empty array.
180          */
181         public function WP_Widget( $id_base, $name, $widget_options = array(), $control_options = array() ) {
182                 _deprecated_constructor( 'WP_Widget', '4.3.0', get_class( $this ) );
183                 WP_Widget::__construct( $id_base, $name, $widget_options, $control_options );
184         }
185
186         /**
187          * Constructs name attributes for use in form() fields
188          *
189          * This function should be used in form() methods to create name attributes for fields
190          * to be saved by update()
191          *
192          * @since 2.8.0
193          * @since 4.4.0 Array format field names are now accepted.
194          * @access public
195          *
196          * @param string $field_name Field name
197          * @return string Name attribute for $field_name
198          */
199         public function get_field_name($field_name) {
200                 if ( false === $pos = strpos( $field_name, '[' ) ) {
201                         return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field_name . ']';
202                 } else {
203                         return 'widget-' . $this->id_base . '[' . $this->number . '][' . substr_replace( $field_name, '][', $pos, strlen( '[' ) );
204                 }
205         }
206
207         /**
208          * Constructs id attributes for use in WP_Widget::form() fields.
209          *
210          * This function should be used in form() methods to create id attributes
211          * for fields to be saved by WP_Widget::update().
212          *
213          * @since 2.8.0
214          * @since 4.4.0 Array format field IDs are now accepted.
215          * @access public
216          *
217          * @param string $field_name Field name.
218          * @return string ID attribute for `$field_name`.
219          */
220         public function get_field_id( $field_name ) {
221                 return 'widget-' . $this->id_base . '-' . $this->number . '-' . trim( str_replace( array( '[]', '[', ']' ), array( '', '-', '' ), $field_name ), '-' );
222         }
223
224         /**
225          * Register all widget instances of this widget class.
226          *
227          * @since 2.8.0
228          * @access public
229          */
230         public function _register() {
231                 $settings = $this->get_settings();
232                 $empty = true;
233
234                 // When $settings is an array-like object, get an intrinsic array for use with array_keys().
235                 if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
236                         $settings = $settings->getArrayCopy();
237                 }
238
239                 if ( is_array( $settings ) ) {
240                         foreach ( array_keys( $settings ) as $number ) {
241                                 if ( is_numeric( $number ) ) {
242                                         $this->_set( $number );
243                                         $this->_register_one( $number );
244                                         $empty = false;
245                                 }
246                         }
247                 }
248
249                 if ( $empty ) {
250                         // If there are none, we register the widget's existence with a generic template.
251                         $this->_set( 1 );
252                         $this->_register_one();
253                 }
254         }
255
256         /**
257          * Sets the internal order number for the widget instance.
258          *
259          * @since 2.8.0
260          * @access public
261          *
262          * @param int $number The unique order number of this widget instance compared to other
263          *                    instances of the same class.
264          */
265         public function _set($number) {
266                 $this->number = $number;
267                 $this->id = $this->id_base . '-' . $number;
268         }
269
270         /**
271          * Retrieves the widget display callback.
272          *
273          * @since 2.8.0
274          * @access public
275          *
276          * @return callable Display callback.
277          */
278         public function _get_display_callback() {
279                 return array($this, 'display_callback');
280         }
281
282         /**
283          * Retrieves the widget update callback.
284          *
285          * @since 2.8.0
286          * @access public
287          *
288          * @return callable Update callback.
289          */
290         public function _get_update_callback() {
291                 return array($this, 'update_callback');
292         }
293
294         /**
295          * Retrieves the form callback.
296          *
297          * @since 2.8.0
298          * @access public
299          *
300          * @return callable Form callback.
301          */
302         public function _get_form_callback() {
303                 return array($this, 'form_callback');
304         }
305
306         /**
307          * Determines whether the current request is inside the Customizer preview.
308          *
309          * If true -- the current request is inside the Customizer preview, then
310          * the object cache gets suspended and widgets should check this to decide
311          * whether they should store anything persistently to the object cache,
312          * to transients, or anywhere else.
313          *
314          * @since 3.9.0
315          * @access public
316          *
317          * @global WP_Customize_Manager $wp_customize
318          *
319          * @return bool True if within the Customizer preview, false if not.
320          */
321         public function is_preview() {
322                 global $wp_customize;
323                 return ( isset( $wp_customize ) && $wp_customize->is_preview() ) ;
324         }
325
326         /**
327          * Generates the actual widget content (Do NOT override).
328          *
329          * Finds the instance and calls WP_Widget::widget().
330          *
331          * @since 2.8.0
332          * @access public
333          *
334          * @param array     $args        Display arguments. See WP_Widget::widget() for information
335          *                               on accepted arguments.
336          * @param int|array $widget_args {
337          *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
338          *     Default 1.
339          *
340          *     @type int $number Number increment used for multiples of the same widget.
341          * }
342          */
343         public function display_callback( $args, $widget_args = 1 ) {
344                 if ( is_numeric( $widget_args ) ) {
345                         $widget_args = array( 'number' => $widget_args );
346                 }
347
348                 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
349                 $this->_set( $widget_args['number'] );
350                 $instances = $this->get_settings();
351
352                 if ( array_key_exists( $this->number, $instances ) ) {
353                         $instance = $instances[ $this->number ];
354
355                         /**
356                          * Filters the settings for a particular widget instance.
357                          *
358                          * Returning false will effectively short-circuit display of the widget.
359                          *
360                          * @since 2.8.0
361                          *
362                          * @param array     $instance The current widget instance's settings.
363                          * @param WP_Widget $this     The current widget instance.
364                          * @param array     $args     An array of default widget arguments.
365                          */
366                         $instance = apply_filters( 'widget_display_callback', $instance, $this, $args );
367
368                         if ( false === $instance ) {
369                                 return;
370                         }
371
372                         $was_cache_addition_suspended = wp_suspend_cache_addition();
373                         if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
374                                 wp_suspend_cache_addition( true );
375                         }
376
377                         $this->widget( $args, $instance );
378
379                         if ( $this->is_preview() ) {
380                                 wp_suspend_cache_addition( $was_cache_addition_suspended );
381                         }
382                 }
383         }
384
385         /**
386          * Handles changed settings (Do NOT override).
387          *
388          * @since 2.8.0
389          * @access public
390          *
391          * @global array $wp_registered_widgets
392          *
393          * @param int $deprecated Not used.
394          */
395         public function update_callback( $deprecated = 1 ) {
396                 global $wp_registered_widgets;
397
398                 $all_instances = $this->get_settings();
399
400                 // We need to update the data
401                 if ( $this->updated )
402                         return;
403
404                 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
405                         // Delete the settings for this instance of the widget
406                         if ( isset($_POST['the-widget-id']) )
407                                 $del_id = $_POST['the-widget-id'];
408                         else
409                                 return;
410
411                         if ( isset($wp_registered_widgets[$del_id]['params'][0]['number']) ) {
412                                 $number = $wp_registered_widgets[$del_id]['params'][0]['number'];
413
414                                 if ( $this->id_base . '-' . $number == $del_id )
415                                         unset($all_instances[$number]);
416                         }
417                 } else {
418                         if ( isset($_POST['widget-' . $this->id_base]) && is_array($_POST['widget-' . $this->id_base]) ) {
419                                 $settings = $_POST['widget-' . $this->id_base];
420                         } elseif ( isset($_POST['id_base']) && $_POST['id_base'] == $this->id_base ) {
421                                 $num = $_POST['multi_number'] ? (int) $_POST['multi_number'] : (int) $_POST['widget_number'];
422                                 $settings = array( $num => array() );
423                         } else {
424                                 return;
425                         }
426
427                         foreach ( $settings as $number => $new_instance ) {
428                                 $new_instance = stripslashes_deep($new_instance);
429                                 $this->_set($number);
430
431                                 $old_instance = isset($all_instances[$number]) ? $all_instances[$number] : array();
432
433                                 $was_cache_addition_suspended = wp_suspend_cache_addition();
434                                 if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
435                                         wp_suspend_cache_addition( true );
436                                 }
437
438                                 $instance = $this->update( $new_instance, $old_instance );
439
440                                 if ( $this->is_preview() ) {
441                                         wp_suspend_cache_addition( $was_cache_addition_suspended );
442                                 }
443
444                                 /**
445                                  * Filters a widget's settings before saving.
446                                  *
447                                  * Returning false will effectively short-circuit the widget's ability
448                                  * to update settings.
449                                  *
450                                  * @since 2.8.0
451                                  *
452                                  * @param array     $instance     The current widget instance's settings.
453                                  * @param array     $new_instance Array of new widget settings.
454                                  * @param array     $old_instance Array of old widget settings.
455                                  * @param WP_Widget $this         The current widget instance.
456                                  */
457                                 $instance = apply_filters( 'widget_update_callback', $instance, $new_instance, $old_instance, $this );
458                                 if ( false !== $instance ) {
459                                         $all_instances[$number] = $instance;
460                                 }
461
462                                 break; // run only once
463                         }
464                 }
465
466                 $this->save_settings($all_instances);
467                 $this->updated = true;
468         }
469
470         /**
471          * Generates the widget control form (Do NOT override).
472          *
473          * @since 2.8.0
474          * @access public
475          *
476          * @param int|array $widget_args {
477          *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
478          *     Default 1.
479          *
480          *     @type int $number Number increment used for multiples of the same widget.
481          * }
482          * @return string|null
483          */
484         public function form_callback( $widget_args = 1 ) {
485                 if ( is_numeric($widget_args) )
486                         $widget_args = array( 'number' => $widget_args );
487
488                 $widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
489                 $all_instances = $this->get_settings();
490
491                 if ( -1 == $widget_args['number'] ) {
492                         // We echo out a form where 'number' can be set later
493                         $this->_set('__i__');
494                         $instance = array();
495                 } else {
496                         $this->_set($widget_args['number']);
497                         $instance = $all_instances[ $widget_args['number'] ];
498                 }
499
500                 /**
501                  * Filters the widget instance's settings before displaying the control form.
502                  *
503                  * Returning false effectively short-circuits display of the control form.
504                  *
505                  * @since 2.8.0
506                  *
507                  * @param array     $instance The current widget instance's settings.
508                  * @param WP_Widget $this     The current widget instance.
509                  */
510                 $instance = apply_filters( 'widget_form_callback', $instance, $this );
511
512                 $return = null;
513                 if ( false !== $instance ) {
514                         $return = $this->form($instance);
515
516                         /**
517                          * Fires at the end of the widget control form.
518                          *
519                          * Use this hook to add extra fields to the widget form. The hook
520                          * is only fired if the value passed to the 'widget_form_callback'
521                          * hook is not false.
522                          *
523                          * Note: If the widget has no form, the text echoed from the default
524                          * form method can be hidden using CSS.
525                          *
526                          * @since 2.8.0
527                          *
528                          * @param WP_Widget $this     The widget instance, passed by reference.
529                          * @param null      $return   Return null if new fields are added.
530                          * @param array     $instance An array of the widget's settings.
531                          */
532                         do_action_ref_array( 'in_widget_form', array( &$this, &$return, $instance ) );
533                 }
534                 return $return;
535         }
536
537         /**
538          * Registers an instance of the widget class.
539          *
540          * @since 2.8.0
541          * @access public
542          *
543          * @param integer $number Optional. The unique order number of this widget instance
544          *                        compared to other instances of the same class. Default -1.
545          */
546         public function _register_one( $number = -1 ) {
547                 wp_register_sidebar_widget(     $this->id, $this->name, $this->_get_display_callback(), $this->widget_options, array( 'number' => $number ) );
548                 _register_widget_update_callback( $this->id_base, $this->_get_update_callback(), $this->control_options, array( 'number' => -1 ) );
549                 _register_widget_form_callback( $this->id, $this->name, $this->_get_form_callback(), $this->control_options, array( 'number' => $number ) );
550         }
551
552         /**
553          * Saves the settings for all instances of the widget class.
554          *
555          * @since 2.8.0
556          * @access public
557          *
558          * @param array $settings Multi-dimensional array of widget instance settings.
559          */
560         public function save_settings( $settings ) {
561                 $settings['_multiwidget'] = 1;
562                 update_option( $this->option_name, $settings );
563         }
564
565         /**
566          * Retrieves the settings for all instances of the widget class.
567          *
568          * @since 2.8.0
569          * @access public
570          *
571          * @return array Multi-dimensional array of widget instance settings.
572          */
573         public function get_settings() {
574
575                 $settings = get_option( $this->option_name );
576
577                 if ( false === $settings ) {
578                         if ( isset( $this->alt_option_name ) ) {
579                                 $settings = get_option( $this->alt_option_name );
580                         } else {
581                                 // Save an option so it can be autoloaded next time.
582                                 $this->save_settings( array() );
583                         }
584                 }
585
586                 if ( ! is_array( $settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) {
587                         $settings = array();
588                 }
589
590                 if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) {
591                         // Old format, convert if single widget.
592                         $settings = wp_convert_widget_settings( $this->id_base, $this->option_name, $settings );
593                 }
594
595                 unset( $settings['_multiwidget'], $settings['__i__'] );
596                 return $settings;
597         }
598 }