diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php index 4bfdd34e2b2fb..c89309366d2de 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php @@ -222,7 +222,6 @@ public static function remove_insecure_properties( $theme_json ) { return $theme_json; } - /** * Returns the metadata for each block. * @@ -451,7 +450,6 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) { * @return string Styles for the block. */ public function get_styles_for_block( $block_metadata ) { - $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); $selector = $block_metadata['selector']; @@ -474,9 +472,9 @@ public function get_styles_for_block( $block_metadata ) { // element then compute the style properties for it. // Otherwise just compute the styles for the default selector as normal. if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings ); + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json ); } else { - $declarations = static::compute_style_properties( $node, $settings ); + $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json ); } $block_rules = ''; @@ -554,4 +552,119 @@ protected function get_block_classes( $style_nodes ) { return $block_rules; } + + /** + * Given a styles array, it extracts the style properties + * and adds them to the $declarations array following the format: + * + * ```php + * array( + * 'name' => 'property_name', + * 'value' => 'property_value, + * ) + * ``` + * + * @param array $styles Styles to process. + * @param array $settings Theme settings. + * @param array $properties Properties metadata. + * @param array $theme_json Theme JSON array. + * @return array Returns the modified $declarations. + */ + protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) { + if ( null === $properties ) { + $properties = static::PROPERTIES_METADATA; + } + + $declarations = array(); + if ( empty( $styles ) ) { + return $declarations; + } + + foreach ( $properties as $css_property => $value_path ) { + $value = static::get_property_value( $styles, $value_path, $theme_json ); + + // Look up protected properties, keyed by value path. + // Skip protected properties that are explicitly set to `null`. + if ( is_array( $value_path ) ) { + $path_string = implode( '.', $value_path ); + if ( + array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) && + _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null + ) { + continue; + } + } + + // Skip if empty and not "0" or value represents array of longhand values. + $has_missing_value = empty( $value ) && ! is_numeric( $value ); + if ( $has_missing_value || is_array( $value ) ) { + continue; + } + + $declarations[] = array( + 'name' => $css_property, + 'value' => $value, + ); + } + + return $declarations; + } + + /** + * Returns the style property for the given path. + * + * It also converts CSS Custom Property stored as + * "var:preset|color|secondary" to the form + * "--wp--preset--color--secondary". + * + * It also converts references to a path to the value + * stored at that location, e.g. + * { "ref": "style.color.background" } => "#fff". + * + * @param array $styles Styles subtree. + * @param array $path Which property to process. + * @param array $theme_json Theme JSON array. + * @return string Style property value. + */ + protected static function get_property_value( $styles, $path, $theme_json = null ) { + $value = _wp_array_get( $styles, $path, '' ); + + // This converts references to a path to the value at that path + // where the values is an array with a "ref" key, pointing to a path. + // For example: { "ref": "style.color.background" } => "#fff". + if ( is_array( $value ) && array_key_exists( 'ref', $value ) ) { + $value_path = explode( '.', $value['ref'] ); + $ref_value = _wp_array_get( $theme_json, $value_path ); + // Only use the ref value if we find anything. + if ( ! empty( $ref_value ) && is_string( $ref_value ) ) { + $value = $ref_value; + } + + if ( is_array( $ref_value ) && array_key_exists( 'ref', $ref_value ) ) { + $path_string = json_encode( $path ); + $ref_value_string = json_encode( $ref_value ); + _doing_it_wrong( 'get_property_value', "Your theme.json file uses a dynamic value (${ref_value_string}) for the path at ${path_string}. However, the value at ${path_string} is also a dynamic value (pointing to ${ref_value['ref']}) and pointing to another dynamic value is not supported. Please update ${path_string} to point directly to ${ref_value['ref']}.", '6.1.0' ); + } + } + + if ( '' === $value || is_array( $value ) ) { + return $value; + } + + // Convert custom CSS properties. + $prefix = 'var:'; + $prefix_len = strlen( $prefix ); + $token_in = '|'; + $token_out = '--'; + if ( 0 === strncmp( $value, $prefix, $prefix_len ) ) { + $unwrapped_name = str_replace( + $token_in, + $token_out, + substr( $value, $prefix_len ) + ); + $value = "var(--wp--$unwrapped_name)"; + } + + return $value; + } } diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php index 6c4e5b534a4f7..7f7d4a3ace2f1 100644 --- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php @@ -176,6 +176,4 @@ public static function get_merged_data( $origin = 'custom' ) { return $result; } - - } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 78267771bcc46..ed5b867e77534 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -2821,4 +2821,119 @@ function test_get_element_class_name_invalid() { $this->assertEquals( $expected, $actual ); } + + /** + * Testing that dynamic properties in theme.json return the value they refrence, e.g. + * array( 'ref' => 'styles.color.background' ) => "#ffffff". + */ + function test_get_property_value_valid() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => '#000000', + ), + 'elements' => array( + 'button' => array( + 'color' => array( + 'background' => array( 'ref' => 'styles.color.text' ), + 'text' => array( 'ref' => 'styles.color.background' ), + ), + ), + ), + ), + ) + ); + + $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #000000;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}'; + $this->assertEquals( $expected, $theme_json->get_stylesheet() ); + } + + /** + * Testing that dynamic properties in theme.json that + * refer to other dynamic properties in a loop + * then they should be left untouched. + * + * @expectedIncorrectUsage get_property_value + */ + function test_get_property_value_loop() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => array( 'ref' => 'styles.elements.button.color.background' ), + ), + 'elements' => array( + 'button' => array( + 'color' => array( + 'background' => array( 'ref' => 'styles.color.text' ), + 'text' => array( 'ref' => 'styles.color.background' ), + ), + ), + ), + ), + ) + ); + + $expected = 'body { margin: 0; }body{background-color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $this->assertSame( $expected, $theme_json->get_stylesheet() ); + } + + /** + * Testing that dynamic properties in theme.json that + * refer to other dynamic properties then they should be left unprocessed. + * + * @expectedIncorrectUsage get_property_value + */ + function test_get_property_value_recursion() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => array( 'ref' => 'styles.color.background' ), + ), + 'elements' => array( + 'button' => array( + 'color' => array( + 'background' => array( 'ref' => 'styles.color.text' ), + 'text' => array( 'ref' => 'styles.color.background' ), + ), + ), + ), + ), + ) + ); + + $expected = 'body { margin: 0; }body{background-color: #ffffff;color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }.wp-element-button, .wp-block-button__link{color: #ffffff;}'; + $this->assertEquals( $expected, $theme_json->get_stylesheet() ); + } + + /** + * Testing that dynamic properties in theme.json that + * refer to themselves then they should be left unprocessed. + * + * @expectedIncorrectUsage get_property_value + */ + function test_get_property_value_self() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => 2, + 'styles' => array( + 'color' => array( + 'background' => '#ffffff', + 'text' => array( 'ref' => 'styles.color.text' ), + ), + ), + ) + ); + + $expected = 'body { margin: 0; }body{background-color: #ffffff;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + $this->assertEquals( $expected, $theme_json->get_stylesheet() ); + } } diff --git a/test/integration/fixtures/blocks/core__table__deprecated-1.html b/test/integration/fixtures/blocks/core__table__deprecated-1.html index 6f31e17fd4263..3b748a37a4eda 100644 --- a/test/integration/fixtures/blocks/core__table__deprecated-1.html +++ b/test/integration/fixtures/blocks/core__table__deprecated-1.html @@ -1,3 +1,3 @@
VersionMusicianDate
.70No musician chosen.May 27, 2003
1.0Miles DavisJanuary 3, 2004
Lots of versions skipped, see the full list
4.4Clifford BrownDecember 8, 2015
4.5Coleman HawkinsApril 12, 2016
4.6Pepper AdamsAugust 16, 2016
4.7Sarah VaughanDecember 6, 2016
Table Caption
- \ No newline at end of file +