diff --git a/src/wp-admin/js/customize-widgets.js b/src/wp-admin/js/customize-widgets.js index 48ba335138..f2782b98e4 100644 --- a/src/wp-admin/js/customize-widgets.js +++ b/src/wp-admin/js/customize-widgets.js @@ -942,6 +942,7 @@ params.action = 'update-widget'; params.wp_customize = 'on'; params.nonce = api.Widgets.data.nonce; + params.theme = api.settings.theme.stylesheet; data = $.param( params ); $inputs = this._getInputs( $widgetContent ); @@ -1614,7 +1615,7 @@ widgetNumber = widget.get( 'multi_number' ); } - controlHtml = $( '#widget-tpl-' + widget.get( 'id' ) ).html(); + controlHtml = $.trim( $( '#widget-tpl-' + widget.get( 'id' ) ).html() ); if ( widget.get( 'is_multi' ) ) { controlHtml = controlHtml.replace( /<[^<>]+>/g, function( m ) { return m.replace( /__i__|%i%/g, widgetNumber ); diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 18a1d761c8..8ed4e699a5 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -575,6 +575,7 @@ final class WP_Customize_Manager { // to operate properly. $this->stop_previewing_theme(); switch_theme( $this->get_stylesheet() ); + update_option( 'theme_switched_via_customizer', true ); $this->start_previewing_theme(); } diff --git a/src/wp-includes/class-wp-customize-widgets.php b/src/wp-includes/class-wp-customize-widgets.php index 5416590b9a..049ceddbfb 100644 --- a/src/wp-includes/class-wp-customize-widgets.php +++ b/src/wp-includes/class-wp-customize-widgets.php @@ -60,6 +60,13 @@ final class WP_Customize_Widgets { */ protected $rendered_widgets = array(); + /** + * @since 3.9.0 + * @access protected + * @var array + */ + protected $old_sidebars_widgets = array(); + /** * Initial loader. * @@ -72,6 +79,7 @@ final class WP_Customize_Widgets { $this->manager = $manager; add_action( 'after_setup_theme', array( $this, 'setup_widget_addition_previews' ) ); + add_action( 'wp_loaded', array( $this, 'override_sidebars_widgets_for_theme_switch' ) ); add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) ); add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 ); add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); @@ -115,7 +123,6 @@ final class WP_Customize_Widgets { * @since 3.9.0 * * @access public - * @global WP_Customize_Manager $wp_customize Customizer instance. */ public function setup_widget_addition_previews() { $is_customize_preview = false; @@ -125,12 +132,12 @@ final class WP_Customize_Widgets { } $is_ajax_widget_update = false; - if ( defined( 'DOING_AJAX' ) && DOING_AJAX && 'update-widget' === $this->get_post_value( 'action' ) ) { + if ( $this->manager->doing_ajax() && 'update-widget' === $this->get_post_value( 'action' ) ) { $is_ajax_widget_update = check_ajax_referer( 'update-widget', 'nonce', false ); } $is_ajax_customize_save = false; - if ( defined( 'DOING_AJAX' ) && DOING_AJAX && 'customize_save' === $this->get_post_value( 'action' ) ) { + if ( $this->manager->doing_ajax() && 'customize_save' === $this->get_post_value( 'action' ) ) { $is_ajax_customize_save = check_ajax_referer( 'save-customize_' . $this->manager->get_stylesheet(), 'nonce', false ); } @@ -278,6 +285,72 @@ final class WP_Customize_Widgets { $this->_prepreview_added_filters = array(); } + /** + * Override sidebars_widgets for theme switch. + * + * When switching a theme via the customizer, supply any previously-configured + * sidebars_widgets from the target theme as the initial sidebars_widgets + * setting. Also store the old theme's existing settings so that they can + * be passed along for storing in the sidebars_widgets theme_mod when the + * theme gets switched. + * + * @since 3.9.0 + * @access public + */ + public function override_sidebars_widgets_for_theme_switch() { + global $sidebars_widgets; + + if ( $this->manager->doing_ajax() || $this->manager->is_theme_active() ) { + return; + } + + $this->old_sidebars_widgets = wp_get_sidebars_widgets(); + add_filter( 'customize_value_old_sidebars_widgets_data', array( $this, 'filter_customize_value_old_sidebars_widgets_data' ) ); + + // retrieve_widgets() looks at the global $sidebars_widgets + $sidebars_widgets = $this->old_sidebars_widgets; + $sidebars_widgets = retrieve_widgets( 'customize' ); + add_filter( 'option_sidebars_widgets', array( $this, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 ); + } + + /** + * Filter old_sidebars_widgets_data customizer setting. + * + * When switching themes, filter the Customizer setting + * old_sidebars_widgets_data to supply initial $sidebars_widgets before they + * were overridden by retrieve_widgets(). The value for + * old_sidebars_widgets_data gets set in the old theme's sidebars_widgets + * theme_mod. + * + * @see WP_Customize_Widgets::handle_theme_switch() + * @since 3.9.0 + * @access public + * + * @param array $sidebars_widgets + */ + public function filter_customize_value_old_sidebars_widgets_data( $old_sidebars_widgets ) { + return $this->old_sidebars_widgets; + } + + /** + * Filter sidebars_widgets option for theme switch. + * + * When switching themes, the retrieve_widgets() function is run when the + * Customizer initializes, and then the new sidebars_widgets here get + * supplied as the default value for the sidebars_widgets option. + * + * @see WP_Customize_Widgets::handle_theme_switch() + * @since 3.9.0 + * @access public + * + * @param array $sidebars_widgets + */ + public function filter_option_sidebars_widgets_for_theme_switch( $sidebars_widgets ) { + $sidebars_widgets = $GLOBALS['sidebars_widgets']; + $sidebars_widgets['array_version'] = 3; + return $sidebars_widgets; + } + /** * Make sure all widgets get loaded into the Customizer. * @@ -349,6 +422,18 @@ final class WP_Customize_Widgets { $new_setting_ids[] = $setting_id; } + /* + * Add a setting which will be supplied for the theme's sidebars_widgets + * theme_mod when the the theme is switched. + */ + if ( ! $this->manager->is_theme_active() ) { + $setting_id = 'old_sidebars_widgets_data'; + $setting_args = $this->get_setting_args( $setting_id, array( + 'type' => 'global_variable', + ) ); + $this->manager->add_setting( $setting_id, $setting_args ); + } + foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) { if ( empty( $sidebar_widget_ids ) ) { $sidebar_widget_ids = array(); diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index f66fd95215..00b3c39d1f 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -752,10 +752,18 @@ function preview_theme_ob_filter_callback( $matches ) { * @param string $stylesheet Stylesheet name */ function switch_theme( $stylesheet ) { - global $wp_theme_directories, $sidebars_widgets; + global $wp_theme_directories, $wp_customize, $sidebars_widgets; - if ( is_array( $sidebars_widgets ) ) - set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $sidebars_widgets ) ); + $_sidebars_widgets = null; + if ( 'wp_ajax_customize_save' === current_action() ) { + $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); + } elseif ( is_array( $sidebars_widgets ) ) { + $_sidebars_widgets = $sidebars_widgets; + } + + if ( is_array( $_sidebars_widgets ) ) { + set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); + } $old_theme = wp_get_theme(); $new_theme = wp_get_theme( $stylesheet ); @@ -782,9 +790,19 @@ function switch_theme( $stylesheet ) { update_option( 'current_theme', $new_name ); + // Migrate from the old mods_{name} option to theme_mods_{slug}. if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) { $default_theme_mods = (array) get_option( 'mods_' . $new_name ); add_option( "theme_mods_$stylesheet", $default_theme_mods ); + } else { + /* + * Since retrieve_widgets() is called when initializing the customizer theme, + * we need to to remove the theme mods to avoid overwriting changes made via + * the widget customizer when accessing wp-admin/widgets.php. + */ + if ( 'wp_ajax_customize_save' === current_action() ) { + remove_theme_mod( 'sidebars_widgets' ); + } } update_option( 'theme_switched', $old_theme->get_stylesheet() ); @@ -1791,6 +1809,12 @@ function check_theme_switched() { if ( $stylesheet = get_option( 'theme_switched' ) ) { $old_theme = wp_get_theme( $stylesheet ); + // Prevent retrieve_widgets() from running since Customizer already called it up front + if ( get_option( 'theme_switched_via_customizer' ) ) { + remove_action( 'after_switch_theme', '_wp_sidebars_changed' ); + update_option( 'theme_switched_via_customizer', false ); + } + if ( $old_theme->exists() ) { /** * Fires on the first WP load after a theme switch if the old theme still exists. @@ -1918,4 +1942,4 @@ function wp_customize_support_script() { }()); </script> <?php -} \ No newline at end of file +} diff --git a/src/wp-includes/widgets.php b/src/wp-includes/widgets.php index b911fdebf4..4905bbdb07 100644 --- a/src/wp-includes/widgets.php +++ b/src/wp-includes/widgets.php @@ -1401,8 +1401,16 @@ function _wp_sidebars_changed() { retrieve_widgets(true); } -// look for "lost" widgets, this has to run at least on each theme change -function retrieve_widgets($theme_changed = false) { +/** + * Look for "lost" widgets, this has to run at least on each theme change. + * + * @since 2.8.0 + * + * @param mixed $theme_changed Whether the theme was changed as a boolean. A value + * of 'customize' defers updates for the customizer. + * @return array + */ +function retrieve_widgets( $theme_changed = false ) { global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets; $registered_sidebar_keys = array_keys( $wp_registered_sidebars ); @@ -1412,7 +1420,10 @@ function retrieve_widgets($theme_changed = false) { if ( is_array( $old_sidebars_widgets ) ) { // time() that sidebars were stored is in $old_sidebars_widgets['time'] $_sidebars_widgets = $old_sidebars_widgets['data']; - remove_theme_mod( 'sidebars_widgets' ); + + if ( 'customize' === $theme_changed ) { + remove_theme_mod( 'sidebars_widgets' ); + } foreach ( $_sidebars_widgets as $sidebar => $widgets ) { if ( 'wp_inactive_widgets' == $sidebar || 'orphaned_widgets' == substr( $sidebar, 0, 16 ) ) @@ -1495,7 +1506,9 @@ function retrieve_widgets($theme_changed = false) { } $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']); - wp_set_sidebars_widgets($sidebars_widgets); + if ( 'customize' === $theme_changed ) { + wp_set_sidebars_widgets( $sidebars_widgets ); + } return $sidebars_widgets; }