From 7eaf5ac605c3145b22e7f9adc26dffb7b8fce49b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 4 Apr 2022 09:00:58 +0100 Subject: [PATCH] Backport the global styles variations changes from Gutenberg --- .../class-wp-theme-json-resolver.php | 28 ++++++++ ...class-wp-rest-global-styles-controller.php | 67 +++++++++++++++++++ .../themedir1/theme1/styles/variation.json | 23 +++++++ .../rest-global-styles-controller.php | 42 ++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 tests/phpunit/data/themedir1/theme1/styles/variation.json diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index d7828d727d22a..ba066c8271eb5 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -449,4 +449,32 @@ public static function clean_cached_data() { static::$i18n_schema = null; } + /** + * Returns the style variations defined by the theme. + * + * @since 6.0.0 + * + * @return array + */ + public static function get_style_variations() { + $variations = array(); + $base_directory = get_stylesheet_directory() . '/styles'; + if ( is_dir( $base_directory ) ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); + $nested_html_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); + ksort( $nested_html_files ); + foreach ( $nested_html_files as $path => $file ) { + $decoded_file = wp_json_file_decode( $path, array( 'associative' => true ) ); + if ( is_array( $decoded_file ) ) { + $translated = static::translate( $decoded_file, wp_get_theme()->get( 'TextDomain' ) ); + $variation = ( new WP_Theme_JSON_Gutenberg( $translated ) )->get_raw_data(); + if ( empty( $variation['title'] ) ) { + $variation['title'] = basename( $path, '.json' ); + } + $variations[] = $variation; + } + } + } + return $variations; + } } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php index e6771ff851cf3..e621fd36bb1bf 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php @@ -65,6 +65,24 @@ public function register_routes() { ) ); + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/themes/(?P[\/\s%\w\.\(\)\[\]\@_\-]+)/variations', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_theme_items' ), + 'permission_callback' => array( $this, 'get_theme_items_permissions_check' ), + 'args' => array( + 'stylesheet' => array( + 'description' => __( 'The theme identifier', 'gutenberg' ), + 'type' => 'string', + ), + ), + ), + ) + ); + // Lists/updates a single global style variation based on the given id. register_rest_route( $this->namespace, @@ -585,4 +603,53 @@ public function get_theme_item( $request ) { return $response; } + + /** + * Checks if a given request has access to read a single theme global styles config. + * + * @since 6.0.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. + */ + public function get_theme_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + // Verify if the current user has edit_theme_options capability. + // This capability is required to edit/view/delete templates. + if ( ! current_user_can( 'edit_theme_options' ) ) { + return new WP_Error( + 'rest_cannot_manage_global_styles', + __( 'Sorry, you are not allowed to access the global styles on this site.' ), + array( + 'status' => rest_authorization_required_code(), + ) + ); + } + + return true; + } + + /** + * Returns the given theme global styles variations. + * + * @since 6.0.0 + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function get_theme_items( $request ) { + if ( wp_get_theme()->get_stylesheet() !== $request['stylesheet'] ) { + // This endpoint only supports the active theme for now. + return new WP_Error( + 'rest_theme_not_found', + __( 'Theme not found.' ), + array( 'status' => 404 ) + ); + } + + $variations = WP_Theme_JSON_Resolver::get_style_variations(); + $response = rest_ensure_response( $variations ); + + return $response; + } } diff --git a/tests/phpunit/data/themedir1/theme1/styles/variation.json b/tests/phpunit/data/themedir1/theme1/styles/variation.json new file mode 100644 index 0000000000000..ad3affb1152d6 --- /dev/null +++ b/tests/phpunit/data/themedir1/theme1/styles/variation.json @@ -0,0 +1,23 @@ +{ + "version": 2, + "settings": { + "color": { + "palette": [ + { + "slug": "foreground", + "color": "#3F67C6", + "name": "Foreground" + } + ] + } + }, + "styles": { + "blocks": { + "core/post-title": { + "typography": { + "fontWeight": "700" + } + } + } + } +} \ No newline at end of file diff --git a/tests/phpunit/tests/rest-api/rest-global-styles-controller.php b/tests/phpunit/tests/rest-api/rest-global-styles-controller.php index 60023e7c01f27..164d0bc1e15e0 100644 --- a/tests/phpunit/tests/rest-api/rest-global-styles-controller.php +++ b/tests/phpunit/tests/rest-api/rest-global-styles-controller.php @@ -117,6 +117,11 @@ public function test_register_routes() { $routes['/wp/v2/global-styles/themes/(?P[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'], 'Theme global styles route does not have exactly one element' ); + $this->assertArrayHasKey( + '/wp/v2/global-styles/themes/(?P[\/\s%\w\.\(\)\[\]\@_\-]+)/variations', + $routes, + 'Theme global styles variations route does not exist' + ); } public function test_context_param() { @@ -455,4 +460,41 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'settings', $properties, 'Schema properties array does not have "settings" key' ); $this->assertArrayHasKey( 'title', $properties, 'Schema properties array does not have "title" key' ); } + + + public function test_get_theme_items() { + wp_set_current_user( self::$admin_id ); + switch_theme( 'theme1' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/theme1/variations' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $expected = array( + array( + 'version' => 2, + 'settings' => array( + 'color' => array( + 'palette' => array( + 'theme' => array( + array( + 'slug' => 'foreground', + 'color' => '#3F67C6', + 'name' => 'Foreground', + ), + ), + ), + ), + ), + 'styles' => array( + 'blocks' => array( + 'core/post-title' => array( + 'typography' => array( + 'fontWeight' => '700', + ), + ), + ), + ), + ), + ); + $this->assertSameSetsWithIndex( $data, $expected ); + } }