diff --git a/wp-includes/class-wp-customize-manager.php b/wp-includes/class-wp-customize-manager.php index c3b1f32509..9f15d31f57 100644 --- a/wp-includes/class-wp-customize-manager.php +++ b/wp-includes/class-wp-customize-manager.php @@ -659,6 +659,36 @@ final class WP_Customize_Manager { public function set_post_value( $setting_id, $value ) { $this->unsanitized_post_values(); $this->_post_values[ $setting_id ] = $value; + + /** + * Announce when a specific setting's unsanitized post value has been set. + * + * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called. + * + * The dynamic portion of the hook name, `$setting_id`, refers to the setting ID. + * + * @since 4.4.0 + * + * @param mixed $value Unsanitized setting post value. + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( "customize_post_value_set_{$setting_id}", $value, $this ); + + /** + * Announce when any setting's unsanitized post value has been set. + * + * Fires when the {@see WP_Customize_Manager::set_post_value()} method is called. + * + * This is useful for WP_Customize_Setting instances to watch + * in order to update a cached previewed value. + * + * @since 4.4.0 + * + * @param string $setting_id Setting ID. + * @param mixed $value Unsanitized setting post value. + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'customize_post_value_set', $setting_id, $value, $this ); } /** diff --git a/wp-includes/class-wp-customize-setting.php b/wp-includes/class-wp-customize-setting.php index 12f76d4309..434dec7c06 100644 --- a/wp-includes/class-wp-customize-setting.php +++ b/wp-includes/class-wp-customize-setting.php @@ -81,6 +81,15 @@ class WP_Customize_Setting { */ protected $id_data = array(); + /** + * Whether or not preview() was called. + * + * @since 4.4.0 + * @access protected + * @var bool + */ + protected $is_previewed = false; + /** * Cache of multidimensional values to improve performance. * @@ -191,6 +200,8 @@ class WP_Customize_Setting { } if ( ! empty( $this->id_data['keys'] ) ) { + // Note the preview-applied flag is cleared at priority 9 to ensure it is cleared before a deferred-preview runs. + add_action( "customize_post_value_set_{$this->id}", array( $this, '_clear_aggregated_multidimensional_preview_applied_flag' ), 9 ); $this->is_multidimensional_aggregated = true; } } @@ -245,6 +256,12 @@ class WP_Customize_Setting { if ( ! isset( $this->_previewed_blog_id ) ) { $this->_previewed_blog_id = get_current_blog_id(); } + + // Prevent re-previewing an already-previewed setting. + if ( $this->is_previewed ) { + return true; + } + $id_base = $this->id_data['base']; $is_multidimensional = ! empty( $this->id_data['keys'] ); $multidimensional_filter = array( $this, '_multidimensional_preview_filter' ); @@ -273,7 +290,11 @@ class WP_Customize_Setting { $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied. } + // If the setting does not need previewing now, defer to when it has a value to preview. if ( ! $needs_preview ) { + if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) { + add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ); + } return false; } @@ -327,9 +348,28 @@ class WP_Customize_Setting { */ do_action( "customize_preview_{$this->type}", $this ); } + + $this->is_previewed = true; + return true; } + /** + * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated. + * + * This ensures that the new value will get sanitized and used the next time + * that WP_Customize_Setting::_multidimensional_preview_filter() + * is called for this setting. + * + * @since 4.4.0 + * @access private + * @see WP_Customize_Manager::set_post_value() + * @see WP_Customize_Setting::_multidimensional_preview_filter() + */ + final public function _clear_aggregated_multidimensional_preview_applied_flag() { + unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $this->id ] ); + } + /** * Callback function to filter non-multidimensional theme mods and options. * @@ -369,13 +409,13 @@ class WP_Customize_Setting { * the first setting previewed will be used to apply the values for the others. * * @since 4.4.0 - * @access public + * @access private * * @see WP_Customize_Setting::$aggregated_multidimensionals * @param mixed $original Original root value. * @return mixed New or old value. */ - public function _multidimensional_preview_filter( $original ) { + final public function _multidimensional_preview_filter( $original ) { if ( ! $this->is_current_blog_previewed() ) { return $original; } diff --git a/wp-includes/class-wp-customize-widgets.php b/wp-includes/class-wp-customize-widgets.php index 6ee69421cd..7639d5091c 100644 --- a/wp-includes/class-wp-customize-widgets.php +++ b/wp-includes/class-wp-customize-widgets.php @@ -1380,7 +1380,7 @@ final class WP_Customize_Widgets { * in place from WP_Customize_Setting::preview() will use this value * instead of the default widget instance value (an empty array). */ - $this->manager->set_post_value( $setting_id, $instance ); + $this->manager->set_post_value( $setting_id, $this->sanitize_widget_js_instance( $instance ) ); // Obtain the widget control with the updated instance in place. ob_start(); diff --git a/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php b/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php index 2fa0b5c0d2..073423ecbe 100644 --- a/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php +++ b/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php @@ -119,15 +119,6 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { */ public $original_nav_menu_term_id; - /** - * Whether or not preview() was called. - * - * @since 4.3.0 - * @access protected - * @var bool - */ - protected $is_previewed = false; - /** * Whether or not update() was called. * diff --git a/wp-includes/customize/class-wp-customize-nav-menu-setting.php b/wp-includes/customize/class-wp-customize-nav-menu-setting.php index 766099e06b..5562a8df52 100644 --- a/wp-includes/customize/class-wp-customize-nav-menu-setting.php +++ b/wp-includes/customize/class-wp-customize-nav-menu-setting.php @@ -88,15 +88,6 @@ class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { */ public $previous_term_id; - /** - * Whether or not preview() was called. - * - * @since 4.3.0 - * @access protected - * @var bool - */ - protected $is_previewed = false; - /** * Whether or not update() was called. * diff --git a/wp-includes/version.php b/wp-includes/version.php index 1f0c8c807f..e63792dbc0 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '4.4-beta4-35723'; +$wp_version = '4.4-beta4-35724'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.