From 176d5a20adc91621b974ebd0aa1d6e8cacaaa065 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 14 Dec 2022 13:05:54 -0500 Subject: [PATCH 01/15] Include parent theme dir in style variation getter. --- ...class-wp-theme-json-resolver-gutenberg.php | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 lib/experimental/class-wp-theme-json-resolver-gutenberg.php diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php new file mode 100644 index 00000000000000..5adcec54d2333b --- /dev/null +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -0,0 +1,208 @@ + true ) ) { + if ( ! empty( $deprecated ) ) { + _deprecated_argument( __METHOD__, '5.9' ); + } + + // When backporting to core, remove the instanceof Gutenberg class check, as it is only required for the Gutenberg plugin. + if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) { + $theme_json_file = static::get_file_path_from_theme( 'theme.json' ); + $wp_theme = wp_get_theme(); + if ( '' !== $theme_json_file ) { + $theme_json_data = static::read_json_file( $theme_json_file ); + $theme_json_data = static::translate( $theme_json_data, $wp_theme->get( 'TextDomain' ) ); + } else { + $theme_json_data = array(); + } + $theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data ); + + /** + * Filters the data provided by the theme for global styles & settings. + * + * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. + */ + $theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data_Gutenberg( $theme_json_data, 'theme' ) ); + $theme_json_data = $theme_json->get_data(); + static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data ); + + if ( $wp_theme->parent() ) { + // Get parent theme.json. + $parent_theme_json_file = static::get_file_path_from_theme( 'theme.json', true ); + if ( '' !== $parent_theme_json_file ) { + $parent_theme_json_data = static::read_json_file( $parent_theme_json_file ); + $parent_theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $parent_theme_json_data ); + $parent_theme = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data ); + + /* + * Merge the child theme.json into the parent theme.json. + * The child theme takes precedence over the parent. + */ + $parent_theme->merge( static::$theme ); + static::$theme = $parent_theme; + } + } + } + + if ( ! $settings['with_supports'] ) { + return static::$theme; + } + + /* + * We want the presets and settings declared in theme.json + * to override the ones declared via theme supports. + * So we take theme supports, transform it to theme.json shape + * and merge the static::$theme upon that. + */ + $theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( gutenberg_get_legacy_theme_supports_for_theme_json() ); + if ( ! wp_theme_has_theme_json() ) { + if ( ! isset( $theme_support_data['settings']['color'] ) ) { + $theme_support_data['settings']['color'] = array(); + } + + $default_palette = false; + if ( current_theme_supports( 'default-color-palette' ) ) { + $default_palette = true; + } + if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) { + // If the theme does not have any palette, we still want to show the core one. + $default_palette = true; + } + $theme_support_data['settings']['color']['defaultPalette'] = $default_palette; + + $default_gradients = false; + if ( current_theme_supports( 'default-gradient-presets' ) ) { + $default_gradients = true; + } + if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) { + // If the theme does not have any gradients, we still want to show the core ones. + $default_gradients = true; + } + $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients; + + // Classic themes without a theme.json don't support global duotone. + $theme_support_data['settings']['color']['defaultDuotone'] = false; + + // Allow themes to enable appearance tools via theme_support. + if ( current_theme_supports( 'appearance-tools' ) ) { + $theme_support_data['settings']['appearanceTools'] = true; + } + } + $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data ); + $with_theme_supports->merge( static::$theme ); + + return $with_theme_supports; + } + + /** + * Gets the styles for blocks from the block.json file. + * + * @return WP_Theme_JSON + */ + public static function get_block_data() { + $registry = WP_Block_Type_Registry::get_instance(); + $blocks = $registry->get_all_registered(); + $config = array( 'version' => 1 ); + foreach ( $blocks as $block_name => $block_type ) { + if ( isset( $block_type->supports['__experimentalStyle'] ) ) { + $config['styles']['blocks'][ $block_name ] = static::remove_JSON_comments( $block_type->supports['__experimentalStyle'] ); + } + + if ( + isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) && + null === _wp_array_get( $config, array( 'styles', 'blocks', $block_name, 'spacing', 'blockGap' ), null ) + ) { + // Ensure an empty placeholder value exists for the block, if it provides a default blockGap value. + // The real blockGap value to be used will be determined when the styles are rendered for output. + $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null; + } + } + + /** + * Filters the data provided by the blocks for global styles & settings. + * + * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. + */ + $theme_json = apply_filters( 'wp_theme_json_data_blocks', new WP_Theme_JSON_Data_Gutenberg( $config, 'blocks' ) ); + $config = $theme_json->get_data(); + + return new WP_Theme_JSON_Gutenberg( $config, 'blocks' ); + } + + /** + * When given an array, this will remove any keys with the name `//`. + * + * @param array $array The array to filter. + * @return array The filtered array. + */ + private static function remove_JSON_comments( $array ) { + unset( $array['//'] ); + foreach ( $array as $k => $v ) { + if ( is_array( $v ) ) { + $array[ $k ] = static::remove_JSON_comments( $v ); + } + } + + return $array; + } + + /** + * Returns the style variations defined by the theme (parent and child). + * + * @return array + */ + public static function get_style_variations() { + $variations = array(); + $base_directory = get_stylesheet_directory() . '/styles'; + $parent_theme_directory = get_template_directory() . '/styles'; + if ( is_dir( $base_directory ) ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); + $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); + if ( $parent_theme_directory !== $base_directory && is_dir( $parent_theme_directory ) ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $parent_theme_directory ) ); + $nested_json_files = array_merge( iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ), $nested_html_files ); + } + 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( $translated ) )->get_raw_data(); + if ( empty( $variation['title'] ) ) { + $variation['title'] = basename( $path, '.json' ); + } + $variations[] = $variation; + } + } + } + return $variations; + } +} From 5e6133b50f5ef955d02cee680931b41a0ad1c7e3 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 14 Dec 2022 13:20:22 -0500 Subject: [PATCH 02/15] Fix mismatched variable names. --- lib/experimental/class-wp-theme-json-resolver-gutenberg.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index 5adcec54d2333b..ef3c704cc147be 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -188,10 +188,10 @@ public static function get_style_variations() { $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); if ( $parent_theme_directory !== $base_directory && is_dir( $parent_theme_directory ) ) { $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $parent_theme_directory ) ); - $nested_json_files = array_merge( iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ), $nested_html_files ); + $nested_json_files = array_merge( iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ), $nested_json_files ); } - ksort( $nested_html_files ); - foreach ( $nested_html_files as $path => $file ) { + ksort( $nested_json_files ); + foreach ( $nested_json_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' ) ); From 06e756a4b037ad3864d1ead57203164fe651fa5b Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Fri, 16 Dec 2022 11:00:35 -0500 Subject: [PATCH 03/15] Refactor to static utility function. --- ...class-wp-theme-json-resolver-gutenberg.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index ef3c704cc147be..e13b17964105c6 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -174,24 +174,34 @@ private static function remove_JSON_comments( $array ) { return $array; } + /** + * Adds all nested json files within a given directory to a given array. + * + * @param dir $dir The directory to recursively iterate and list files of. + * @param array $array The array to add to found json files to. + * @return array The merged array. + */ + private static function recursively_iterate_JSON( $dir, $array ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); + return array_merge( $array, iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ) ); + } + /** * Returns the style variations defined by the theme (parent and child). * * @return array */ public static function get_style_variations() { - $variations = array(); - $base_directory = get_stylesheet_directory() . '/styles'; + $variations = array(); + $base_directory = get_stylesheet_directory() . '/styles'; $parent_theme_directory = get_template_directory() . '/styles'; if ( is_dir( $base_directory ) ) { - $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); - $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); + $variation_files = static::recursively_iterate_JSON( $base_directory, array() ); if ( $parent_theme_directory !== $base_directory && is_dir( $parent_theme_directory ) ) { - $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $parent_theme_directory ) ); - $nested_json_files = array_merge( iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ), $nested_json_files ); + $variation_files = static::recursively_iterate_JSON( $parent_theme_directory, $variation_files ); } - ksort( $nested_json_files ); - foreach ( $nested_json_files as $path => $file ) { + ksort( $variation_files ); + foreach ( $variation_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' ) ); From 6a94315d9222a3dd6e97eb414f3108191a27f36f Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Fri, 16 Dec 2022 12:24:54 -0500 Subject: [PATCH 04/15] Check for naming collisions via basename. --- ...class-wp-theme-json-resolver-gutenberg.php | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index e13b17964105c6..1f640146801f6d 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -175,15 +175,15 @@ private static function remove_JSON_comments( $array ) { } /** - * Adds all nested json files within a given directory to a given array. + * Returns an array of all nested json files within a given directory. * - * @param dir $dir The directory to recursively iterate and list files of. - * @param array $array The array to add to found json files to. + * @param dir $dir The directory to recursively iterate and list files of. * @return array The merged array. */ - private static function recursively_iterate_JSON( $dir, $array ) { - $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); - return array_merge( $array, iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ) ); + private static function recursively_iterate_JSON( $dir ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); + $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); + return $nested_json_files; } /** @@ -192,13 +192,22 @@ private static function recursively_iterate_JSON( $dir, $array ) { * @return array */ public static function get_style_variations() { - $variations = array(); - $base_directory = get_stylesheet_directory() . '/styles'; - $parent_theme_directory = get_template_directory() . '/styles'; + $variations = array(); + $base_directory = get_stylesheet_directory() . '/styles'; + $template_directory = get_template_directory() . '/styles'; if ( is_dir( $base_directory ) ) { - $variation_files = static::recursively_iterate_JSON( $base_directory, array() ); - if ( $parent_theme_directory !== $base_directory && is_dir( $parent_theme_directory ) ) { - $variation_files = static::recursively_iterate_JSON( $parent_theme_directory, $variation_files ); + $variation_files = static::recursively_iterate_JSON( $base_directory ); + if ( $template_directory !== $base_directory && is_dir( $template_directory ) ) { + $variation_files_parent = static::recursively_iterate_JSON( $template_directory ); + // If the child and parent variation file basename are the same, only include the child theme's + foreach ( $variation_files_parent as $parent_path => $parent ) { + foreach ( $variation_files as $child_path => $child ) { + if ( basename( $parent_path ) === basename( $child_path ) ) { + unset( $variation_files_parent[ $parent_path ] ); + } + } + } + $variation_files = array_merge( $variation_files, $variation_files_parent ); } ksort( $variation_files ); foreach ( $variation_files as $path => $file ) { From b3f216248b10a86087ba73a3fb3a978a7bcb1c51 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Fri, 16 Dec 2022 12:46:04 -0500 Subject: [PATCH 05/15] Fix linting errors and warnings. --- lib/experimental/class-wp-theme-json-resolver-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index 1f640146801f6d..baea4a2cb5a1d0 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -199,7 +199,7 @@ public static function get_style_variations() { $variation_files = static::recursively_iterate_JSON( $base_directory ); if ( $template_directory !== $base_directory && is_dir( $template_directory ) ) { $variation_files_parent = static::recursively_iterate_JSON( $template_directory ); - // If the child and parent variation file basename are the same, only include the child theme's + // If the child and parent variation file basename are the same, only include the child theme's. foreach ( $variation_files_parent as $parent_path => $parent ) { foreach ( $variation_files as $child_path => $child ) { if ( basename( $parent_path ) === basename( $child_path ) ) { From 621ba97c408573211f571e7ed74e85b82ab56ca4 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 12:38:25 +0700 Subject: [PATCH 06/15] Rebase trunk. --- ...class-wp-theme-json-resolver-gutenberg.php | 43 +++- ...berg-rest-global-styles-controller-6-2.php | 26 ++ ...class-wp-theme-json-resolver-gutenberg.php | 227 ------------------ 3 files changed, 59 insertions(+), 237 deletions(-) delete mode 100644 lib/experimental/class-wp-theme-json-resolver-gutenberg.php diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 8733fd13bf2c77..5d3eca688f9e9e 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -670,24 +670,48 @@ public static function clean_cached_data() { } /** - * Returns the style variations defined by the theme. + * Returns an array of all nested json files within a given directory. + * + * @since 5.8.0 * - * @since 6.0.0 + * @param dir $dir The directory to recursively iterate and list files of. + * @return array The merged array. + */ + private static function recursively_iterate_JSON( $dir ) { + $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); + $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); + return $nested_json_files; + } + + /** + * Returns the style variations defined by the theme (parent and child). * * @return array */ public static function get_style_variations() { - $variations = array(); - $base_directory = get_stylesheet_directory() . '/styles'; + $variations = array(); + $base_directory = get_stylesheet_directory() . '/styles'; + $template_directory = get_template_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 ) { + $variation_files = static::recursively_iterate_JSON( $base_directory ); + if ( $template_directory !== $base_directory && is_dir( $template_directory ) ) { + $variation_files_parent = static::recursively_iterate_JSON( $template_directory ); + // If the child and parent variation file basename are the same, only include the child theme's. + foreach ( $variation_files_parent as $parent_path => $parent ) { + foreach ( $variation_files as $child_path => $child ) { + if ( basename( $parent_path ) === basename( $child_path ) ) { + unset( $variation_files_parent[ $parent_path ] ); + } + } + } + $variation_files = array_merge( $variation_files, $variation_files_parent ); + } + ksort( $variation_files ); + foreach ( $variation_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(); + $variation = ( new WP_Theme_JSON( $translated ) )->get_raw_data(); if ( empty( $variation['title'] ) ) { $variation['title'] = basename( $path, '.json' ); } @@ -697,5 +721,4 @@ public static function get_style_variations() { } return $variations; } - } diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php index 643374aba490cc..0f524ebecc6d39 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php @@ -252,4 +252,30 @@ public function get_theme_item( $request ) { return $response; } + + /** + * Returns the given theme global styles variations. + * + * @since 6.0.0 + * @since 6.2.0 Returns parent theme variations, if they exist. + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function get_theme_items( $request ) { + if ( get_stylesheet() !== $request['stylesheet'] ) { + // This endpoint only supports the active or parent theme for now. + return new WP_Error( + sprintf( '%s', $request['template'] ), + __( 'Theme not found.', 'gutenberg' ), + array( 'status' => 404 ) + ); + } + + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); + $response = rest_ensure_response( $variations ); + + return $response; + } } diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php deleted file mode 100644 index baea4a2cb5a1d0..00000000000000 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ /dev/null @@ -1,227 +0,0 @@ - true ) ) { - if ( ! empty( $deprecated ) ) { - _deprecated_argument( __METHOD__, '5.9' ); - } - - // When backporting to core, remove the instanceof Gutenberg class check, as it is only required for the Gutenberg plugin. - if ( null === static::$theme || ! static::has_same_registered_blocks( 'theme' ) ) { - $theme_json_file = static::get_file_path_from_theme( 'theme.json' ); - $wp_theme = wp_get_theme(); - if ( '' !== $theme_json_file ) { - $theme_json_data = static::read_json_file( $theme_json_file ); - $theme_json_data = static::translate( $theme_json_data, $wp_theme->get( 'TextDomain' ) ); - } else { - $theme_json_data = array(); - } - $theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data ); - - /** - * Filters the data provided by the theme for global styles & settings. - * - * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. - */ - $theme_json = apply_filters( 'wp_theme_json_data_theme', new WP_Theme_JSON_Data_Gutenberg( $theme_json_data, 'theme' ) ); - $theme_json_data = $theme_json->get_data(); - static::$theme = new WP_Theme_JSON_Gutenberg( $theme_json_data ); - - if ( $wp_theme->parent() ) { - // Get parent theme.json. - $parent_theme_json_file = static::get_file_path_from_theme( 'theme.json', true ); - if ( '' !== $parent_theme_json_file ) { - $parent_theme_json_data = static::read_json_file( $parent_theme_json_file ); - $parent_theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $parent_theme_json_data ); - $parent_theme = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data ); - - /* - * Merge the child theme.json into the parent theme.json. - * The child theme takes precedence over the parent. - */ - $parent_theme->merge( static::$theme ); - static::$theme = $parent_theme; - } - } - } - - if ( ! $settings['with_supports'] ) { - return static::$theme; - } - - /* - * We want the presets and settings declared in theme.json - * to override the ones declared via theme supports. - * So we take theme supports, transform it to theme.json shape - * and merge the static::$theme upon that. - */ - $theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( gutenberg_get_legacy_theme_supports_for_theme_json() ); - if ( ! wp_theme_has_theme_json() ) { - if ( ! isset( $theme_support_data['settings']['color'] ) ) { - $theme_support_data['settings']['color'] = array(); - } - - $default_palette = false; - if ( current_theme_supports( 'default-color-palette' ) ) { - $default_palette = true; - } - if ( ! isset( $theme_support_data['settings']['color']['palette'] ) ) { - // If the theme does not have any palette, we still want to show the core one. - $default_palette = true; - } - $theme_support_data['settings']['color']['defaultPalette'] = $default_palette; - - $default_gradients = false; - if ( current_theme_supports( 'default-gradient-presets' ) ) { - $default_gradients = true; - } - if ( ! isset( $theme_support_data['settings']['color']['gradients'] ) ) { - // If the theme does not have any gradients, we still want to show the core ones. - $default_gradients = true; - } - $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients; - - // Classic themes without a theme.json don't support global duotone. - $theme_support_data['settings']['color']['defaultDuotone'] = false; - - // Allow themes to enable appearance tools via theme_support. - if ( current_theme_supports( 'appearance-tools' ) ) { - $theme_support_data['settings']['appearanceTools'] = true; - } - } - $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data ); - $with_theme_supports->merge( static::$theme ); - - return $with_theme_supports; - } - - /** - * Gets the styles for blocks from the block.json file. - * - * @return WP_Theme_JSON - */ - public static function get_block_data() { - $registry = WP_Block_Type_Registry::get_instance(); - $blocks = $registry->get_all_registered(); - $config = array( 'version' => 1 ); - foreach ( $blocks as $block_name => $block_type ) { - if ( isset( $block_type->supports['__experimentalStyle'] ) ) { - $config['styles']['blocks'][ $block_name ] = static::remove_JSON_comments( $block_type->supports['__experimentalStyle'] ); - } - - if ( - isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ) && - null === _wp_array_get( $config, array( 'styles', 'blocks', $block_name, 'spacing', 'blockGap' ), null ) - ) { - // Ensure an empty placeholder value exists for the block, if it provides a default blockGap value. - // The real blockGap value to be used will be determined when the styles are rendered for output. - $config['styles']['blocks'][ $block_name ]['spacing']['blockGap'] = null; - } - } - - /** - * Filters the data provided by the blocks for global styles & settings. - * - * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. - */ - $theme_json = apply_filters( 'wp_theme_json_data_blocks', new WP_Theme_JSON_Data_Gutenberg( $config, 'blocks' ) ); - $config = $theme_json->get_data(); - - return new WP_Theme_JSON_Gutenberg( $config, 'blocks' ); - } - - /** - * When given an array, this will remove any keys with the name `//`. - * - * @param array $array The array to filter. - * @return array The filtered array. - */ - private static function remove_JSON_comments( $array ) { - unset( $array['//'] ); - foreach ( $array as $k => $v ) { - if ( is_array( $v ) ) { - $array[ $k ] = static::remove_JSON_comments( $v ); - } - } - - return $array; - } - - /** - * Returns an array of all nested json files within a given directory. - * - * @param dir $dir The directory to recursively iterate and list files of. - * @return array The merged array. - */ - private static function recursively_iterate_JSON( $dir ) { - $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); - $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); - return $nested_json_files; - } - - /** - * Returns the style variations defined by the theme (parent and child). - * - * @return array - */ - public static function get_style_variations() { - $variations = array(); - $base_directory = get_stylesheet_directory() . '/styles'; - $template_directory = get_template_directory() . '/styles'; - if ( is_dir( $base_directory ) ) { - $variation_files = static::recursively_iterate_JSON( $base_directory ); - if ( $template_directory !== $base_directory && is_dir( $template_directory ) ) { - $variation_files_parent = static::recursively_iterate_JSON( $template_directory ); - // If the child and parent variation file basename are the same, only include the child theme's. - foreach ( $variation_files_parent as $parent_path => $parent ) { - foreach ( $variation_files as $child_path => $child ) { - if ( basename( $parent_path ) === basename( $child_path ) ) { - unset( $variation_files_parent[ $parent_path ] ); - } - } - } - $variation_files = array_merge( $variation_files, $variation_files_parent ); - } - ksort( $variation_files ); - foreach ( $variation_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( $translated ) )->get_raw_data(); - if ( empty( $variation['title'] ) ) { - $variation['title'] = basename( $path, '.json' ); - } - $variations[] = $variation; - } - } - } - return $variations; - } -} From 34f972f50aa488151b820228ee6ca30212150a56 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 12:40:22 +0700 Subject: [PATCH 07/15] Update version since. --- lib/class-wp-theme-json-resolver-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 5d3eca688f9e9e..253fa17eeb460a 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -672,7 +672,7 @@ public static function clean_cached_data() { /** * Returns an array of all nested json files within a given directory. * - * @since 5.8.0 + * @since 6.2.0 * * @param dir $dir The directory to recursively iterate and list files of. * @return array The merged array. From e704e858fc42c9e2eaa50e3dba26ce4deddb34ea Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 12:41:44 +0700 Subject: [PATCH 08/15] Use _Gutenberg class. --- lib/class-wp-theme-json-resolver-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 253fa17eeb460a..6c5c8e84913e14 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -711,7 +711,7 @@ public static function get_style_variations() { $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( $translated ) )->get_raw_data(); + $variation = ( new WP_Theme_JSON_Gutenberg( $translated ) )->get_raw_data(); if ( empty( $variation['title'] ) ) { $variation['title'] = basename( $path, '.json' ); } From 745cea638a316517837bde381d8b1fbb76e9c664 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 12:42:33 +0700 Subject: [PATCH 09/15] Add back line break. --- lib/class-wp-theme-json-resolver-gutenberg.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 6c5c8e84913e14..a701a1cae06b9e 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -721,4 +721,5 @@ public static function get_style_variations() { } return $variations; } + } From 8e64d72d543bb044853dd7e68c90007a19e8feff Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 12:46:09 +0700 Subject: [PATCH 10/15] Fix linting errors. --- lib/class-wp-theme-json-resolver-gutenberg.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index a701a1cae06b9e..2d1b569d60ad70 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -671,7 +671,7 @@ public static function clean_cached_data() { /** * Returns an array of all nested json files within a given directory. - * + * * @since 6.2.0 * * @param dir $dir The directory to recursively iterate and list files of. @@ -721,5 +721,4 @@ public static function get_style_variations() { } return $variations; } - } From 7b1018ffcea9fa7bffed3454a483eb147b865e9a Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Wed, 11 Jan 2023 20:44:55 +0700 Subject: [PATCH 11/15] Refactor to allow parent theme variations to show up when child has no variations. --- ...class-wp-theme-json-resolver-gutenberg.php | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 2d1b569d60ad70..bbc66c0bbdf4fc 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -677,7 +677,7 @@ public static function clean_cached_data() { * @param dir $dir The directory to recursively iterate and list files of. * @return array The merged array. */ - private static function recursively_iterate_JSON( $dir ) { + private static function recursively_iterate_json( $dir ) { $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ) ); $nested_json_files = iterator_to_array( new RegexIterator( $nested_files, '/^.+\.json$/i', RecursiveRegexIterator::GET_MATCH ) ); return $nested_json_files; @@ -686,37 +686,40 @@ private static function recursively_iterate_JSON( $dir ) { /** * Returns the style variations defined by the theme (parent and child). * + * @since 6.2.0 Returns parent theme variations if theme is a child. + * * @return array */ public static function get_style_variations() { + $variation_files = array(); $variations = array(); $base_directory = get_stylesheet_directory() . '/styles'; $template_directory = get_template_directory() . '/styles'; if ( is_dir( $base_directory ) ) { - $variation_files = static::recursively_iterate_JSON( $base_directory ); - if ( $template_directory !== $base_directory && is_dir( $template_directory ) ) { - $variation_files_parent = static::recursively_iterate_JSON( $template_directory ); - // If the child and parent variation file basename are the same, only include the child theme's. - foreach ( $variation_files_parent as $parent_path => $parent ) { - foreach ( $variation_files as $child_path => $child ) { - if ( basename( $parent_path ) === basename( $child_path ) ) { - unset( $variation_files_parent[ $parent_path ] ); - } + $variation_files = static::recursively_iterate_json( $base_directory ); + } + if ( is_dir( $template_directory ) && $template_directory !== $base_directory ) { + $variation_files_parent = static::recursively_iterate_json( $template_directory ); + // If the child and parent variation file basename are the same, only include the child theme's. + foreach ( $variation_files_parent as $parent_path => $parent ) { + foreach ( $variation_files as $child_path => $child ) { + if ( basename( $parent_path ) === basename( $child_path ) ) { + unset( $variation_files_parent[ $parent_path ] ); } } - $variation_files = array_merge( $variation_files, $variation_files_parent ); } - ksort( $variation_files ); - foreach ( $variation_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; + $variation_files = array_merge( $variation_files, $variation_files_parent ); + } + ksort( $variation_files ); + foreach ( $variation_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; From 23932115883f2e60cb12411a82df4af512e1df7c Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Thu, 12 Jan 2023 11:45:06 +0700 Subject: [PATCH 12/15] Add unit test for resolver class. --- phpunit/class-wp-theme-json-resolver-test.php | 59 +++++++++++++++++++ .../styles/variation-child.json | 18 ++++++ .../themedir1/block-theme-child/theme.json | 2 +- .../block-theme/styles/variation.json | 18 ++++++ phpunit/data/themedir1/block-theme/theme.json | 2 +- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 phpunit/data/themedir1/block-theme-child/styles/variation-child.json create mode 100644 phpunit/data/themedir1/block-theme/styles/variation.json diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 7b46dc3cd0d4a2..fc48e7acaf44a7 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -503,4 +503,63 @@ public function data_get_merged_data_returns_origin() { ); } + public function test_get_style_variations_returns_all_variations() { + // Switch to a child theme. + switch_theme( 'block-theme-child' ); + wp_set_current_user( self::$administrator_id ); + + $actual_settings = WP_Theme_JSON_Resolver_Gutenberg::get_style_variations(); + $expected_settings = array( + array( + 'version' => 2, + 'title' => 'variation-child', + 'settings' => array( + 'blocks' => array( + 'core/post-title' => array( + 'color' => array( + 'palette' => array( + 'theme' => array( + array( + 'slug' => 'light', + 'name' => 'Light', + 'color' => '#f1f1f1', + ), + ), + ), + ), + ), + ), + ), + ), + array( + 'version' => 2, + 'title' => 'variation', + 'settings' => array( + 'blocks' => array( + 'core/paragraph' => array( + 'color' => array( + 'palette' => array( + 'theme' => array( + array( + 'slug' => 'light', + 'name' => 'Light', + 'color' => '#f2f2f2', + ), + ), + ), + ), + ), + ), + ), + ), + ); + self::recursive_ksort( $actual_settings ); + self::recursive_ksort( $expected_settings ); + + $this->assertSame( + $expected_settings, + $actual_settings + ); + } + } diff --git a/phpunit/data/themedir1/block-theme-child/styles/variation-child.json b/phpunit/data/themedir1/block-theme-child/styles/variation-child.json new file mode 100644 index 00000000000000..340198ffe0b65f --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child/styles/variation-child.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "settings": { + "blocks": { + "core/post-title": { + "color": { + "palette": [ + { + "slug": "light", + "name": "Light", + "color": "#f1f1f1" + } + ] + } + } + } + } +} diff --git a/phpunit/data/themedir1/block-theme-child/theme.json b/phpunit/data/themedir1/block-theme-child/theme.json index 90fe35e758b455..ebfa68d9f74811 100644 --- a/phpunit/data/themedir1/block-theme-child/theme.json +++ b/phpunit/data/themedir1/block-theme-child/theme.json @@ -1,5 +1,5 @@ { - "version": 1, + "version": 2, "settings": { "color": { "palette": [ diff --git a/phpunit/data/themedir1/block-theme/styles/variation.json b/phpunit/data/themedir1/block-theme/styles/variation.json new file mode 100644 index 00000000000000..42c20fc63b5925 --- /dev/null +++ b/phpunit/data/themedir1/block-theme/styles/variation.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "settings": { + "blocks": { + "core/paragraph": { + "color": { + "palette": [ + { + "slug": "light", + "name": "Light", + "color": "#f2f2f2" + } + ] + } + } + } + } +} diff --git a/phpunit/data/themedir1/block-theme/theme.json b/phpunit/data/themedir1/block-theme/theme.json index fdedf31009473d..aa9f93a3c0aa1a 100644 --- a/phpunit/data/themedir1/block-theme/theme.json +++ b/phpunit/data/themedir1/block-theme/theme.json @@ -1,5 +1,5 @@ { - "version": 1, + "version": 2, "settings": { "color": { "palette": [ From 90f27801b5ec998480ef4a954292a3130bf320d2 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Thu, 12 Jan 2023 11:55:56 +0700 Subject: [PATCH 13/15] Comment the test. --- phpunit/class-wp-theme-json-resolver-test.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index fc48e7acaf44a7..b0940e30fdb0ea 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -503,7 +503,13 @@ public function data_get_merged_data_returns_origin() { ); } - public function test_get_style_variations_returns_all_variations() { + + /** + * Test that get_style_variations returns all variations, including parent theme variations if the theme is a child. + * + * @covers WP_Theme_JSON_Resolver::get_style_variations + **/ + public function test_get_style_variations_returns_all_variations() { // Switch to a child theme. switch_theme( 'block-theme-child' ); wp_set_current_user( self::$administrator_id ); From 78772891b51d0c26edd97b90b8eeb7ae3132b3dd Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Thu, 12 Jan 2023 20:28:53 +0700 Subject: [PATCH 14/15] Fix lint. --- phpunit/class-wp-theme-json-resolver-test.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index b0940e30fdb0ea..865b1945289ff4 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -503,13 +503,13 @@ public function data_get_merged_data_returns_origin() { ); } - + /** * Test that get_style_variations returns all variations, including parent theme variations if the theme is a child. * * @covers WP_Theme_JSON_Resolver::get_style_variations **/ - public function test_get_style_variations_returns_all_variations() { + public function test_get_style_variations_returns_all_variations() { // Switch to a child theme. switch_theme( 'block-theme-child' ); wp_set_current_user( self::$administrator_id ); From fcfbc1246ffa88b0c19ee03e0491ecee0a3e7a3a Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Tue, 17 Jan 2023 14:35:43 +0800 Subject: [PATCH 15/15] Rename variations to account for child overwriting parent variation. --- phpunit/class-wp-theme-json-resolver-test.php | 19 ++++++++++--------- .../block-theme-child/styles/variation-a.json | 18 ++++++++++++++++++ .../{variation.json => variation-a.json} | 0 .../styles/variation-b.json} | 0 4 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 phpunit/data/themedir1/block-theme-child/styles/variation-a.json rename phpunit/data/themedir1/block-theme/styles/{variation.json => variation-a.json} (100%) rename phpunit/data/themedir1/{block-theme-child/styles/variation-child.json => block-theme/styles/variation-b.json} (100%) diff --git a/phpunit/class-wp-theme-json-resolver-test.php b/phpunit/class-wp-theme-json-resolver-test.php index 865b1945289ff4..4cc34bf97b32c7 100644 --- a/phpunit/class-wp-theme-json-resolver-test.php +++ b/phpunit/class-wp-theme-json-resolver-test.php @@ -505,7 +505,8 @@ public function data_get_merged_data_returns_origin() { /** - * Test that get_style_variations returns all variations, including parent theme variations if the theme is a child. + * Test that get_style_variations returns all variations, including parent theme variations if the theme is a child, + * and that the child variation overwrites the parent variation of the same name. * * @covers WP_Theme_JSON_Resolver::get_style_variations **/ @@ -518,17 +519,17 @@ public function test_get_style_variations_returns_all_variations() { $expected_settings = array( array( 'version' => 2, - 'title' => 'variation-child', + 'title' => 'variation-a', 'settings' => array( 'blocks' => array( - 'core/post-title' => array( + 'core/paragraph' => array( 'color' => array( 'palette' => array( 'theme' => array( array( - 'slug' => 'light', - 'name' => 'Light', - 'color' => '#f1f1f1', + 'slug' => 'dark', + 'name' => 'Dark', + 'color' => '#010101', ), ), ), @@ -539,17 +540,17 @@ public function test_get_style_variations_returns_all_variations() { ), array( 'version' => 2, - 'title' => 'variation', + 'title' => 'variation-b', 'settings' => array( 'blocks' => array( - 'core/paragraph' => array( + 'core/post-title' => array( 'color' => array( 'palette' => array( 'theme' => array( array( 'slug' => 'light', 'name' => 'Light', - 'color' => '#f2f2f2', + 'color' => '#f1f1f1', ), ), ), diff --git a/phpunit/data/themedir1/block-theme-child/styles/variation-a.json b/phpunit/data/themedir1/block-theme-child/styles/variation-a.json new file mode 100644 index 00000000000000..a9d5ade8946928 --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child/styles/variation-a.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "settings": { + "blocks": { + "core/paragraph": { + "color": { + "palette": [ + { + "slug": "dark", + "name": "Dark", + "color": "#010101" + } + ] + } + } + } + } +} diff --git a/phpunit/data/themedir1/block-theme/styles/variation.json b/phpunit/data/themedir1/block-theme/styles/variation-a.json similarity index 100% rename from phpunit/data/themedir1/block-theme/styles/variation.json rename to phpunit/data/themedir1/block-theme/styles/variation-a.json diff --git a/phpunit/data/themedir1/block-theme-child/styles/variation-child.json b/phpunit/data/themedir1/block-theme/styles/variation-b.json similarity index 100% rename from phpunit/data/themedir1/block-theme-child/styles/variation-child.json rename to phpunit/data/themedir1/block-theme/styles/variation-b.json