home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / class-wp-widget.php < prev    next >
Encoding:
PHP Script  |  2017-10-02  |  17.2 KB  |  589 lines

  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. }
  589.