PageRenderTime 51ms CodeModel.GetById 12ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-includes/class-wp-widget.php

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