Make WordPress Core

Changeset 31370


Ignore:
Timestamp:
02/08/2015 11:10:05 PM (12 years ago)
Author:
ocean90
Message:

Customizer: Introduce an API to create WP_Customize_Settings for dynamically-created settings.

  • Introduce WP_Customize_Manager::add_dynamic_settings() to register dynamically-created settings.
  • Introduce customize_dynamic_setting_args filter to pass an array of args to a dynamic setting's constructor.
  • Add unit tests for WP_Customize_Manager and WP_Customize_Widgets.
  • See WP_Customize_Widgets as an example.

props westonruter.
fixes #30936.

Location:
trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r31360 r31370  
    6666         * Unsanitized values for Customize Settings parsed from $_POST['customized'].
    6767         *
    68          * @var array|false
     68         * @var array
    6969         */
    7070        private $_post_values;
     
    103103
    104104                add_action( 'customize_register',                 array( $this, 'register_controls' ) );
     105                add_action( 'customize_register',                 array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first
    105106                add_action( 'customize_controls_init',            array( $this, 'prepare_controls' ) );
    106107                add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
     
    111112         *
    112113         * @since 3.4.0
    113          *
     114         * @since 4.2.0 Added $action param.
     115         *
     116         * @param string|null $action whether the supplied Ajax action is being run.
    114117         * @return bool
    115118         */
    116         public function doing_ajax() {
    117                 return isset( $_POST['customized'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX );
     119        public function doing_ajax( $action = null ) {
     120                $doing_ajax = ( defined( 'DOING_AJAX' ) && DOING_AJAX );
     121                if ( ! $doing_ajax ) {
     122                        return false;
     123                }
     124
     125                if ( ! $action ) {
     126                        return true;
     127                } else {
     128                        // Note: we can't just use doing_action( "wp_ajax_{$action}" ) because we need to check before admin-ajax.php gets to that point
     129                        return isset( $_REQUEST['action'] ) && wp_unslash( $_REQUEST['action'] ) === $action;
     130                }
    118131        }
    119132
     
    412425                                $this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true );
    413426                        }
    414                         if ( empty( $this->_post_values ) ) { // if not isset or of JSON error
    415                                 $this->_post_values = false;
     427                        if ( empty( $this->_post_values ) ) { // if not isset or if JSON error
     428                                $this->_post_values = array();
    416429                        }
    417430                }
     
    440453                        return $default;
    441454                }
     455        }
     456
     457        /**
     458         * Override a setting's (unsanitized) value as found in any incoming $_POST['customized']
     459         *
     460         * @since 4.2.0
     461         *
     462         * @param string $setting_id  The ID for the WP_Customize_Setting instance.
     463         * @param mixed $value
     464         */
     465        public function set_post_value( $setting_id, $value ) {
     466                $this->unsanitized_post_values();
     467                $this->_post_values[ $setting_id ] = $value;
    442468        }
    443469
     
    728754
    729755        /**
     756         * Register any dynamically-created settings, such as those from $_POST['customized'] that have no corresponding setting created.
     757         *
     758         * This is a mechanism to "wake up" settings that have been dynamically created
     759         * on the frontend and have been sent to WordPress in $_POST['customized']. When WP
     760         * loads, the dynamically-created settings then will get created and previewed
     761         * even though they are not directly created statically with code.
     762         *
     763         * @since 4.2.0
     764         *
     765         * @param string[] $setting_ids The setting IDs to add.
     766         * @return WP_Customize_Setting[] The settings added.
     767         */
     768        public function add_dynamic_settings( $setting_ids ) {
     769                $new_settings = array();
     770                foreach ( $setting_ids as $setting_id ) {
     771                        // Skip settings already created
     772                        if ( $this->get_setting( $setting_id ) ) {
     773                                continue;
     774                        }
     775
     776                        $setting_args = false;
     777                        $setting_class = 'WP_Customize_Setting';
     778
     779                        /**
     780                         * Filter a dynamic setting's constructor args.
     781                         *
     782                         * For a dynamic setting to be registered, this filter must be employed
     783                         * to override the default false value with an array of args to pass to
     784                         * the WP_Customize_Setting constructor.
     785                         *
     786                         * @since 4.2.0
     787                         *
     788                         * @param false|array $setting_args  The arguments to the WP_Customize_Setting constructor.
     789                         * @param string      $setting_id    ID for dynamic setting, usually coming from $_POST['customized'].
     790                         */
     791                        $setting_args = apply_filters( 'customize_dynamic_setting_args', $setting_args, $setting_id );
     792                        if ( false === $setting_args ) {
     793                                continue;
     794                        }
     795
     796                        /**
     797                         * Allow non-statically created settings to be constructed with custom WP_Customize_Setting subclass.
     798                         *
     799                         * @since 4.2.0
     800                         *
     801                         * @param string $setting_class  WP_Customize_Setting or a subclass.
     802                         * @param string $setting_id     ID for dynamic setting, usually coming from $_POST['customized'].
     803                         * @param string $setting_args   WP_Customize_Setting or a subclass.
     804                         */
     805                        $setting_class = apply_filters( 'customize_dynamic_setting_class', $setting_class, $setting_id, $setting_args );
     806
     807                        $setting = new $setting_class( $this, $setting_id, $setting_args );
     808                        $this->add_setting( $setting );
     809                        $new_settings[] = $setting;
     810                }
     811                return $new_settings;
     812        }
     813
     814        /**
    730815         * Retrieve a customize setting.
    731816         *
     
    736821         */
    737822        public function get_setting( $id ) {
    738                 if ( isset( $this->settings[ $id ] ) )
     823                if ( isset( $this->settings[ $id ] ) ) {
    739824                        return $this->settings[ $id ];
     825                }
    740826        }
    741827
     
    12731359                        ) );
    12741360                }
     1361        }
     1362
     1363        /**
     1364         * Add settings from the POST data that were not added with code, e.g. dynamically-created settings for Widgets
     1365         *
     1366         * @since 4.2.0
     1367         */
     1368        public function register_dynamic_settings() {
     1369                $this->add_dynamic_settings( array_keys( $this->unsanitized_post_values() ) );
    12751370        }
    12761371
  • trunk/src/wp-includes/class-wp-customize-setting.php

    r31329 r31370  
    5454
    5555        protected $id_data = array();
    56 
    57         /**
    58          * Cached and sanitized $_POST value for the setting.
    59          *
    60          * @access private
    61          * @var mixed
    62          */
    63         private $_post_value;
    6456
    6557        /**
     
    164156        public function _preview_filter( $original ) {
    165157                $undefined = new stdClass(); // symbol hack
    166                 $post_value = $this->manager->post_value( $this, $undefined );
     158                $post_value = $this->post_value( $undefined );
    167159                if ( $undefined === $post_value ) {
    168160                        $value = $this->_original_value;
     
    212204         */
    213205        final public function post_value( $default = null ) {
    214                 // Check for a cached value
    215                 if ( isset( $this->_post_value ) )
    216                         return $this->_post_value;
    217 
    218                 // Call the manager for the post value
    219                 $result = $this->manager->post_value( $this );
    220 
    221                 if ( isset( $result ) )
    222                         return $this->_post_value = $result;
    223                 else
    224                         return $default;
     206                return $this->manager->post_value( $this, $default );
    225207        }
    226208
  • trunk/src/wp-includes/class-wp-customize-widgets.php

    r31226 r31370  
    3636         * @since 3.9.0
    3737         * @access protected
    38          * @var
    39          */
    40         protected $_customized;
     38         * @var array
     39         */
     40        protected $rendered_sidebars = array();
    4141
    4242        /**
     
    4545         * @var array
    4646         */
    47         protected $_prepreview_added_filters = array();
     47        protected $rendered_widgets = array();
    4848
    4949        /**
     
    5252         * @var array
    5353         */
    54         protected $rendered_sidebars = array();
    55 
    56         /**
    57          * @since 3.9.0
     54        protected $old_sidebars_widgets = array();
     55
     56        /**
     57         * Mapping of setting type to setting ID pattern.
     58         *
     59         * @since 4.2.0
    5860         * @access protected
    5961         * @var array
    6062         */
    61         protected $rendered_widgets = array();
    62 
    63         /**
    64          * @since 3.9.0
    65          * @access protected
    66          * @var array
    67          */
    68         protected $old_sidebars_widgets = array();
     63        protected $setting_id_patterns = array(
     64                'widget_instance' => '/^(widget_.+?)(?:\[(\d+)\])?$/',
     65                'sidebar_widgets' => '/^sidebars_widgets\[(.+?)\]$/',
     66        );
    6967
    7068        /**
     
    7977                $this->manager = $manager;
    8078
    81                 add_action( 'after_setup_theme',                       array( $this, 'setup_widget_addition_previews' ) );
     79                add_filter( 'customize_dynamic_setting_args',          array( $this, 'filter_customize_dynamic_setting_args' ), 10, 2 );
     80                add_action( 'after_setup_theme',                       array( $this, 'register_settings' ) );
    8281                add_action( 'wp_loaded',                               array( $this, 'override_sidebars_widgets_for_theme_switch' ) );
    8382                add_action( 'customize_controls_init',                 array( $this, 'customize_controls_init' ) );
     
    9695
    9796        /**
     97         * Get the widget setting type given a setting ID.
     98         *
     99         * @since 4.2.0
     100         *
     101         * @param $setting_id
     102         *
     103         * @return string|null
     104         */
     105        protected function get_setting_type( $setting_id ) {
     106                static $cache = array();
     107                if ( isset( $cache[ $setting_id ] ) ) {
     108                        return $cache[ $setting_id ];
     109                }
     110                foreach ( $this->setting_id_patterns as $type => $pattern ) {
     111                        if ( preg_match( $pattern, $setting_id ) ) {
     112                                $cache[ $setting_id ] = $type;
     113                                return $type;
     114                        }
     115                }
     116                return null;
     117        }
     118
     119        /**
     120         * Inspect the incoming customized data for any widget settings, and dynamically add them up-front so widgets will be initialized properly.
     121         *
     122         * @since 4.2.0
     123         */
     124        public function register_settings() {
     125                $widget_setting_ids = array();
     126                $incoming_setting_ids = array_keys( $this->manager->unsanitized_post_values() );
     127                foreach ( $incoming_setting_ids as $setting_id ) {
     128                        if ( ! is_null( $this->get_setting_type( $setting_id ) ) ) {
     129                                $widget_setting_ids[] = $setting_id;
     130                        }
     131                }
     132                if ( $this->manager->doing_ajax( 'update-widget' ) && isset( $_REQUEST['widget-id'] ) ) {
     133                        $widget_setting_ids[] = $this->get_setting_id( wp_unslash( $_REQUEST['widget-id'] ) );
     134                }
     135
     136                $settings = $this->manager->add_dynamic_settings( array_unique( $widget_setting_ids ) );
     137
     138                /*
     139                 * Preview settings right away so that widgets and sidebars will get registered properly.
     140                 * But don't do this if a customize_save because this will cause WP to think there is nothing
     141                 * changed that needs to be saved.
     142                 */
     143                if ( ! $this->manager->doing_ajax( 'customize_save' ) ) {
     144                        foreach ( $settings as $setting ) {
     145                                $setting->preview();
     146                        }
     147                }
     148        }
     149
     150        /**
     151         * Determine the arguments for a dynamically-created setting.
     152         *
     153         * @since 4.2.0
     154         *
     155         * @param false|array $args
     156         * @param string $setting_id
     157         * @return false|array
     158         */
     159        public function filter_customize_dynamic_setting_args( $args, $setting_id ) {
     160                if ( $this->get_setting_type( $setting_id ) ) {
     161                        $args = $this->get_setting_args( $setting_id );
     162                }
     163                return $args;
     164        }
     165
     166        /**
    98167         * Get an unslashed post value or return a default.
    99168         *
     
    111180                }
    112181
    113                 return wp_unslash( $_POST[$name] );
    114         }
    115 
    116         /**
    117          * Set up widget addition previews.
    118          *
    119          * Since the widgets get registered on 'widgets_init' before the Customizer
    120          * settings are set up on 'customize_register', we have to filter the options
    121          * similarly to how the setting previewer will filter the options later.
    122          *
    123          * @since 3.9.0
    124          *
    125          * @access public
    126          */
    127         public function setup_widget_addition_previews() {
    128                 $is_customize_preview = false;
    129 
    130                 if ( ! empty( $this->manager ) && ! is_admin() && 'on' === $this->get_post_value( 'wp_customize' ) ) {
    131                         $is_customize_preview = check_ajax_referer( 'preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false );
    132                 }
    133 
    134                 $is_ajax_widget_update = false;
    135                 if ( $this->manager->doing_ajax() && 'update-widget' === $this->get_post_value( 'action' ) ) {
    136                         $is_ajax_widget_update = check_ajax_referer( 'update-widget', 'nonce', false );
    137                 }
    138 
    139                 $is_ajax_customize_save = false;
    140                 if ( $this->manager->doing_ajax() && 'customize_save' === $this->get_post_value( 'action' ) ) {
    141                         $is_ajax_customize_save = check_ajax_referer( 'save-customize_' . $this->manager->get_stylesheet(), 'nonce', false );
    142                 }
    143 
    144                 $is_valid_request = ( $is_ajax_widget_update || $is_customize_preview || $is_ajax_customize_save );
    145                 if ( ! $is_valid_request ) {
    146                         return;
    147                 }
    148 
    149                 // Input from Customizer preview.
    150                 if ( isset( $_POST['customized'] ) ) {
    151                         $this->_customized = json_decode( $this->get_post_value( 'customized' ), true );
    152                 } else { // Input from ajax widget update request.
    153                         $this->_customized = array();
    154                         $id_base = $this->get_post_value( 'id_base' );
    155                         $widget_number = $this->get_post_value( 'widget_number', false );
    156                         $option_name = 'widget_' . $id_base;
    157                         $this->_customized[ $option_name ] = array();
    158                         if ( preg_match( '/^[0-9]+$/', $widget_number ) ) {
    159                                 $option_name .= '[' . $widget_number . ']';
    160                                 $this->_customized[ $option_name ][ $widget_number ] = array();
    161                         }
    162                 }
    163 
    164                 $function = array( $this, 'prepreview_added_sidebars_widgets' );
    165 
    166                 $hook = 'option_sidebars_widgets';
    167                 add_filter( $hook, $function );
    168                 $this->_prepreview_added_filters[] = compact( 'hook', 'function' );
    169 
    170                 $hook = 'default_option_sidebars_widgets';
    171                 add_filter( $hook, $function );
    172                 $this->_prepreview_added_filters[] = compact( 'hook', 'function' );
    173 
    174                 $function = array( $this, 'prepreview_added_widget_instance' );
    175                 foreach ( $this->_customized as $setting_id => $value ) {
    176                         if ( preg_match( '/^(widget_.+?)(?:\[(\d+)\])?$/', $setting_id, $matches ) ) {
    177                                 $option = $matches[1];
    178 
    179                                 $hook = sprintf( 'option_%s', $option );
    180                                 if ( ! has_filter( $hook, $function ) ) {
    181                                         add_filter( $hook, $function );
    182                                         $this->_prepreview_added_filters[] = compact( 'hook', 'function' );
    183                                 }
    184 
    185                                 $hook = sprintf( 'default_option_%s', $option );
    186                                 if ( ! has_filter( $hook, $function ) ) {
    187                                         add_filter( $hook, $function );
    188                                         $this->_prepreview_added_filters[] = compact( 'hook', 'function' );
    189                                 }
    190 
    191                                 /*
    192                                  * Make sure the option is registered so that the update_option()
    193                                  * won't fail due to the filters providing a default value, which
    194                                  * causes the update_option() to get confused.
    195                                  */
    196                                 add_option( $option, array() );
    197                         }
    198                 }
    199         }
    200 
    201         /**
    202          * Ensure that newly-added widgets will appear in the widgets_sidebars.
    203          *
    204          * This is necessary because the Customizer's setting preview filters
    205          * are added after the widgets_init action, which is too late for the
    206          * widgets to be set up properly.
    207          *
    208          * @since 3.9.0
    209          * @access public
    210          *
    211          * @param array $sidebars_widgets Associative array of sidebars and their widgets.
    212          * @return array Filtered array of sidebars and their widgets.
    213          */
    214         public function prepreview_added_sidebars_widgets( $sidebars_widgets ) {
    215                 foreach ( $this->_customized as $setting_id => $value ) {
    216                         if ( preg_match( '/^sidebars_widgets\[(.+?)\]$/', $setting_id, $matches ) ) {
    217                                 $sidebar_id = $matches[1];
    218                                 $sidebars_widgets[ $sidebar_id ] = $value;
    219                         }
    220                 }
    221                 return $sidebars_widgets;
    222         }
    223 
    224         /**
    225          * Ensure newly-added widgets have empty instances so they
    226          * will be recognized.
    227          *
    228          * This is necessary because the Customizer's setting preview
    229          * filters are added after the widgets_init action, which is
    230          * too late for the widgets to be set up properly.
    231          *
    232          * @since 3.9.0
    233          * @access public
    234          *
    235          * @param array|bool|mixed $value Widget instance(s), false if open was empty.
    236          * @return array|mixed Widget instance(s) with additions.
    237          */
    238         public function prepreview_added_widget_instance( $value = false ) {
    239                 if ( ! preg_match( '/^(?:default_)?option_(widget_(.+))/', current_filter(), $matches ) ) {
    240                         return $value;
    241                 }
    242                 $id_base = $matches[2];
    243 
    244                 foreach ( $this->_customized as $setting_id => $setting ) {
    245                         $parsed_setting_id = $this->parse_widget_setting_id( $setting_id );
    246                         if ( is_wp_error( $parsed_setting_id ) || $id_base !== $parsed_setting_id['id_base'] ) {
    247                                 continue;
    248                         }
    249                         $widget_number = $parsed_setting_id['number'];
    250 
    251                         if ( is_null( $widget_number ) ) {
    252                                 // Single widget.
    253                                 if ( false === $value ) {
    254                                         $value = array();
    255                                 }
    256                         } else {
    257                                 // Multi widget.
    258                                 if ( empty( $value ) ) {
    259                                         $value = array( '_multiwidget' => 1 );
    260                                 }
    261                                 if ( ! isset( $value[ $widget_number ] ) ) {
    262                                         $value[ $widget_number ] = array();
    263                                 }
    264                         }
    265                 }
    266 
    267                 return $value;
    268         }
    269 
    270         /**
    271          * Remove pre-preview filters.
    272          *
    273          * Removes filters added in setup_widget_addition_previews()
    274          * to ensure widgets are populating the options during
    275          * 'widgets_init'.
    276          *
    277          * @since 3.9.0
    278          * @access public
    279          */
    280         public function remove_prepreview_filters() {
    281                 foreach ( $this->_prepreview_added_filters as $prepreview_added_filter ) {
    282                         remove_filter( $prepreview_added_filter['hook'], $prepreview_added_filter['function'] );
    283                 }
    284                 $this->_prepreview_added_filters = array();
     182                return wp_unslash( $_POST[ $name ] );
    285183        }
    286184
     
    381279         */
    382280        public function schedule_customize_register() {
    383                 if ( is_admin() ) { // @todo for some reason, $wp_customize->is_preview() is true here?
     281                if ( is_admin() ) {
    384282                        $this->customize_register();
    385283                } else {
     
    413311                        $setting_id   = $this->get_setting_id( $widget_id );
    414312                        $setting_args = $this->get_setting_args( $setting_id );
    415 
    416                         $setting_args['sanitize_callback']    = array( $this, 'sanitize_widget_instance' );
    417                         $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
    418 
    419                         $this->manager->add_setting( $setting_id, $setting_args );
    420 
     313                        if ( ! $this->manager->get_setting( $setting_id ) ) {
     314                                $this->manager->add_setting( $setting_id, $setting_args );
     315                        }
    421316                        $new_setting_ids[] = $setting_id;
    422317                }
     
    453348                                $setting_id   = sprintf( 'sidebars_widgets[%s]', $sidebar_id );
    454349                                $setting_args = $this->get_setting_args( $setting_id );
    455 
    456                                 $setting_args['sanitize_callback']    = array( $this, 'sanitize_sidebar_widgets' );
    457                                 $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
    458 
    459                                 $this->manager->add_setting( $setting_id, $setting_args );
     350                                if ( ! $this->manager->get_setting( $setting_id ) ) {
     351                                        $this->manager->add_setting( $setting_id, $setting_args );
     352                                }
    460353                                $new_setting_ids[] = $setting_id;
    461354
     
    524417                }
    525418
    526                 /*
    527                  * We have to register these settings later than customize_preview_init
    528                  * so that other filters have had a chance to run.
    529                  */
    530                 if ( did_action( 'customize_preview_init' ) ) {
     419                if ( ! $this->manager->doing_ajax( 'customize_save' ) ) {
    531420                        foreach ( $new_setting_ids as $new_setting_id ) {
    532421                                $this->manager->get_setting( $new_setting_id )->preview();
    533422                        }
    534423                }
    535                 $this->remove_prepreview_filters();
     424
     425                add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 );
    536426        }
    537427
     
    805695                        'default'    => array(),
    806696                );
     697
     698                if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) {
     699                        $args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' );
     700                        $args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' );
     701                } else if ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) {
     702                        $args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' );
     703                        $args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' );
     704                }
     705
    807706                $args = array_merge( $args, $overrides );
    808707
     
    832731         */
    833732        public function sanitize_sidebar_widgets( $widget_ids ) {
    834                 global $wp_registered_widgets;
    835 
    836                 $widget_ids           = array_map( 'strval', (array) $widget_ids );
     733                $widget_ids = array_map( 'strval', (array) $widget_ids );
    837734                $sanitized_widget_ids = array();
    838 
    839735                foreach ( $widget_ids as $widget_id ) {
    840                         if ( array_key_exists( $widget_id, $wp_registered_widgets ) ) {
    841                                 $sanitized_widget_ids[] = $widget_id;
    842                         }
     736                        $sanitized_widget_ids[] = preg_replace( '/[^a-z0-9_\-]/', '', $widget_id );
    843737                }
    844738                return $sanitized_widget_ids;
     
    975869         */
    976870        public function customize_preview_init() {
    977                 add_filter( 'sidebars_widgets',   array( $this, 'preview_sidebars_widgets' ), 1 );
    978871                add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) );
    979872                add_action( 'wp_print_styles',    array( $this, 'print_preview_css' ), 1 );
     
    13161209                // Clean up any input vars that were manually added
    13171210                foreach ( $added_input_vars as $key ) {
    1318                         unset( $_POST[$key] );
    1319                         unset( $_REQUEST[$key] );
     1211                        unset( $_POST[ $key ] );
     1212                        unset( $_REQUEST[ $key ] );
    13201213                }
    13211214
     
    13341227                }
    13351228
     1229                // Obtain the widget instance.
     1230                $option = $this->get_captured_option( $option_name );
     1231                if ( null !== $parsed_id['number'] ) {
     1232                        $instance = $option[ $parsed_id['number'] ];
     1233                } else {
     1234                        $instance = $option;
     1235                }
     1236
     1237                /*
     1238                 * Override the incoming $_POST['customized'] for a newly-created widget's
     1239                 * setting with the new $instance so that the preview filter currently
     1240                 * in place from WP_Customize_Setting::preview() will use this value
     1241                 * instead of the default widget instance value (an empty array).
     1242                 */
     1243                $setting_id = $this->get_setting_id( $widget_id );
     1244                $this->manager->set_post_value( $setting_id, $instance );
     1245
    13361246                // Obtain the widget control with the updated instance in place.
    13371247                ob_start();
    1338 
    1339                 $form = $wp_registered_widget_controls[$widget_id];
     1248                $form = $wp_registered_widget_controls[ $widget_id ];
    13401249                if ( $form ) {
    13411250                        call_user_func_array( $form['callback'], $form['params'] );
    13421251                }
    1343 
    13441252                $form = ob_get_clean();
    1345 
    1346                 // Obtain the widget instance.
    1347                 $option = get_option( $option_name );
    1348 
    1349                 if ( null !== $parsed_id['number'] ) {
    1350                         $instance = $option[$parsed_id['number']];
    1351                 } else {
    1352                         $instance = $option;
    1353                 }
    13541253
    13551254                $this->stop_capturing_option_updates();
     
    13841283                }
    13851284
    1386                 if ( ! isset( $_POST['widget-id'] ) ) {
    1387                         wp_send_json_error();
     1285                if ( empty( $_POST['widget-id'] ) ) {
     1286                        wp_send_json_error( 'missing_widget-id' );
    13881287                }
    13891288
     
    13991298                $widget_id = $this->get_post_value( 'widget-id' );
    14001299                $parsed_id = $this->parse_widget_id( $widget_id );
    1401                 $id_base   = $parsed_id['id_base'];
    1402 
    1403                 if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) {
    1404                         wp_send_json_error();
     1300                $id_base = $parsed_id['id_base'];
     1301
     1302                $is_updating_widget_template = (
     1303                        isset( $_POST[ 'widget-' . $id_base ] )
     1304                        &&
     1305                        is_array( $_POST[ 'widget-' . $id_base ] )
     1306                        &&
     1307                        preg_match( '/__i__|%i%/', key( $_POST[ 'widget-' . $id_base ] ) )
     1308                );
     1309                if ( $is_updating_widget_template ) {
     1310                        wp_send_json_error( 'template_widget_not_updatable' );
    14051311                }
    14061312
    14071313                $updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form}
    14081314                if ( is_wp_error( $updated_widget ) ) {
    1409                         wp_send_json_error();
     1315                        wp_send_json_error( $updated_widget->get_error_message() );
    14101316                }
    14111317
     
    14641370
    14651371        /**
     1372         * Get the option that was captured from being saved.
     1373         *
     1374         * @since 4.2.0
     1375         * @access protected
     1376         *
     1377         * @param string $option_name Option name.
     1378         * @param mixed  $default     Optional. Default value to return if the option does not exist.
     1379         * @return mixed Value set for the option.
     1380         */
     1381        protected function get_captured_option( $option_name, $default = false ) {
     1382                if ( array_key_exists( $option_name, $this->_captured_options ) ) {
     1383                        $value = $this->_captured_options[ $option_name ];
     1384                } else {
     1385                        $value = $default;
     1386                }
     1387                return $value;
     1388        }
     1389
     1390        /**
    14661391         * Get the number of captured widget option updates.
    14671392         *
     
    14971422         * @access public
    14981423         *
    1499          * @param mixed $new_value
    1500          * @param string $option_name
    1501          * @param mixed $old_value
    1502          * @return mixed
     1424         * @param mixed  $new_value   The new option value.
     1425         * @param string $option_name Name of the option.
     1426         * @param mixed  $old_value   The old option value.
     1427         * @return mixed Filtered option value.
    15031428         */
    15041429        public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) {
     
    15071432                }
    15081433
    1509                 if ( ! isset( $this->_captured_options[$option_name] ) ) {
     1434                if ( ! isset( $this->_captured_options[ $option_name ] ) ) {
    15101435                        add_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) );
    15111436                }
    15121437
    1513                 $this->_captured_options[$option_name] = $new_value;
     1438                $this->_captured_options[ $option_name ] = $new_value;
    15141439
    15151440                return $old_value;
     
    15221447         * @access public
    15231448         *
    1524          * @param mixed $value Option
    1525          * @return mixed
     1449         * @param mixed $value Value to return instead of the option value.
     1450         * @return mixed Filtered option value.
    15261451         */
    15271452        public function capture_filter_pre_get_option( $value ) {
    15281453                $option_name = preg_replace( '/^pre_option_/', '', current_filter() );
    15291454
    1530                 if ( isset( $this->_captured_options[$option_name] ) ) {
    1531                         $value = $this->_captured_options[$option_name];
     1455                if ( isset( $this->_captured_options[ $option_name ] ) ) {
     1456                        $value = $this->_captured_options[ $option_name ];
    15321457
    15331458                        /** This filter is documented in wp-includes/option.php */
     
    15581483                $this->_is_capturing_option_updates = false;
    15591484        }
     1485
     1486        /**
     1487         * @since 3.9.0
     1488         * @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
     1489         */
     1490        public function setup_widget_addition_previews() {
     1491                _deprecated_function( __METHOD__, '4.2.0' );
     1492        }
     1493
     1494        /**
     1495         * @since 3.9.0
     1496         * @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
     1497         */
     1498        public function prepreview_added_sidebars_widgets() {
     1499                _deprecated_function( __METHOD__, '4.2.0' );
     1500        }
     1501
     1502        /**
     1503         * @since 3.9.0
     1504         * @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
     1505         */
     1506        public function prepreview_added_widget_instance() {
     1507                _deprecated_function( __METHOD__, '4.2.0' );
     1508        }
     1509
     1510        /**
     1511         * @since 3.9.0
     1512         * @deprecated 4.2.0 Deprecated in favor of customize_dynamic_setting_args filter.
     1513         */
     1514        public function remove_prepreview_filters() {
     1515                _deprecated_function( __METHOD__, '4.2.0' );
     1516        }
    15601517}
  • trunk/tests/phpunit/tests/customize/manager.php

    r31329 r31370  
    3333
    3434        /**
    35          * Test WP_Customize_Manager::unsanitized_post_values()
     35         * Test WP_Customize_Manager::doing_ajax().
     36         *
     37         * @group ajax
     38         */
     39        function test_doing_ajax() {
     40                if ( ! defined( 'DOING_AJAX' ) ) {
     41                        define( 'DOING_AJAX', true );
     42                }
     43
     44                $manager = $this->instantiate();
     45                $this->assertTrue( $manager->doing_ajax() );
     46
     47                $_REQUEST['action'] = 'customize_save';
     48                $this->assertTrue( $manager->doing_ajax( 'customize_save' ) );
     49                $this->assertFalse( $manager->doing_ajax( 'update-widget' ) );
     50        }
     51
     52        /**
     53         * Test ! WP_Customize_Manager::doing_ajax().
     54         */
     55        function test_not_doing_ajax() {
     56                if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
     57                        $this->markTestSkipped( 'Cannot test when DOING_AJAX' );
     58                }
     59
     60                $manager = $this->instantiate();
     61                $this->assertFalse( $manager->doing_ajax() );
     62        }
     63
     64        /**
     65         * Test WP_Customize_Manager::unsanitized_post_values().
    3666         *
    3767         * @ticket 30988
     
    5080
    5181        /**
    52          * Test the WP_Customize_Manager::post_value() method
     82         * Test the WP_Customize_Manager::post_value() method.
    5383         *
    5484         * @ticket 30988
     
    72102        }
    73103
     104        /**
     105         * Test the WP_Customize_Manager::add_dynamic_settings() method.
     106         *
     107         * @ticket 30936
     108         */
     109        function test_add_dynamic_settings() {
     110                $manager = $this->instantiate();
     111                $setting_ids = array( 'foo', 'bar' );
     112                $manager->add_setting( 'foo', array( 'default' => 'foo_default' ) );
     113                $this->assertEmpty( $manager->get_setting( 'bar' ), 'Expected there to not be a bar setting up front.' );
     114                $manager->add_dynamic_settings( $setting_ids );
     115                $this->assertEmpty( $manager->get_setting( 'bar' ), 'Expected the bar setting to remain absent since filters not added.' );
     116
     117                $this->action_customize_register_for_dynamic_settings();
     118                $manager->add_dynamic_settings( $setting_ids );
     119                $this->assertNotEmpty( $manager->get_setting( 'bar' ), 'Expected bar setting to be created since filters were added.' );
     120                $this->assertEquals( 'foo_default', $manager->get_setting( 'foo' )->default, 'Expected static foo setting to not get overridden by dynamic setting.' );
     121                $this->assertEquals( 'dynamic_bar_default', $manager->get_setting( 'bar' )->default, 'Expected dynamic setting bar to have default providd by filter.' );
     122        }
     123
     124        /**
     125         * Test the WP_Customize_Manager::register_dynamic_settings() method.
     126         *
     127         * This is similar to test_add_dynamic_settings, except the settings are passed via $_POST['customized'].
     128         *
     129         * @ticket 30936
     130         */
     131        function test_register_dynamic_settings() {
     132                $posted_settings = array(
     133                        'foo' => 'OOF',
     134                        'bar' => 'RAB',
     135                );
     136                $_POST['customized'] = wp_slash( wp_json_encode( $posted_settings ) );
     137
     138                add_action( 'customize_register', array( $this, 'action_customize_register_for_dynamic_settings' ) );
     139
     140                $manager = $this->instantiate();
     141                $manager->add_setting( 'foo', array( 'default' => 'foo_default' ) );
     142
     143                $this->assertEmpty( $manager->get_setting( 'bar' ), 'Expected dynamic setting "bar" to not be registered.' );
     144                do_action( 'customize_register', $manager );
     145                $this->assertNotEmpty( $manager->get_setting( 'bar' ), 'Expected dynamic setting "bar" to be automatically registered after customize_register action.' );
     146                $this->assertEmpty( $manager->get_setting( 'baz' ), 'Expected unrecognized dynamic setting "baz" to remain unregistered.' );
     147        }
     148
     149        /**
     150         * In lieu of closures, callback for customize_register action added in test_register_dynamic_settings().
     151         */
     152        function action_customize_register_for_dynamic_settings() {
     153                add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_customize_dynamic_setting_args_for_test_dynamic_settings' ), 10, 2 );
     154                add_filter( 'customize_dynamic_setting_class', array( $this, 'filter_customize_dynamic_setting_class_for_test_dynamic_settings' ), 10, 3 );
     155        }
     156
     157        /**
     158         * In lieu of closures, callback for customize_dynamic_setting_args filter added for test_register_dynamic_settings().
     159         */
     160        function filter_customize_dynamic_setting_args_for_test_dynamic_settings( $setting_args, $setting_id ) {
     161                $this->assertEquals( false, $setting_args, 'Expected $setting_args to be false by default.' );
     162                $this->assertInternalType( 'string', $setting_id );
     163                if ( in_array( $setting_id, array( 'foo', 'bar' ) ) ) {
     164                        $setting_args = array( 'default' => "dynamic_{$setting_id}_default" );
     165                }
     166                return $setting_args;
     167        }
     168
     169        /**
     170         * In lieu of closures, callback for customize_dynamic_setting_class filter added for test_register_dynamic_settings().
     171         */
     172        function filter_customize_dynamic_setting_class_for_test_dynamic_settings( $setting_class, $setting_id, $setting_args ) {
     173                $this->assertEquals( 'WP_Customize_Setting', $setting_class );
     174                $this->assertInternalType( 'string', $setting_id );
     175                $this->assertInternalType( 'array', $setting_args );
     176                return $setting_class;
     177        }
    74178}
    75 
Note: See TracChangeset for help on using the changeset viewer.