balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-widget.php (18157B)


      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 	 * Subclasses should override 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 overridden in a subclass.' );
    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 PHP 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
    158 	 *                                information 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 		if ( ! empty( $id_base ) ) {
    164 			$id_base = strtolower( $id_base );
    165 		} else {
    166 			$id_base = preg_replace( '/(wp_)?widget_/', '', strtolower( get_class( $this ) ) );
    167 		}
    168 
    169 		$this->id_base         = $id_base;
    170 		$this->name            = $name;
    171 		$this->option_name     = 'widget_' . $this->id_base;
    172 		$this->widget_options  = wp_parse_args(
    173 			$widget_options,
    174 			array(
    175 				'classname'                   => str_replace( '\\', '_', $this->option_name ),
    176 				'customize_selective_refresh' => false,
    177 			)
    178 		);
    179 		$this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base ) );
    180 	}
    181 
    182 	/**
    183 	 * PHP4 constructor.
    184 	 *
    185 	 * @since 2.8.0
    186 	 * @deprecated 4.3.0 Use __construct() instead.
    187 	 *
    188 	 * @see WP_Widget::__construct()
    189 	 *
    190 	 * @param string $id_base         Optional. Base ID for the widget, lowercase and unique. If left empty,
    191 	 *                                a portion of the widget's PHP class name will be used. Has to be unique.
    192 	 * @param string $name            Name for the widget displayed on the configuration page.
    193 	 * @param array  $widget_options  Optional. Widget options. See wp_register_sidebar_widget() for
    194 	 *                                information on accepted arguments. Default empty array.
    195 	 * @param array  $control_options Optional. Widget control options. See wp_register_widget_control() for
    196 	 *                                information on accepted arguments. Default empty array.
    197 	 */
    198 	public function WP_Widget( $id_base, $name, $widget_options = array(), $control_options = array() ) {
    199 		_deprecated_constructor( 'WP_Widget', '4.3.0', get_class( $this ) );
    200 		WP_Widget::__construct( $id_base, $name, $widget_options, $control_options );
    201 	}
    202 
    203 	/**
    204 	 * Constructs name attributes for use in form() fields
    205 	 *
    206 	 * This function should be used in form() methods to create name attributes for fields
    207 	 * to be saved by update()
    208 	 *
    209 	 * @since 2.8.0
    210 	 * @since 4.4.0 Array format field names are now accepted.
    211 	 *
    212 	 * @param string $field_name Field name.
    213 	 * @return string Name attribute for `$field_name`.
    214 	 */
    215 	public function get_field_name( $field_name ) {
    216 		$pos = strpos( $field_name, '[' );
    217 
    218 		if ( false !== $pos ) {
    219 			// Replace the first occurrence of '[' with ']['.
    220 			$field_name = '[' . substr_replace( $field_name, '][', $pos, strlen( '[' ) );
    221 		} else {
    222 			$field_name = '[' . $field_name . ']';
    223 		}
    224 
    225 		return 'widget-' . $this->id_base . '[' . $this->number . ']' . $field_name;
    226 	}
    227 
    228 	/**
    229 	 * Constructs id attributes for use in WP_Widget::form() fields.
    230 	 *
    231 	 * This function should be used in form() methods to create id attributes
    232 	 * for fields to be saved by WP_Widget::update().
    233 	 *
    234 	 * @since 2.8.0
    235 	 * @since 4.4.0 Array format field IDs are now accepted.
    236 	 *
    237 	 * @param string $field_name Field name.
    238 	 * @return string ID attribute for `$field_name`.
    239 	 */
    240 	public function get_field_id( $field_name ) {
    241 		$field_name = str_replace( array( '[]', '[', ']' ), array( '', '-', '' ), $field_name );
    242 		$field_name = trim( $field_name, '-' );
    243 
    244 		return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field_name;
    245 	}
    246 
    247 	/**
    248 	 * Register all widget instances of this widget class.
    249 	 *
    250 	 * @since 2.8.0
    251 	 */
    252 	public function _register() {
    253 		$settings = $this->get_settings();
    254 		$empty    = true;
    255 
    256 		// When $settings is an array-like object, get an intrinsic array for use with array_keys().
    257 		if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
    258 			$settings = $settings->getArrayCopy();
    259 		}
    260 
    261 		if ( is_array( $settings ) ) {
    262 			foreach ( array_keys( $settings ) as $number ) {
    263 				if ( is_numeric( $number ) ) {
    264 					$this->_set( $number );
    265 					$this->_register_one( $number );
    266 					$empty = false;
    267 				}
    268 			}
    269 		}
    270 
    271 		if ( $empty ) {
    272 			// If there are none, we register the widget's existence with a generic template.
    273 			$this->_set( 1 );
    274 			$this->_register_one();
    275 		}
    276 	}
    277 
    278 	/**
    279 	 * Sets the internal order number for the widget instance.
    280 	 *
    281 	 * @since 2.8.0
    282 	 *
    283 	 * @param int $number The unique order number of this widget instance compared to other
    284 	 *                    instances of the same class.
    285 	 */
    286 	public function _set( $number ) {
    287 		$this->number = $number;
    288 		$this->id     = $this->id_base . '-' . $number;
    289 	}
    290 
    291 	/**
    292 	 * Retrieves the widget display callback.
    293 	 *
    294 	 * @since 2.8.0
    295 	 *
    296 	 * @return callable Display callback.
    297 	 */
    298 	public function _get_display_callback() {
    299 		return array( $this, 'display_callback' );
    300 	}
    301 
    302 	/**
    303 	 * Retrieves the widget update callback.
    304 	 *
    305 	 * @since 2.8.0
    306 	 *
    307 	 * @return callable Update callback.
    308 	 */
    309 	public function _get_update_callback() {
    310 		return array( $this, 'update_callback' );
    311 	}
    312 
    313 	/**
    314 	 * Retrieves the form callback.
    315 	 *
    316 	 * @since 2.8.0
    317 	 *
    318 	 * @return callable Form callback.
    319 	 */
    320 	public function _get_form_callback() {
    321 		return array( $this, 'form_callback' );
    322 	}
    323 
    324 	/**
    325 	 * Determines whether the current request is inside the Customizer preview.
    326 	 *
    327 	 * If true -- the current request is inside the Customizer preview, then
    328 	 * the object cache gets suspended and widgets should check this to decide
    329 	 * whether they should store anything persistently to the object cache,
    330 	 * to transients, or anywhere else.
    331 	 *
    332 	 * @since 3.9.0
    333 	 *
    334 	 * @global WP_Customize_Manager $wp_customize
    335 	 *
    336 	 * @return bool True if within the Customizer preview, false if not.
    337 	 */
    338 	public function is_preview() {
    339 		global $wp_customize;
    340 		return ( isset( $wp_customize ) && $wp_customize->is_preview() );
    341 	}
    342 
    343 	/**
    344 	 * Generates the actual widget content (Do NOT override).
    345 	 *
    346 	 * Finds the instance and calls WP_Widget::widget().
    347 	 *
    348 	 * @since 2.8.0
    349 	 *
    350 	 * @param array     $args        Display arguments. See WP_Widget::widget() for information
    351 	 *                               on accepted arguments.
    352 	 * @param int|array $widget_args {
    353 	 *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
    354 	 *     Default 1.
    355 	 *
    356 	 *     @type int $number Number increment used for multiples of the same widget.
    357 	 * }
    358 	 */
    359 	public function display_callback( $args, $widget_args = 1 ) {
    360 		if ( is_numeric( $widget_args ) ) {
    361 			$widget_args = array( 'number' => $widget_args );
    362 		}
    363 
    364 		$widget_args = wp_parse_args( $widget_args, array( 'number' => -1 ) );
    365 		$this->_set( $widget_args['number'] );
    366 		$instances = $this->get_settings();
    367 
    368 		if ( array_key_exists( $this->number, $instances ) ) {
    369 			$instance = $instances[ $this->number ];
    370 
    371 			/**
    372 			 * Filters the settings for a particular widget instance.
    373 			 *
    374 			 * Returning false will effectively short-circuit display of the widget.
    375 			 *
    376 			 * @since 2.8.0
    377 			 *
    378 			 * @param array     $instance The current widget instance's settings.
    379 			 * @param WP_Widget $widget   The current widget instance.
    380 			 * @param array     $args     An array of default widget arguments.
    381 			 */
    382 			$instance = apply_filters( 'widget_display_callback', $instance, $this, $args );
    383 
    384 			if ( false === $instance ) {
    385 				return;
    386 			}
    387 
    388 			$was_cache_addition_suspended = wp_suspend_cache_addition();
    389 			if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
    390 				wp_suspend_cache_addition( true );
    391 			}
    392 
    393 			$this->widget( $args, $instance );
    394 
    395 			if ( $this->is_preview() ) {
    396 				wp_suspend_cache_addition( $was_cache_addition_suspended );
    397 			}
    398 		}
    399 	}
    400 
    401 	/**
    402 	 * Handles changed settings (Do NOT override).
    403 	 *
    404 	 * @since 2.8.0
    405 	 *
    406 	 * @global array $wp_registered_widgets
    407 	 *
    408 	 * @param int $deprecated Not used.
    409 	 */
    410 	public function update_callback( $deprecated = 1 ) {
    411 		global $wp_registered_widgets;
    412 
    413 		$all_instances = $this->get_settings();
    414 
    415 		// We need to update the data.
    416 		if ( $this->updated ) {
    417 			return;
    418 		}
    419 
    420 		if ( isset( $_POST['delete_widget'] ) && $_POST['delete_widget'] ) {
    421 			// Delete the settings for this instance of the widget.
    422 			if ( isset( $_POST['the-widget-id'] ) ) {
    423 				$del_id = $_POST['the-widget-id'];
    424 			} else {
    425 				return;
    426 			}
    427 
    428 			if ( isset( $wp_registered_widgets[ $del_id ]['params'][0]['number'] ) ) {
    429 				$number = $wp_registered_widgets[ $del_id ]['params'][0]['number'];
    430 
    431 				if ( $this->id_base . '-' . $number == $del_id ) {
    432 					unset( $all_instances[ $number ] );
    433 				}
    434 			}
    435 		} else {
    436 			if ( isset( $_POST[ 'widget-' . $this->id_base ] ) && is_array( $_POST[ 'widget-' . $this->id_base ] ) ) {
    437 				$settings = $_POST[ 'widget-' . $this->id_base ];
    438 			} elseif ( isset( $_POST['id_base'] ) && $_POST['id_base'] == $this->id_base ) {
    439 				$num      = $_POST['multi_number'] ? (int) $_POST['multi_number'] : (int) $_POST['widget_number'];
    440 				$settings = array( $num => array() );
    441 			} else {
    442 				return;
    443 			}
    444 
    445 			foreach ( $settings as $number => $new_instance ) {
    446 				$new_instance = stripslashes_deep( $new_instance );
    447 				$this->_set( $number );
    448 
    449 				$old_instance = isset( $all_instances[ $number ] ) ? $all_instances[ $number ] : array();
    450 
    451 				$was_cache_addition_suspended = wp_suspend_cache_addition();
    452 				if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
    453 					wp_suspend_cache_addition( true );
    454 				}
    455 
    456 				$instance = $this->update( $new_instance, $old_instance );
    457 
    458 				if ( $this->is_preview() ) {
    459 					wp_suspend_cache_addition( $was_cache_addition_suspended );
    460 				}
    461 
    462 				/**
    463 				 * Filters a widget's settings before saving.
    464 				 *
    465 				 * Returning false will effectively short-circuit the widget's ability
    466 				 * to update settings.
    467 				 *
    468 				 * @since 2.8.0
    469 				 *
    470 				 * @param array     $instance     The current widget instance's settings.
    471 				 * @param array     $new_instance Array of new widget settings.
    472 				 * @param array     $old_instance Array of old widget settings.
    473 				 * @param WP_Widget $widget       The current widget instance.
    474 				 */
    475 				$instance = apply_filters( 'widget_update_callback', $instance, $new_instance, $old_instance, $this );
    476 
    477 				if ( false !== $instance ) {
    478 					$all_instances[ $number ] = $instance;
    479 				}
    480 
    481 				break; // Run only once.
    482 			}
    483 		}
    484 
    485 		$this->save_settings( $all_instances );
    486 		$this->updated = true;
    487 	}
    488 
    489 	/**
    490 	 * Generates the widget control form (Do NOT override).
    491 	 *
    492 	 * @since 2.8.0
    493 	 *
    494 	 * @param int|array $widget_args {
    495 	 *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
    496 	 *     Default 1.
    497 	 *
    498 	 *     @type int $number Number increment used for multiples of the same widget.
    499 	 * }
    500 	 * @return string|null
    501 	 */
    502 	public function form_callback( $widget_args = 1 ) {
    503 		if ( is_numeric( $widget_args ) ) {
    504 			$widget_args = array( 'number' => $widget_args );
    505 		}
    506 
    507 		$widget_args   = wp_parse_args( $widget_args, array( 'number' => -1 ) );
    508 		$all_instances = $this->get_settings();
    509 
    510 		if ( -1 == $widget_args['number'] ) {
    511 			// We echo out a form where 'number' can be set later.
    512 			$this->_set( '__i__' );
    513 			$instance = array();
    514 		} else {
    515 			$this->_set( $widget_args['number'] );
    516 			$instance = $all_instances[ $widget_args['number'] ];
    517 		}
    518 
    519 		/**
    520 		 * Filters the widget instance's settings before displaying the control form.
    521 		 *
    522 		 * Returning false effectively short-circuits display of the control form.
    523 		 *
    524 		 * @since 2.8.0
    525 		 *
    526 		 * @param array     $instance The current widget instance's settings.
    527 		 * @param WP_Widget $widget   The current widget instance.
    528 		 */
    529 		$instance = apply_filters( 'widget_form_callback', $instance, $this );
    530 
    531 		$return = null;
    532 
    533 		if ( false !== $instance ) {
    534 			$return = $this->form( $instance );
    535 
    536 			/**
    537 			 * Fires at the end of the widget control form.
    538 			 *
    539 			 * Use this hook to add extra fields to the widget form. The hook
    540 			 * is only fired if the value passed to the 'widget_form_callback'
    541 			 * hook is not false.
    542 			 *
    543 			 * Note: If the widget has no form, the text echoed from the default
    544 			 * form method can be hidden using CSS.
    545 			 *
    546 			 * @since 2.8.0
    547 			 *
    548 			 * @param WP_Widget $widget   The widget instance (passed by reference).
    549 			 * @param null      $return   Return null if new fields are added.
    550 			 * @param array     $instance An array of the widget's settings.
    551 			 */
    552 			do_action_ref_array( 'in_widget_form', array( &$this, &$return, $instance ) );
    553 		}
    554 
    555 		return $return;
    556 	}
    557 
    558 	/**
    559 	 * Registers an instance of the widget class.
    560 	 *
    561 	 * @since 2.8.0
    562 	 *
    563 	 * @param int $number Optional. The unique order number of this widget instance
    564 	 *                    compared to other instances of the same class. Default -1.
    565 	 */
    566 	public function _register_one( $number = -1 ) {
    567 		wp_register_sidebar_widget(
    568 			$this->id,
    569 			$this->name,
    570 			$this->_get_display_callback(),
    571 			$this->widget_options,
    572 			array( 'number' => $number )
    573 		);
    574 
    575 		_register_widget_update_callback(
    576 			$this->id_base,
    577 			$this->_get_update_callback(),
    578 			$this->control_options,
    579 			array( 'number' => -1 )
    580 		);
    581 
    582 		_register_widget_form_callback(
    583 			$this->id,
    584 			$this->name,
    585 			$this->_get_form_callback(),
    586 			$this->control_options,
    587 			array( 'number' => $number )
    588 		);
    589 	}
    590 
    591 	/**
    592 	 * Saves the settings for all instances of the widget class.
    593 	 *
    594 	 * @since 2.8.0
    595 	 *
    596 	 * @param array $settings Multi-dimensional array of widget instance settings.
    597 	 */
    598 	public function save_settings( $settings ) {
    599 		$settings['_multiwidget'] = 1;
    600 		update_option( $this->option_name, $settings );
    601 	}
    602 
    603 	/**
    604 	 * Retrieves the settings for all instances of the widget class.
    605 	 *
    606 	 * @since 2.8.0
    607 	 *
    608 	 * @return array Multi-dimensional array of widget instance settings.
    609 	 */
    610 	public function get_settings() {
    611 
    612 		$settings = get_option( $this->option_name );
    613 
    614 		if ( false === $settings ) {
    615 			if ( isset( $this->alt_option_name ) ) {
    616 				$settings = get_option( $this->alt_option_name );
    617 			} else {
    618 				// Save an option so it can be autoloaded next time.
    619 				$this->save_settings( array() );
    620 			}
    621 		}
    622 
    623 		if ( ! is_array( $settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) {
    624 			$settings = array();
    625 		}
    626 
    627 		if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) {
    628 			// Old format, convert if single widget.
    629 			$settings = wp_convert_widget_settings( $this->id_base, $this->option_name, $settings );
    630 		}
    631 
    632 		unset( $settings['_multiwidget'], $settings['__i__'] );
    633 
    634 		return $settings;
    635 	}
    636 }