Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for merging plugin theme.json into active block theme.json #42573

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .wp-env.json
Expand Up @@ -8,7 +8,8 @@
"wp-content/plugins/gutenberg": ".",
"wp-content/mu-plugins": "./packages/e2e-tests/mu-plugins",
"wp-content/plugins/gutenberg-test-plugins": "./packages/e2e-tests/plugins",
"wp-content/themes/gutenberg-test-themes": "./test/gutenberg-test-themes"
"wp-content/themes/gutenberg-test-themes": "./test/gutenberg-test-themes",
"wp-content/plugins/test-plugin-theme-json": "./test/test-plugin-theme-json"
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions lib/experimental/class-wp-theme-json-gutenberg.php
Expand Up @@ -16,4 +16,13 @@
*/
class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_6_1 {

/**
* Cleans cached data across class instances.
*
* @since 6.1.0
*/
public static function clean_cached_data() {
static::$blocks_metadata = null;
}

}
75 changes: 71 additions & 4 deletions lib/experimental/class-wp-theme-json-resolver-gutenberg.php
Expand Up @@ -16,6 +16,15 @@
* @access private
*/
class WP_Theme_JSON_Resolver_Gutenberg extends WP_Theme_JSON_Resolver_6_1 {

/**
* Container for data coming from plugins
*
* @since 6.1.0
* @var WP_Theme_JSON
*/
protected static $plugins = null;

/**
* Returns the theme's data.
*
Expand Down Expand Up @@ -127,6 +136,41 @@ public static function get_block_data() {
return new WP_Theme_JSON_Gutenberg( $config, 'core' );
}

/**
* Returns any plugin provided theme data from all plugins merged together.
*
* Note: The current merge strategy merges in order of plugins returned by
* `wp_get_active_and_valid_plugins()`. This means the data from the last
* plugin in the list will override any equivalent properties from those
* earlier in the list.
*
* @return WP_Theme_JSON
*/
public static function get_plugin_theme_data() {
if ( null === static::$plugins ) {
$plugins_data = array();
$active_plugin_paths = wp_get_active_and_valid_plugins();
$plugin_json = new WP_Theme_JSON_Gutenberg();
foreach ( $active_plugin_paths as $path ) {
$config = static::read_json_file( dirname( $path ) . '/theme.json' );
if ( ! empty( $config ) ) {
$plugin_meta_data = get_plugin_data( $path, false, false );
if ( isset( $plugin_meta_data['TextDomain'] ) ) {
$config = static::translate( $config, $plugin_meta_data['TextDomain'] );
}
// TODO, this is where we could potentially introduce different merge
// strategies for plugin provided data.
$plugin_json->merge(
new WP_Theme_JSON_Gutenberg( $config )
);
$plugins_data[ $path ] = $config;
}
}
static::$plugins = $plugin_json;
}
return static::$plugins;
}

/**
* When given an array, this will remove any keys with the name `//`.
*
Expand All @@ -148,14 +192,15 @@ private static function remove_JSON_comments( $array ) {
* Returns the data merged from multiple origins.
*
* There are three sources of data (origins) for a site:
* default, theme, and custom. The custom's has higher priority
* than the theme's, and the theme's higher than default's.
* default, theme, plugin, and custom. The custom's has higher priority
* than the theme's, the theme's has higher priority than plugin's,
* and the plugin's has higher priority than the default's.
*
* Unlike the getters {@link get_core_data},
* {@link get_theme_data}, and {@link get_user_data},
* {@link get_theme_data}, {@link get_plugin_data}, and {@link get_user_data},
* this method returns data after it has been merged
* with the previous origins. This means that if the same piece of data
* is declared in different origins (user, theme, and core),
* is declared in different origins (user, theme, plugin, and core),
* the last origin overrides the previous.
*
* For example, if the user has set a background color
Expand All @@ -178,7 +223,11 @@ public static function get_merged_data( $origin = 'custom' ) {
$result = new WP_Theme_JSON_Gutenberg();
$result->merge( static::get_core_data() );
$result->merge( static::get_block_data() );
// TODO: This is where we could potentially introduce new strategies for
// merging plugin provided data.
$result->merge( static::get_plugin_theme_data() );
$result->merge( static::get_theme_data() );

if ( 'custom' === $origin ) {
$result->merge( static::get_user_data() );
}
Expand All @@ -187,4 +236,22 @@ public static function get_merged_data( $origin = 'custom' ) {

return $result;
}

/**
* Cleans the cached data so it can be recalculated.
*
* @since 5.8.0
* @since 5.9.0 Added the `$user`, `$user_custom_post_type_id`,
* and `$i18n_schema` variables to reset.
* @since 6.1.0 Added the `$plugins` variables to reset.
*/
public static function clean_cached_data() {
static::$core = null;
static::$theme = null;
static::$user = null;
static::$user_custom_post_type_id = null;
static::$theme_has_support = null;
static::$i18n_schema = null;
static::$plugins = null;
}
}
195 changes: 195 additions & 0 deletions phpunit/class-wp-theme-json-resolver-test.php
Expand Up @@ -293,4 +293,199 @@ function test_get_user_data_from_wp_global_styles_does_not_use_uncached_queries(
$this->assertEquals( 0, $query_count, 'Unexpected SQL queries detected for the wp_global_style post type' );
remove_filter( 'query', array( $this, 'filter_db_query' ) );
}

function test_get_plugin_theme_json() {
WP_Theme_JSON_Gutenberg::clean_cached_data();
activate_plugin( 'test-plugin-theme-json/test-plugin-theme-json.php' );
register_block_type( 'mycustom/block', array( 'render_callback' => null ) );
switch_theme( 'block-theme' );
$actual = WP_Theme_JSON_Resolver_Gutenberg::get_plugin_theme_data()->get_raw_data();
$expected = array(
'settings' => array(
'border' => array(
'color' => true,
'radius' => true,
'style' => true,
'width' => true,
),
'color' => array(
'custom' => true,
'customGradient' => true,
'link' => true,
'palette' => array(
'theme' => array(
array(
'color' => '#e44064',
'name' => 'Pink Red',
'slug' => 'pink-red',
),
),
),
),
'spacing' => array(
'blockGap' => true,
'margin' => true,
'padding' => true,
),
'typography' => array(
'lineHeight' => true,
),
),
'styles' => array(
'blocks' => array(
'core/button' => array(
'border' => array(
'radius' => '10px',
),
'color' => array(
'background' => 'var(--wp--preset--color--pink-red)',
),
),
'mycustom/block' => array(
'color' => array(
'background' => 'var(--wp--preset--color--pink-red)',
),
),
),
),
'version' => 2,
);
self::recursive_ksort( $actual );
self::recursive_ksort( $expected );

$this->assertSame(
$actual,
$expected
);
unregister_block_type( 'mycustom/block' );
deactivate_plugins( 'test-plugin-theme-json/test-plugin-theme-json.php' );
}

function test_merge_plugin_theme_json() {
WP_Theme_JSON_Gutenberg::clean_cached_data();
activate_plugin( 'test-plugin-theme-json/test-plugin-theme-json.php' );
register_block_type( 'mycustom/block', array( 'render_callback' => null ) );
switch_theme( 'block-theme' );
$raw_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_raw_data();
$actual_settings = $raw_data['settings'];
$expected_settings = array(
'border' => array(
'color' => true,
'radius' => true,
'style' => true,
'width' => true,
),
'color' => array(
'custom' => false,
'customGradient' => true,
'palette' => array(
'theme' => array(
array(
'slug' => 'light',
'name' => 'Light',
'color' => '#f3f4f6',
),
array(
'slug' => 'primary',
'name' => 'Primary',
'color' => '#3858e9',
),
array(
'slug' => 'dark',
'name' => 'Dark',
'color' => '#111827',
),
array(
'color' => '#e44064',
'name' => 'Pink Red',
'slug' => 'pink-red',
),
),
),
'link' => true,
),
'typography' => array(
'customFontSize' => true,
'lineHeight' => false,
),
'spacing' => array(
'units' => false,
'padding' => false,
),
'blocks' => array(
'core/button' => array(
'border' => array(
'radius' => true,
),
),
'core/paragraph' => array(
'color' => array(
'palette' => array(
'theme' => array(
array(
'slug' => 'light',
'name' => 'Light',
'color' => '#f5f7f9',
),
),
),
),
),
'core/pullquote' => array(
'border' => array(
'color' => true,
'radius' => true,
'style' => true,
'width' => true,
),
),
),
);

$raw_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_raw_data();
$actual_styles = $raw_data['styles'];
$expected_styles = array(
'blocks' => array(
'core/button' => array(
'border' => array(
'radius' => '10px',
),
'color' => array(
'background' => 'var(--wp--preset--color--pink-red)',
),
),
'mycustom/block' => array(
'color' => array(
'background' => 'var(--wp--preset--color--pink-red)',
),
),
),
);

self::recursive_ksort( $actual_settings );
self::recursive_ksort( $expected_settings );
self::recursive_ksort( $actual_styles );
self::recursive_ksort( $expected_styles );

$this->assertSame(
$actual_settings['blocks'],
$expected_settings['blocks'],
'The blocks index in the settings array does not match as expected'
);

$this->assertArrayHasKey( 'mycustom/block', $actual_styles['blocks'] );
$this->assertArrayHasKey( 'core/button', $actual_styles['blocks'] );
$this->assertSame(
$actual_styles['blocks']['mycustom/block'],
$expected_styles['blocks']['mycustom/block'],
'The styles for mycustom/block should be the same'
);
$this->assertSame(
$actual_styles['blocks']['core/button'],
$expected_styles['blocks']['core/button'],
'The plugin theme.json should have its definitions for core/button overridden by the theme.'
);
unregister_block_type( 'mycustom/block' );
deactivate_plugins( 'test-plugin-theme-json/test-plugin-theme-json.php' );
}
}
10 changes: 10 additions & 0 deletions test/test-plugin-theme-json/test-plugin-theme-json.php
@@ -0,0 +1,10 @@
<?php
/**
* Plugin Name: Test Plugin Theme JSON
* Plugin URI: https://dev.test
* Description: For testing plugin adding theme.json.
* Version: 1.0
* Author: WordPress
* Author URI: https://dev.test
* Text Domain: dev-test
*/
42 changes: 42 additions & 0 deletions test/test-plugin-theme-json/theme.json
@@ -0,0 +1,42 @@
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"settings": {
"appearanceTools": true,
"border": {
"color": true,
"radius": true,
"style": true,
"width": true
},
"color": {
"custom": true,
"customGradient": true,
"link": true,
"palette": [
{
"color": "#e44064",
"name": "Pink Red",
"slug": "pink-red"
}
]
}
},
"styles": {
"blocks": {
"core/button": {
"border": {
"radius": "10px"
},
"color": {
"background": "var(--wp--preset--color--pink-red)"
}
},
"mycustom/block": {
"color": {
"background": "var(--wp--preset--color--pink-red)"
}
}
}
},
"version": 2
}