3 * WordPress Customize Setting classes
6 * @subpackage Customize
11 * Customize Setting class.
13 * Handles saving and sanitizing of settings.
17 * @see WP_Customize_Manager
19 class WP_Customize_Setting {
22 * @var WP_Customize_Manager
36 public $type = 'theme_mod';
39 * Capability required to edit this setting.
43 public $capability = 'edit_theme_options';
46 * Feature a theme is required to support to enable this setting.
51 public $theme_supports = '';
53 public $transport = 'refresh';
56 * Server-side sanitization callback for the setting's value.
60 public $sanitize_callback = '';
61 public $sanitize_js_callback = '';
64 * Whether or not the setting is initially dirty when created.
66 * This is used to ensure that a setting will be sent from the pane to the
67 * preview when loading the Customizer. Normally a setting only is synced to
68 * the preview if it has been changed. This allows the setting to be sent
75 public $dirty = false;
77 protected $id_data = array();
82 * Any supplied $args override class property defaults.
86 * @param WP_Customize_Manager $manager
87 * @param string $id An specific ID of the setting. Can be a
88 * theme mod or option name.
89 * @param array $args Setting arguments.
91 public function __construct( $manager, $id, $args = array() ) {
92 $keys = array_keys( get_object_vars( $this ) );
93 foreach ( $keys as $key ) {
94 if ( isset( $args[ $key ] ) )
95 $this->$key = $args[ $key ];
98 $this->manager = $manager;
101 // Parse the ID for array keys.
102 $this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
103 $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] );
106 $this->id = $this->id_data[ 'base' ];
107 if ( ! empty( $this->id_data[ 'keys' ] ) )
108 $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']';
110 if ( $this->sanitize_callback )
111 add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 );
113 if ( $this->sanitize_js_callback )
114 add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 );
118 * The ID for the current blog when the preview() method was called.
124 protected $_previewed_blog_id;
127 * Return true if the current blog is not the same as the previewed blog.
132 * @return bool|null Returns null if preview() has not been called yet.
134 public function is_current_blog_previewed() {
135 if ( ! isset( $this->_previewed_blog_id ) ) {
138 return ( get_current_blog_id() === $this->_previewed_blog_id );
142 * Original non-previewed value stored by the preview method.
144 * @see WP_Customize_Setting::preview()
148 protected $_original_value;
151 * Handle previewing the setting.
155 public function preview() {
156 if ( ! isset( $this->_original_value ) ) {
157 $this->_original_value = $this->value();
159 if ( ! isset( $this->_previewed_blog_id ) ) {
160 $this->_previewed_blog_id = get_current_blog_id();
163 switch( $this->type ) {
165 add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
168 if ( empty( $this->id_data[ 'keys' ] ) )
169 add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
171 add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
172 add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
178 * Fires when the {@see WP_Customize_Setting::preview()} method is called for settings
179 * not handled as theme_mods or options.
181 * The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
185 * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance.
187 do_action( "customize_preview_{$this->id}", $this );
190 * Fires when the {@see WP_Customize_Setting::preview()} method is called for settings
191 * not handled as theme_mods or options.
193 * The dynamic portion of the hook name, `$this->type`, refers to the setting type.
197 * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance.
199 do_action( "customize_preview_{$this->type}", $this );
204 * Callback function to filter the theme mods and options.
206 * If switch_to_blog() was called after the preview() method, and the current
207 * blog is now not the same blog, then this method does a no-op and returns
208 * the original value.
211 * @uses WP_Customize_Setting::multidimensional_replace()
213 * @param mixed $original Old value.
214 * @return mixed New or old value.
216 public function _preview_filter( $original ) {
217 if ( ! $this->is_current_blog_previewed() ) {
221 $undefined = new stdClass(); // symbol hack
222 $post_value = $this->post_value( $undefined );
223 if ( $undefined === $post_value ) {
224 $value = $this->_original_value;
226 $value = $post_value;
229 return $this->multidimensional_replace( $original, $this->id_data['keys'], $value );
233 * Check user capabilities and theme supports, and then save
234 * the value of the setting.
238 * @return false|null False if cap check fails or value isn't set.
240 final public function save() {
241 $value = $this->post_value();
243 if ( ! $this->check_capabilities() || ! isset( $value ) )
247 * Fires when the WP_Customize_Setting::save() method is called.
249 * The dynamic portion of the hook name, `$this->id_data['base']` refers to
250 * the base slug of the setting name.
254 * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance.
256 do_action( 'customize_save_' . $this->id_data[ 'base' ], $this );
258 $this->update( $value );
262 * Fetch and sanitize the $_POST value for the setting.
266 * @param mixed $default A default value which is used as a fallback. Default is null.
267 * @return mixed The default value on failure, otherwise the sanitized value.
269 final public function post_value( $default = null ) {
270 return $this->manager->post_value( $this, $default );
278 * @param mixed $value The value to sanitize.
279 * @return mixed Null if an input isn't valid, otherwise the sanitized value.
281 public function sanitize( $value ) {
282 $value = wp_unslash( $value );
285 * Filter a Customize setting value in un-slashed form.
289 * @param mixed $value Value of the setting.
290 * @param WP_Customize_Setting $this WP_Customize_Setting instance.
292 return apply_filters( "customize_sanitize_{$this->id}", $value, $this );
296 * Save the value of the setting, using the related API.
300 * @param mixed $value The value to update.
301 * @return mixed The result of saving the value.
303 protected function update( $value ) {
304 switch( $this->type ) {
306 return $this->_update_theme_mod( $value );
309 return $this->_update_option( $value );
314 * Fires when the {@see WP_Customize_Setting::update()} method is called for settings
315 * not handled as theme_mods or options.
317 * The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
321 * @param mixed $value Value of the setting.
322 * @param WP_Customize_Setting $this WP_Customize_Setting instance.
324 return do_action( 'customize_update_' . $this->type, $value, $this );
329 * Update the theme mod from the value of the parameter.
333 * @param mixed $value The value to update.
334 * @return mixed The result of saving the value.
336 protected function _update_theme_mod( $value ) {
337 // Handle non-array theme mod.
338 if ( empty( $this->id_data[ 'keys' ] ) )
339 return set_theme_mod( $this->id_data[ 'base' ], $value );
341 // Handle array-based theme mod.
342 $mods = get_theme_mod( $this->id_data[ 'base' ] );
343 $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value );
344 if ( isset( $mods ) )
345 return set_theme_mod( $this->id_data[ 'base' ], $mods );
349 * Update the option from the value of the setting.
353 * @param mixed $value The value to update.
354 * @return bool|null The result of saving the value.
356 protected function _update_option( $value ) {
357 // Handle non-array option.
358 if ( empty( $this->id_data[ 'keys' ] ) )
359 return update_option( $this->id_data[ 'base' ], $value );
361 // Handle array-based options.
362 $options = get_option( $this->id_data[ 'base' ] );
363 $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value );
364 if ( isset( $options ) )
365 return update_option( $this->id_data[ 'base' ], $options );
369 * Fetch the value of the setting.
373 * @return mixed The value.
375 public function value() {
376 // Get the callback that corresponds to the setting type.
377 switch( $this->type ) {
379 $function = 'get_theme_mod';
382 $function = 'get_option';
387 * Filter a Customize setting value not handled as a theme_mod or option.
389 * The dynamic portion of the hook name, `$this->id_date['base']`, refers to
390 * the base slug of the setting name.
392 * For settings handled as theme_mods or options, see those corresponding
393 * functions for available hooks.
397 * @param mixed $default The setting default value. Default empty.
399 return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default );
402 // Handle non-array value
403 if ( empty( $this->id_data[ 'keys' ] ) )
404 return $function( $this->id_data[ 'base' ], $this->default );
406 // Handle array-based value
407 $values = $function( $this->id_data[ 'base' ] );
408 return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default );
412 * Sanitize the setting's value for use in JavaScript.
416 * @return mixed The requested escaped value.
418 public function js_value() {
421 * Filter a Customize setting value for use in JavaScript.
423 * The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
427 * @param mixed $value The setting value.
428 * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance.
430 $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this );
432 if ( is_string( $value ) )
433 return html_entity_decode( $value, ENT_QUOTES, 'UTF-8');
439 * Validate user capabilities whether the theme supports the setting.
443 * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
445 final public function check_capabilities() {
446 if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
449 if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) )
456 * Multidimensional helper function.
462 * @param bool $create Default is false.
463 * @return null|array Keys are 'root', 'node', and 'key'.
465 final protected function multidimensional( &$root, $keys, $create = false ) {
466 if ( $create && empty( $root ) )
469 if ( ! isset( $root ) || empty( $keys ) )
472 $last = array_pop( $keys );
475 foreach ( $keys as $key ) {
476 if ( $create && ! isset( $node[ $key ] ) )
477 $node[ $key ] = array();
479 if ( ! is_array( $node ) || ! isset( $node[ $key ] ) )
482 $node = &$node[ $key ];
486 if ( ! is_array( $node ) ) {
487 // account for an array overriding a string or object value
490 if ( ! isset( $node[ $last ] ) ) {
491 $node[ $last ] = array();
495 if ( ! isset( $node[ $last ] ) )
506 * Will attempt to replace a specific value in a multidimensional array.
512 * @param mixed $value The value to update.
515 final protected function multidimensional_replace( $root, $keys, $value ) {
516 if ( ! isset( $value ) )
518 elseif ( empty( $keys ) ) // If there are no keys, we're replacing the root.
521 $result = $this->multidimensional( $root, $keys, true );
523 if ( isset( $result ) )
524 $result['node'][ $result['key'] ] = $value;
530 * Will attempt to fetch a specific value from a multidimensional array.
536 * @param mixed $default A default value which is used as a fallback. Default is null.
537 * @return mixed The requested value or the default value.
539 final protected function multidimensional_get( $root, $keys, $default = null ) {
540 if ( empty( $keys ) ) // If there are no keys, test the root.
541 return isset( $root ) ? $root : $default;
543 $result = $this->multidimensional( $root, $keys );
544 return isset( $result ) ? $result['node'][ $result['key'] ] : $default;
548 * Will attempt to check if a specific value in a multidimensional array is set.
554 * @return bool True if value is set, false if not.
556 final protected function multidimensional_isset( $root, $keys ) {
557 $result = $this->multidimensional_get( $root, $keys );
558 return isset( $result );
563 * A setting that is used to filter a value, but will not save the results.
565 * Results should be properly handled using another setting or callback.
569 * @see WP_Customize_Setting
571 class WP_Customize_Filter_Setting extends WP_Customize_Setting {
576 public function update( $value ) {}
580 * A setting that is used to filter a value, but will not save the results.
582 * Results should be properly handled using another setting or callback.
586 * @see WP_Customize_Setting
588 final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting {
589 public $id = 'header_image_data';
596 public function update( $value ) {
597 global $custom_image_header;
599 // If the value doesn't exist (removed or random),
600 // use the header_image value.
602 $value = $this->manager->get_setting('header_image')->post_value();
604 if ( is_array( $value ) && isset( $value['choice'] ) )
605 $custom_image_header->set_header_image( $value['choice'] );
607 $custom_image_header->set_header_image( $value );
612 * Customizer Background Image Setting class.
616 * @see WP_Customize_Setting
618 final class WP_Customize_Background_Image_Setting extends WP_Customize_Setting {
619 public $id = 'background_image_thumb';
626 public function update( $value ) {
627 remove_theme_mod( 'background_image_thumb' );