wp/wp-includes/class-wp-widget.php
changeset 7 cf61fcea0001
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     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  */
       
    20 class 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 }