From 8ff58993482988856d6df72db6932a33174169f0 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Fri, 3 Feb 2023 00:13:52 +0000 Subject: [PATCH] Widgets: Preserve classic sidebars when switching to a block theme. When switching to a block theme, classic sidebars were orphaned and their widgets remapping to the `'wp_inactive_widgets'` sidebar . This changeset preserves the sidebars and their widgets, providing a migration path to a block theme without losing the widgets. Classic sidebars are now: * Stored in a new theme mod called `'wp_classic_sidebars'`; * Restored to the `$wp_registered_sidebars` global variable when the `'widgets_init'` action fires (via a new internal function called `_wp_block_theme_register_classic_sidebars()`); * And marked as `'inactive'` when interacting with sidebars REST API endpoint. References: * [https://github.com/WordPress/gutenberg/pull/45509 Gutenberg PR 45509] which adds an option for importing widgets from sidebars into template parts. Follow-up to [50995], [6334]. Props mamaduka, audrasjb, hellofromTonya, ironprogrammer, jameskoster, joen, matveb, mukesh27, noisysocks, poena, youknowriad. Fixes #57531. git-svn-id: https://develop.svn.wordpress.org/trunk@55200 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/default-filters.php | 1 + .../class-wp-rest-sidebars-controller.php | 4 + src/wp-includes/theme.php | 8 +- src/wp-includes/widgets.php | 26 ++++++ .../rest-api/rest-sidebars-controller.php | 67 ++++++++++++++ .../wpBlockThemeRegisterClassicSidebar.php | 89 +++++++++++++++++++ 6 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/tests/widgets/wpBlockThemeRegisterClassicSidebar.php diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 09be9e576b6d2..f10dc6f280b2b 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -626,6 +626,7 @@ add_action( 'after_setup_theme', 'wp_setup_widgets_block_editor', 1 ); add_action( 'init', 'wp_widgets_init', 1 ); add_action( 'change_locale', array( 'WP_Widget_Media', 'reset_default_labels' ) ); +add_action( 'widgets_init', '_wp_block_theme_register_classic_sidebars', 1 ); // Admin Bar. // Don't remove. Wrong way to disable. diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php index 449ad6bb5b335..88a10a0a42f5b 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php @@ -339,6 +339,10 @@ public function prepare_item_for_response( $item, $request ) { $sidebar['class'] = ''; } + if ( wp_is_block_theme() ) { + $sidebar['status'] = 'inactive'; + } + $fields = $this->get_fields_for_response( $request ); if ( rest_is_field_included( 'widgets', $fields ) ) { $sidebars = wp_get_sidebars_widgets(); diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index c3a4206530079..c1623f405864b 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -733,11 +733,12 @@ function locale_stylesheet() { * @global array $wp_theme_directories * @global WP_Customize_Manager $wp_customize * @global array $sidebars_widgets + * @global array $wp_registered_sidebars * * @param string $stylesheet Stylesheet name. */ function switch_theme( $stylesheet ) { - global $wp_theme_directories, $wp_customize, $sidebars_widgets; + global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars; $requirements = validate_theme_requirements( $stylesheet ); if ( is_wp_error( $requirements ) ) { @@ -814,6 +815,11 @@ function switch_theme( $stylesheet ) { } } + // Stores classic sidebars for later use by block themes. + if ( $new_theme->is_block_theme() ) { + set_theme_mod( 'wp_classic_sidebars', $wp_registered_sidebars ); + } + update_option( 'theme_switched', $old_theme->get_stylesheet() ); /** diff --git a/src/wp-includes/widgets.php b/src/wp-includes/widgets.php index 015cb3c48a646..dc643ffe1c9ca 100644 --- a/src/wp-includes/widgets.php +++ b/src/wp-includes/widgets.php @@ -2105,3 +2105,29 @@ function wp_check_widget_editor_deps() { } } } + +/** + * Registers the previous theme's sidebars for the block themes. + * + * @since 6.2.0 + * @access private + * + * @global array $wp_registered_sidebars Registered sidebars. + */ +function _wp_block_theme_register_classic_sidebars() { + global $wp_registered_sidebars; + + if ( ! wp_is_block_theme() ) { + return; + } + + $classic_sidebars = get_theme_mod( 'wp_classic_sidebars' ); + if ( empty( $classic_sidebars ) ) { + return; + } + + // Don't use `register_sidebar` since it will enable the `widgets` support for a theme. + foreach ( $classic_sidebars as $sidebar ) { + $wp_registered_sidebars[ $sidebar['id'] ] = $sidebar; + } +} diff --git a/tests/phpunit/tests/rest-api/rest-sidebars-controller.php b/tests/phpunit/tests/rest-api/rest-sidebars-controller.php index e2745f519f098..21bbc092d1c5a 100644 --- a/tests/phpunit/tests/rest-api/rest-sidebars-controller.php +++ b/tests/phpunit/tests/rest-api/rest-sidebars-controller.php @@ -860,6 +860,73 @@ public function test_get_items_inactive_widgets() { ); } + /** + * @ticket 57531 + * @covers WP_Test_REST_Sidebars_Controller::prepare_item_for_response + */ + public function test_prepare_item_for_response_to_set_inactive_on_theme_switch() { + $request = new WP_REST_Request( 'GET', '/wp/v2/sidebars/sidebar-1' ); + + // Set up the test. + wp_widgets_init(); + $this->setup_widget( + 'widget_rss', + 1, + array( + 'title' => 'RSS test', + ) + ); + $this->setup_widget( + 'widget_text', + 1, + array( + 'text' => 'Custom text test', + ) + ); + $this->setup_sidebar( + 'sidebar-1', + array( + 'name' => 'Sidebar 1', + ), + array( 'text-1', 'rss-1' ) + ); + + // Validate the state before a theme switch. + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $data = $this->remove_links( $data ); + + $this->assertSame( 'active', $data['status'] ); + $this->assertFalse( + get_theme_mod( 'wp_classic_sidebars' ), + 'wp_classic_sidebars theme mod should not exist before switching to block theme' + ); + + switch_theme( 'block-theme' ); + wp_widgets_init(); + + // Validate the state after a theme switch. + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $data = $this->remove_links( $data ); + + $this->assertSame( + 'inactive', + $data['status'], + 'Sidebar status should have changed to inactive' + ); + $this->assertSame( + array( 'text-1', 'rss-1' ), + $data['widgets'], + 'The text and rss widgets should still in sidebar-1' + ); + $this->assertArrayHasKey( + 'sidebar-1', + get_theme_mod( 'wp_classic_sidebars' ), + 'sidebar-1 should be in "wp_classic_sidebars" theme mod' + ); + } + /** * @ticket 41683 */ diff --git a/tests/phpunit/tests/widgets/wpBlockThemeRegisterClassicSidebar.php b/tests/phpunit/tests/widgets/wpBlockThemeRegisterClassicSidebar.php new file mode 100644 index 0000000000000..f8f8c519ae639 --- /dev/null +++ b/tests/phpunit/tests/widgets/wpBlockThemeRegisterClassicSidebar.php @@ -0,0 +1,89 @@ +assertNotEmpty( $sidebar_id ); + } + + /** + * @ticket 57531 + */ + public function test_should_reregister_previous_theme_sidebar() { + global $wp_registered_sidebars; + + $sidebar_id = array_key_first( $wp_registered_sidebars ); + + switch_theme( 'block-theme' ); + unregister_sidebar( $sidebar_id ); + + // Test before. + $this->assertArrayNotHasKey( + $sidebar_id, + $wp_registered_sidebars, + 'Sidebar should not be in registered sidebars after unregister' + ); + + _wp_block_theme_register_classic_sidebars(); + + // Test after. + $this->assertArrayHasKey( + $sidebar_id, + $wp_registered_sidebars, + 'Sidebar should be in registered sidebars after invoking _wp_block_theme_register_classic_sidebars()' + ); + } + + /** + * @ticket 57531 + */ + public function test_should_bail_out_when_theme_mod_is_empty() { + global $wp_registered_sidebars; + + // Test state before invoking. + $this->assertFalse( + get_theme_mod( 'wp_classic_sidebars' ), + 'Theme mod should not be set before invoking _wp_block_theme_register_classic_sidebars()' + ); + + $before = $wp_registered_sidebars; + _wp_block_theme_register_classic_sidebars(); + + // Test state after invoking. + $this->assertSameSetsWithIndex( + $before, + $wp_registered_sidebars, + 'No change should happen after invoking _wp_block_theme_register_classic_sidebars()' + ); + } +}