Changeset 52049
- Timestamp:
- 11/08/2021 07:18:39 PM (3 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/block-editor.php
r52042 r52049 304 304 ); 305 305 306 $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $editor_settings);306 $theme_json = WP_Theme_JSON_Resolver::get_merged_data(); 307 307 308 308 if ( WP_Theme_JSON_Resolver::theme_has_support() ) { 309 309 $editor_settings['styles'][] = array( 310 'css' => $theme_json->get_stylesheet( 'block_styles'),310 'css' => $theme_json->get_stylesheet( array( 'styles', 'presets' ) ), 311 311 '__unstableType' => 'globalStyles', 312 312 ); 313 313 $editor_settings['styles'][] = array( 314 'css' => $theme_json->get_stylesheet( 'css_variables'),314 'css' => $theme_json->get_stylesheet( array( 'variables' ) ), 315 315 '__experimentalNoWrapper' => true, 316 316 '__unstableType' => 'globalStyles', … … 359 359 unset( $editor_settings['__experimentalFeatures']['typography']['customFontSize'] ); 360 360 } 361 if ( isset( $editor_settings['__experimentalFeatures']['typography'][' customLineHeight'] ) ) {362 $editor_settings['enableCustomLineHeight'] = $editor_settings['__experimentalFeatures']['typography'][' customLineHeight'];363 unset( $editor_settings['__experimentalFeatures']['typography'][' customLineHeight'] );361 if ( isset( $editor_settings['__experimentalFeatures']['typography']['lineHeight'] ) ) { 362 $editor_settings['enableCustomLineHeight'] = $editor_settings['__experimentalFeatures']['typography']['lineHeight']; 363 unset( $editor_settings['__experimentalFeatures']['typography']['lineHeight'] ); 364 364 } 365 365 if ( isset( $editor_settings['__experimentalFeatures']['spacing']['units'] ) ) { … … 367 367 unset( $editor_settings['__experimentalFeatures']['spacing']['units'] ); 368 368 } 369 if ( isset( $editor_settings['__experimentalFeatures']['spacing'][' customPadding'] ) ) {370 $editor_settings['enableCustomSpacing'] = $editor_settings['__experimentalFeatures']['spacing'][' customPadding'];371 unset( $editor_settings['__experimentalFeatures']['spacing'][' customPadding'] );369 if ( isset( $editor_settings['__experimentalFeatures']['spacing']['padding'] ) ) { 370 $editor_settings['enableCustomSpacing'] = $editor_settings['__experimentalFeatures']['spacing']['padding']; 371 unset( $editor_settings['__experimentalFeatures']['spacing']['padding'] ); 372 372 } 373 373 -
trunk/src/wp-includes/block-supports/duotone.php
r51657 r52049 68 68 // Convert into [0, 1] range if it isn't already. 69 69 return ( $n % $max ) / (float) $max; 70 } 71 72 /** 73 * Direct port of tinycolor's boundAlpha function to maintain consistency with 74 * how tinycolor works. 75 * 76 * @see https://github.com/bgrins/TinyColor 77 * 78 * @since 5.9.0 79 * @access private 80 * 81 * @param mixed $n Number of unknown type. 82 * @return float Value in the range [0,1]. 83 */ 84 function _wp_tinycolor_bound_alpha( $n ) { 85 if ( is_numeric( $n ) ) { 86 $n = (float) $n; 87 if ( $n >= 0 && $n <= 1 ) { 88 return $n; 89 } 90 } 91 return 1; 70 92 } 71 93 … … 171 193 /** 172 194 * Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2 173 * used in the JavaScript. Only colors output from react-color are implemented 174 * and the alpha value is ignored as it is not used in duotone. 195 * used in the JavaScript. Only colors output from react-color are implemented. 175 196 * 176 197 * Direct port of TinyColor's function, lightly simplified to maintain … … 181 202 * 182 203 * @since 5.8.0 204 * @since 5.9.0 Added alpha processing. 183 205 * @access private 184 206 * … … 200 222 $rgb_regexp = '/^rgb' . $permissive_match3 . '$/'; 201 223 if ( preg_match( $rgb_regexp, $color_str, $match ) ) { 202 returnwp_tinycolor_rgb_to_rgb(224 $rgb = wp_tinycolor_rgb_to_rgb( 203 225 array( 204 226 'r' => $match[1], … … 207 229 ) 208 230 ); 231 232 $rgb['a'] = 1; 233 234 return $rgb; 209 235 } 210 236 211 237 $rgba_regexp = '/^rgba' . $permissive_match4 . '$/'; 212 238 if ( preg_match( $rgba_regexp, $color_str, $match ) ) { 213 returnwp_tinycolor_rgb_to_rgb(239 $rgb = wp_tinycolor_rgb_to_rgb( 214 240 array( 215 241 'r' => $match[1], … … 218 244 ) 219 245 ); 246 247 $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] ); 248 249 return $rgb; 220 250 } 221 251 222 252 $hsl_regexp = '/^hsl' . $permissive_match3 . '$/'; 223 253 if ( preg_match( $hsl_regexp, $color_str, $match ) ) { 224 returnwp_tinycolor_hsl_to_rgb(254 $rgb = wp_tinycolor_hsl_to_rgb( 225 255 array( 226 256 'h' => $match[1], … … 229 259 ) 230 260 ); 261 262 $rgb['a'] = 1; 263 264 return $rgb; 231 265 } 232 266 … … 240 274 ) 241 275 ); 276 277 $rgb['a'] = _wp_tinycolor_bound_alpha( $match[4] ); 278 279 return $rgb; 242 280 } 243 281 244 282 $hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/'; 245 283 if ( preg_match( $hex8_regexp, $color_str, $match ) ) { 246 returnwp_tinycolor_rgb_to_rgb(284 $rgb = wp_tinycolor_rgb_to_rgb( 247 285 array( 248 286 'r' => base_convert( $match[1], 16, 10 ), … … 251 289 ) 252 290 ); 291 292 $rgb['a'] = _wp_tinycolor_bound_alpha( 293 base_convert( $match[4], 16, 10 ) / 255 294 ); 295 296 return $rgb; 253 297 } 254 298 255 299 $hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/'; 256 300 if ( preg_match( $hex6_regexp, $color_str, $match ) ) { 257 returnwp_tinycolor_rgb_to_rgb(301 $rgb = wp_tinycolor_rgb_to_rgb( 258 302 array( 259 303 'r' => base_convert( $match[1], 16, 10 ), … … 262 306 ) 263 307 ); 308 309 $rgb['a'] = 1; 310 311 return $rgb; 264 312 } 265 313 266 314 $hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/'; 267 315 if ( preg_match( $hex4_regexp, $color_str, $match ) ) { 268 returnwp_tinycolor_rgb_to_rgb(316 $rgb = wp_tinycolor_rgb_to_rgb( 269 317 array( 270 318 'r' => base_convert( $match[1] . $match[1], 16, 10 ), … … 273 321 ) 274 322 ); 323 324 $rgb['a'] = _wp_tinycolor_bound_alpha( 325 base_convert( $match[4] . $match[4], 16, 10 ) / 255 326 ); 327 328 return $rgb; 275 329 } 276 330 277 331 $hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/'; 278 332 if ( preg_match( $hex3_regexp, $color_str, $match ) ) { 279 returnwp_tinycolor_rgb_to_rgb(333 $rgb = wp_tinycolor_rgb_to_rgb( 280 334 array( 281 335 'r' => base_convert( $match[1] . $match[1], 16, 10 ), … … 283 337 'b' => base_convert( $match[3] . $match[3], 16, 10 ), 284 338 ) 339 ); 340 341 $rgb['a'] = 1; 342 343 return $rgb; 344 } 345 346 /* 347 * The JS color picker considers the string "transparent" to be a hex value, 348 * so we need to handle it here as a special case. 349 */ 350 if ( 'transparent' === $color_str ) { 351 return array( 352 'r' => 0, 353 'g' => 0, 354 'b' => 0, 355 'a' => 0, 285 356 ); 286 357 } … … 313 384 } 314 385 } 386 } 387 /** 388 * Renders the duotone filter SVG and returns the CSS filter property to 389 * reference the rendered SVG. 390 * 391 * @since 5.9.0 392 * 393 * @param array $preset Duotone preset value as seen in theme.json. 394 * @return string Duotone CSS filter property. 395 */ 396 function wp_render_duotone_filter_preset( $preset ) { 397 $duotone_id = $preset['slug']; 398 $duotone_colors = $preset['colors']; 399 $filter_id = 'wp-duotone-' . $duotone_id; 400 $duotone_values = array( 401 'r' => array(), 402 'g' => array(), 403 'b' => array(), 404 'a' => array(), 405 ); 406 foreach ( $duotone_colors as $color_str ) { 407 $color = wp_tinycolor_string_to_rgb( $color_str ); 408 409 $duotone_values['r'][] = $color['r'] / 255; 410 $duotone_values['g'][] = $color['g'] / 255; 411 $duotone_values['b'][] = $color['b'] / 255; 412 $duotone_values['a'][] = $color['a']; 413 } 414 415 ob_start(); 416 417 ?> 418 419 <svg 420 xmlns="http://www.w3.org/2000/svg" 421 viewBox="0 0 0 0" 422 width="0" 423 height="0" 424 focusable="false" 425 role="none" 426 style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;" 427 > 428 <defs> 429 <filter id="<?php echo esc_attr( $filter_id ); ?>"> 430 <feColorMatrix 431 color-interpolation-filters="sRGB" 432 type="matrix" 433 values=" 434 .299 .587 .114 0 0 435 .299 .587 .114 0 0 436 .299 .587 .114 0 0 437 .299 .587 .114 0 0 438 " 439 /> 440 <feComponentTransfer color-interpolation-filters="sRGB" > 441 <feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" /> 442 <feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" /> 443 <feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" /> 444 <feFuncA type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['a'] ) ); ?>" /> 445 </feComponentTransfer> 446 <feComposite in2="SourceGraphic" operator="in" /> 447 </filter> 448 </defs> 449 </svg> 450 451 <?php 452 453 $svg = ob_get_clean(); 454 455 if ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) { 456 // Clean up the whitespace. 457 $svg = preg_replace( "/[\r\n\t ]+/", ' ', $svg ); 458 $svg = preg_replace( '/> </', '><', $svg ); 459 $svg = trim( $svg ); 460 } 461 462 add_action( 463 /* 464 * Safari doesn't render SVG filters defined in data URIs, 465 * and SVG filters won't render in the head of a document, 466 * so the next best place to put the SVG is in the footer. 467 */ 468 is_admin() ? 'admin_footer' : 'wp_footer', 469 static function () use ( $svg ) { 470 echo $svg; 471 } 472 ); 473 474 return "url('#" . $filter_id . "')"; 315 475 } 316 476 -
trunk/src/wp-includes/class-wp-theme-json-resolver.php
r51693 r52049 12 12 * for site-level config and offers an API to work with them. 13 13 * 14 * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes). 15 * This is a low-level API that may need to do breaking changes. Please, 16 * use get_global_settings, get_global_styles, and get_global_stylesheet instead. 17 * 14 18 * @access private 15 19 */ … … 41 45 42 46 /** 47 * Container for data coming from the user. 48 * 49 * @since 5.9.0 50 * @var WP_Theme_JSON 51 */ 52 private static $user = null; 53 54 /** 55 * Stores the ID of the custom post type 56 * that holds the user data. 57 * 58 * @since 5.9.0 59 * @var integer 60 */ 61 private static $user_custom_post_type_id = null; 62 63 /** 43 64 * Container to keep loaded i18n schema for `theme.json`. 44 65 * 45 * @since 5.9.0 66 * @since 5.8.0 67 * @since 5.9.0 Renamed from $theme_json_i18n 46 68 * @var array 47 69 */ … … 123 145 * Returns the theme's data. 124 146 * 125 * Data from theme.json can be augmented via the $theme_support_data variable. 126 * This is useful, for example, to backfill the gaps in theme.json that a theme 127 * has declared via add_theme_supports. 128 * 129 * Note that if the same data is present in theme.json and in $theme_support_data, 130 * the theme.json's is not overwritten. 131 * 132 * @since 5.8.0 133 * 134 * @param array $theme_support_data Optional. Theme support data in theme.json format. 135 * Default empty array. 147 * Data from theme.json will be backfilled from existing 148 * theme supports, if any. Note that if the same data 149 * is present in theme.json and in theme supports, 150 * the theme.json takes precendence. 151 * 152 * @since 5.8.0 153 * @since 5.9.0 Theme supports have been inlined and the argument removed. 154 * 136 155 * @return WP_Theme_JSON Entity that holds theme data. 137 156 */ 138 public static function get_theme_data( $theme_support_data = array() ) { 157 public static function get_theme_data( $deprecated = array() ) { 158 if ( ! empty( $deprecated ) ) { 159 _deprecated_argument( __METHOD__, '5.9' ); 160 } 139 161 if ( null === self::$theme ) { 140 162 $theme_json_data = self::read_json_file( self::get_file_path_from_theme( 'theme.json' ) ); 141 163 $theme_json_data = self::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) ); 142 164 self::$theme = new WP_Theme_JSON( $theme_json_data ); 143 } 144 145 if ( empty( $theme_support_data ) ) { 146 return self::$theme; 165 166 if ( wp_get_theme()->parent() ) { 167 // Get parent theme.json. 168 $parent_theme_json_data = self::read_json_file( self::get_file_path_from_theme( 'theme.json', true ) ); 169 $parent_theme_json_data = self::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) ); 170 $parent_theme = new WP_Theme_JSON( $parent_theme_json_data ); 171 172 // Merge the child theme.json into the parent theme.json. 173 // The child theme takes precedence over the parent. 174 $parent_theme->merge( self::$theme ); 175 self::$theme = $parent_theme; 176 } 147 177 } 148 178 149 179 /* 150 * We want the presets and settings declared in theme.json 151 * to override the ones declared via add_theme_support. 152 */ 180 * We want the presets and settings declared in theme.json 181 * to override the ones declared via theme supports. 182 * So we take theme supports, transform it to theme.json shape 183 * and merge the self::$theme upon that. 184 */ 185 $theme_support_data = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() ); 153 186 $with_theme_supports = new WP_Theme_JSON( $theme_support_data ); 154 187 $with_theme_supports->merge( self::$theme ); … … 158 191 159 192 /** 160 * There are different sources of data for a site: core and theme. 161 * 162 * While the getters {@link get_core_data}, {@link get_theme_data} return the raw data 163 * from the respective origins, this method merges them all together. 164 * 165 * If the same piece of data is declared in different origins (core and theme), 166 * the last origin overrides the previous. For example, if core disables custom colors 167 * but a theme enables them, the theme config wins. 168 * 169 * @since 5.8.0 170 * 171 * @param array $settings Optional. Existing block editor settings. Default empty array. 193 * Returns the CPT that contains the user's origin config 194 * for the current theme or a void array if none found. 195 * 196 * It can also create and return a new draft CPT. 197 * 198 * @since 5.9.0 199 * 200 * @param bool $should_create_cpt Optional. Whether a new CPT should be created if no one was found. 201 * False by default. 202 * @param array $post_status_filter Filter Optional. CPT by post status. 203 * ['publish'] by default, so it only fetches published posts. 204 * 205 * @return array Custom Post Type for the user's origin config. 206 */ 207 private static function get_user_data_from_custom_post_type( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) { 208 $user_cpt = array(); 209 $post_type_filter = 'wp_global_styles'; 210 $query = new WP_Query( 211 array( 212 'posts_per_page' => 1, 213 'orderby' => 'date', 214 'order' => 'desc', 215 'post_type' => $post_type_filter, 216 'post_status' => $post_status_filter, 217 'tax_query' => array( 218 array( 219 'taxonomy' => 'wp_theme', 220 'field' => 'name', 221 'terms' => wp_get_theme()->get_stylesheet(), 222 ), 223 ), 224 ) 225 ); 226 227 if ( is_array( $query->posts ) && ( 1 === $query->post_count ) ) { 228 $user_cpt = $query->posts[0]->to_array(); 229 } elseif ( $should_create_cpt ) { 230 $cpt_post_id = wp_insert_post( 231 array( 232 'post_content' => '{"version": ' . WP_Theme_JSON::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', 233 'post_status' => 'publish', 234 'post_title' => __( 'Custom Styles', 'default' ), 235 'post_type' => $post_type_filter, 236 'post_name' => 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ), 237 'tax_input' => array( 238 'wp_theme' => array( wp_get_theme()->get_stylesheet() ), 239 ), 240 ), 241 true 242 ); 243 244 if ( is_wp_error( $cpt_post_id ) ) { 245 $user_cpt = array(); 246 } else { 247 $user_cpt = get_post( $cpt_post_id, ARRAY_A ); 248 } 249 } 250 251 return $user_cpt; 252 } 253 254 /** 255 * Returns the user's origin config. 256 * 257 * @since 5.9.0 258 * 259 * @return WP_Theme_JSON Entity that holds user data. 260 */ 261 public static function get_user_data() { 262 if ( null !== self::$user ) { 263 return self::$user; 264 } 265 266 $config = array(); 267 $user_cpt = self::get_user_data_from_custom_post_type(); 268 269 if ( array_key_exists( 'post_content', $user_cpt ) ) { 270 $decoded_data = json_decode( $user_cpt['post_content'], true ); 271 272 $json_decoding_error = json_last_error(); 273 if ( JSON_ERROR_NONE !== $json_decoding_error ) { 274 trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() ); 275 return new WP_Theme_JSON( $config, 'user' ); 276 } 277 278 // Very important to verify if the flag isGlobalStylesUserThemeJSON is true. 279 // If is not true the content was not escaped and is not safe. 280 if ( 281 is_array( $decoded_data ) && 282 isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) && 283 $decoded_data['isGlobalStylesUserThemeJSON'] 284 ) { 285 unset( $decoded_data['isGlobalStylesUserThemeJSON'] ); 286 $config = $decoded_data; 287 } 288 } 289 self::$user = new WP_Theme_JSON( $config, 'user' ); 290 291 return self::$user; 292 } 293 294 /** 295 * There are three sources of data (origins) for a site: 296 * core, theme, and user. The user's has higher priority 297 * than the theme's, and the theme's higher than core's. 298 * 299 * Unlike the getters {@link get_core_data}, 300 * {@link get_theme_data}, and {@link get_user_data}, 301 * this method returns data after it has been merged 302 * with the previous origins. This means that if the same piece of data 303 * is declared in different origins (user, theme, and core), 304 * the last origin overrides the previous. 305 * 306 * For example, if the user has set a background color 307 * for the paragraph block, and the theme has done it as well, 308 * the user preference wins. 309 * 310 * @since 5.8.0 311 * @since 5.9.0 Add user data and change the arguments. 312 * 313 * @param string $origin Optional. To what level should we merge data. 314 * Valid values are 'theme' or 'user'. 315 * Default is 'user'. 172 316 * @return WP_Theme_JSON 173 317 */ 174 public static function get_merged_data( $settings = array() ) { 175 $theme_support_data = WP_Theme_JSON::get_from_editor_settings( $settings ); 318 public static function get_merged_data( $origin = 'user' ) { 319 if ( is_array( $origin ) ) { 320 _deprecated_argument( __FUNCTION__, '5.9' ); 321 } 176 322 177 323 $result = new WP_Theme_JSON(); 178 324 $result->merge( self::get_core_data() ); 179 $result->merge( self::get_theme_data( $theme_support_data ) ); 325 $result->merge( self::get_theme_data() ); 326 327 if ( 'user' === $origin ) { 328 $result->merge( self::get_user_data() ); 329 } 180 330 181 331 return $result; … … 183 333 184 334 /** 335 * Returns the ID of the custom post type 336 * that stores user data. 337 * 338 * @since 5.9.0 339 * 340 * @return integer|null 341 */ 342 public static function get_user_custom_post_type_id() { 343 if ( null !== self::$user_custom_post_type_id ) { 344 return self::$user_custom_post_type_id; 345 } 346 347 $user_cpt = self::get_user_data_from_custom_post_type( true ); 348 349 if ( array_key_exists( 'ID', $user_cpt ) ) { 350 self::$user_custom_post_type_id = $user_cpt['ID']; 351 } 352 353 return self::$user_custom_post_type_id; 354 } 355 356 /** 185 357 * Whether the current theme has a theme.json file. 186 358 * 187 359 * @since 5.8.0 360 * @since 5.9.0 Also check in the parent theme. 188 361 * 189 362 * @return bool … … 191 364 public static function theme_has_support() { 192 365 if ( ! isset( self::$theme_has_support ) ) { 193 self::$theme_has_support = (bool) self::get_file_path_from_theme( 'theme.json');366 self::$theme_has_support = is_readable( get_theme_file_path( 'theme.json' ) ); 194 367 } 195 368 … … 203 376 * 204 377 * @since 5.8.0 378 * @since 5.9.0 Adapt to work with child themes. 205 379 * 206 380 * @param string $file_name Name of the file. 381 * @param bool $template Optional. Use template theme directory. Default false. 207 382 * @return string The whole file path or empty if the file doesn't exist. 208 383 */ 209 private static function get_file_path_from_theme( $file_name ) { 210 /* 211 * This used to be a locate_template call. However, that method proved problematic 212 * due to its use of constants (STYLESHEETPATH) that threw errors in some scenarios. 213 * 214 * When the theme.json merge algorithm properly supports child themes, 215 * this should also fall back to the template path, as locate_template did. 216 */ 217 $located = ''; 218 $candidate = get_stylesheet_directory() . '/' . $file_name; 219 if ( is_readable( $candidate ) ) { 220 $located = $candidate; 221 } 222 return $located; 384 private static function get_file_path_from_theme( $file_name, $template = false ) { 385 $path = $template ? get_template_directory() : get_stylesheet_directory(); 386 $candidate = $path . '/' . $file_name; 387 388 return is_readable( $candidate ) ? $candidate : ''; 223 389 } 224 390 … … 227 393 * 228 394 * @since 5.8.0 395 * @since 5.9.0 Added new variables to reset. 229 396 */ 230 397 public static function clean_cached_data() { 231 self::$core = null; 232 self::$theme = null; 233 self::$theme_has_support = null; 398 self::$core = null; 399 self::$theme = null; 400 self::$user = null; 401 self::$user_custom_post_type_id = null; 402 self::$theme_has_support = null; 403 self::$i18n_schema = null; 234 404 } 235 405 -
trunk/src/wp-includes/class-wp-theme-json.php
r51657 r52049 10 10 /** 11 11 * Class that encapsulates the processing of structures that adhere to the theme.json spec. 12 * 13 * This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes). 14 * This is a low-level API that may need to do breaking changes. Please, 15 * use get_global_settings, get_global_styles, and get_global_stylesheet instead. 12 16 * 13 17 * @access private … … 71 75 * This contains the necessary metadata to process them: 72 76 * 73 * - path => where to find the preset within the settings section 74 * 75 * - value_key => the key that represents the value 76 * 77 * - css_var_infix => infix to use in generating the CSS Custom Property. Example: 78 * --wp--preset--<preset_infix>--<slug>: <preset_value> 79 * 80 * - classes => array containing a structure with the classes to 81 * generate for the presets. Each class should have 82 * the class suffix and the property name. Example: 83 * 84 * .has-<slug>-<class_suffix> { 85 * <property_name>: <preset_value> 86 * } 87 * 88 * @since 5.8.0 77 * - path => where to find the preset within the settings section 78 * - value_key => the key that represents the value 79 * - value_func => the callback to render the value (either value_key or value_func should be present) 80 * - css_vars => template string to use in generating the CSS Custom Property. 81 * Example output: "--wp--preset--duotone--blue: <value>" will generate as many CSS Custom Properties as presets defined 82 * substituting the $slug for the slug's value for each preset value. 83 * - classes => array containing a structure with the classes to generate for the presets. 84 * Each key is a template string to resolve similarly to the css_vars and each value is the CSS property to use for that class. 85 * Example output: ".has-blue-color { color: <value> }" 86 * - properties => a list of CSS properties to be used by kses to check the preset value is safe. 87 * 88 * @since 5.8.0 89 * @since 5.9.0 Added new presets and simplified the metadata structure. 89 90 * @var array 90 91 */ 91 92 const PRESETS_METADATA = array( 92 93 array( 93 'path' => array( 'color', 'palette' ), 94 'value_key' => 'color', 95 'css_var_infix' => 'color', 96 'classes' => array( 97 array( 98 'class_suffix' => 'color', 99 'property_name' => 'color', 100 ), 101 array( 102 'class_suffix' => 'background-color', 103 'property_name' => 'background-color', 104 ), 94 'path' => array( 'color', 'palette' ), 95 'value_key' => 'color', 96 'css_vars' => '--wp--preset--color--$slug', 97 'classes' => array( 98 '.has-$slug-color' => 'color', 99 '.has-$slug-background-color' => 'background-color', 100 '.has-$slug-border-color' => 'border-color', 105 101 ), 102 'properties' => array( 'color', 'background-color', 'border-color' ), 106 103 ), 107 104 array( 108 'path' => array( 'color', 'gradients' ), 109 'value_key' => 'gradient', 110 'css_var_infix' => 'gradient', 111 'classes' => array( 112 array( 113 'class_suffix' => 'gradient-background', 114 'property_name' => 'background', 115 ), 116 ), 105 'path' => array( 'color', 'gradients' ), 106 'value_key' => 'gradient', 107 'css_vars' => '--wp--preset--gradient--$slug', 108 'classes' => array( '.has-$slug-gradient-background' => 'background' ), 109 'properties' => array( 'background' ), 117 110 ), 118 111 array( 119 'path' => array( 'typography', 'fontSizes' ), 120 'value_key' => 'size', 121 'css_var_infix' => 'font-size', 122 'classes' => array( 123 array( 124 'class_suffix' => 'font-size', 125 'property_name' => 'font-size', 126 ), 127 ), 112 'path' => array( 'color', 'duotone' ), 113 'value_func' => 'wp_render_duotone_filter_preset', 114 'css_vars' => '--wp--preset--duotone--$slug', 115 'classes' => array(), 116 'properties' => array( 'filter' ), 117 ), 118 array( 119 'path' => array( 'typography', 'fontSizes' ), 120 'value_key' => 'size', 121 'css_vars' => '--wp--preset--font-size--$slug', 122 'classes' => array( '.has-$slug-font-size' => 'font-size' ), 123 'properties' => array( 'font-size' ), 124 ), 125 array( 126 'path' => array( 'typography', 'fontFamilies' ), 127 'value_key' => 'fontFamily', 128 'css_vars' => '--wp--preset--font-family--$slug', 129 'classes' => array( '.has-$slug-font-family' => 'font-family' ), 130 'properties' => array( 'font-family' ), 128 131 ), 129 132 ); … … 132 135 * Metadata for style properties. 133 136 * 134 * Each property declares:135 * 136 * - 'value': path to the value in theme.json and block attributes.137 * 138 * @since 5. 8.0137 * Each element is a direct mapping from the CSS property name to the 138 * path to the value in theme.json & block attributes. 139 * 140 * @since 5.8.0 141 * @since 5.9.0 Added new properties and simplified the metadata structure. 139 142 * @var array 140 143 */ 141 144 const PROPERTIES_METADATA = array( 142 'background' => array( 143 'value' => array( 'color', 'gradient' ), 144 ), 145 'background-color' => array( 146 'value' => array( 'color', 'background' ), 147 ), 148 'color' => array( 149 'value' => array( 'color', 'text' ), 150 ), 151 'font-size' => array( 152 'value' => array( 'typography', 'fontSize' ), 153 ), 154 'line-height' => array( 155 'value' => array( 'typography', 'lineHeight' ), 156 ), 157 'margin' => array( 158 'value' => array( 'spacing', 'margin' ), 159 'properties' => array( 'top', 'right', 'bottom', 'left' ), 160 ), 161 'padding' => array( 162 'value' => array( 'spacing', 'padding' ), 163 'properties' => array( 'top', 'right', 'bottom', 'left' ), 164 ), 145 'background' => array( 'color', 'gradient' ), 146 'background-color' => array( 'color', 'background' ), 147 'border-radius' => array( 'border', 'radius' ), 148 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), 149 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), 150 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), 151 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), 152 'border-color' => array( 'border', 'color' ), 153 'border-width' => array( 'border', 'width' ), 154 'border-style' => array( 'border', 'style' ), 155 'color' => array( 'color', 'text' ), 156 'font-family' => array( 'typography', 'fontFamily' ), 157 'font-size' => array( 'typography', 'fontSize' ), 158 'font-style' => array( 'typography', 'fontStyle' ), 159 'font-weight' => array( 'typography', 'fontWeight' ), 160 'letter-spacing' => array( 'typography', 'letterSpacing' ), 161 'line-height' => array( 'typography', 'lineHeight' ), 162 'margin' => array( 'spacing', 'margin' ), 163 'margin-top' => array( 'spacing', 'margin', 'top' ), 164 'margin-right' => array( 'spacing', 'margin', 'right' ), 165 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), 166 'margin-left' => array( 'spacing', 'margin', 'left' ), 167 'padding' => array( 'spacing', 'padding' ), 168 'padding-top' => array( 'spacing', 'padding', 'top' ), 169 'padding-right' => array( 'spacing', 'padding', 'right' ), 170 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), 171 'padding-left' => array( 'spacing', 'padding', 'left' ), 172 '--wp--style--block-gap' => array( 'spacing', 'blockGap' ), 173 'text-decoration' => array( 'typography', 'textDecoration' ), 174 'text-transform' => array( 'typography', 'textTransform' ), 175 'filter' => array( 'filter', 'duotone' ), 165 176 ); 166 177 167 178 /** 168 * @since 5.8.0 179 * Protected style properties. 180 * 181 * These style properties are only rendered if a setting enables it 182 * via a value other than `null`. 183 * 184 * Each element maps the style property to the corresponding theme.json 185 * setting key. 186 * 187 * @since 5.9.0 188 */ 189 const PROTECTED_PROPERTIES = array( 190 'spacing.blockGap' => array( 'spacing', 'blockGap' ), 191 ); 192 193 /** 194 * The top-level keys a theme.json can have. 195 * 196 * @since 5.8.0 197 * @since 5.9.0 Renamed from ALLOWED_TOP_LEVEL_KEYS and added new values. 169 198 * @var string[] 170 199 */ 171 const ALLOWED_TOP_LEVEL_KEYS = array( 200 const VALID_TOP_LEVEL_KEYS = array( 201 'customTemplates', 172 202 'settings', 173 203 'styles', 204 'templateParts', 174 205 'version', 175 206 ); 176 207 177 208 /** 178 * @since 5.8.0 209 * The valid properties under the settings key. 210 * 211 * @since 5.8.0 212 * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties, and renamed others according to the new schema. 179 213 * @var array 180 214 */ 181 const ALLOWED_SETTINGS = array(215 const VALID_SETTINGS = array( 182 216 'border' => array( 183 'customRadius' => null, 217 'color' => null, 218 'radius' => null, 219 'style' => null, 220 'width' => null, 184 221 ), 185 222 'color' => array( 223 'background' => null, 186 224 'custom' => null, 187 225 'customDuotone' => null, … … 191 229 'link' => null, 192 230 'palette' => null, 231 'text' => null, 193 232 ), 194 233 'custom' => null, … … 198 237 ), 199 238 'spacing' => array( 200 'customMargin' => null, 201 'customPadding' => null, 202 'units' => null, 239 'blockGap' => null, 240 'margin' => null, 241 'padding' => null, 242 'units' => null, 203 243 ), 204 244 'typography' => array( 205 'customFontSize' => null, 206 'customLineHeight' => null, 207 'dropCap' => null, 208 'fontSizes' => null, 245 'customFontSize' => null, 246 'dropCap' => null, 247 'fontFamilies' => null, 248 'fontSizes' => null, 249 'fontStyle' => null, 250 'fontWeight' => null, 251 'letterSpacing' => null, 252 'lineHeight' => null, 253 'textDecoration' => null, 254 'textTransform' => null, 209 255 ), 210 256 ); 211 257 212 258 /** 213 * @since 5.8.0 259 * The valid properties under the styles key. 260 * 261 * @since 5.8.0 262 * @since 5.9.0 Renamed from ALLOWED_SETTINGS, gained new properties. 214 263 * @var array 215 264 */ 216 const ALLOWED_STYLES = array(265 const VALID_STYLES = array( 217 266 'border' => array( 267 'color' => null, 218 268 'radius' => null, 269 'style' => null, 270 'width' => null, 219 271 ), 220 272 'color' => array( … … 223 275 'text' => null, 224 276 ), 277 'filter' => array( 278 'duotone' => null, 279 ), 225 280 'spacing' => array( 226 'margin' => array( 227 'top' => null, 228 'right' => null, 229 'bottom' => null, 230 'left' => null, 231 ), 232 'padding' => array( 233 'bottom' => null, 234 'left' => null, 235 'right' => null, 236 'top' => null, 237 ), 281 'margin' => null, 282 'padding' => null, 283 'blockGap' => null, 238 284 ), 239 285 'typography' => array( 240 'fontSize' => null, 241 'lineHeight' => null, 286 'fontFamily' => null, 287 'fontSize' => null, 288 'fontStyle' => null, 289 'fontWeight' => null, 290 'letterSpacing' => null, 291 'lineHeight' => null, 292 'textDecoration' => null, 293 'textTransform' => null, 242 294 ), 243 295 ); … … 258 310 259 311 /** 260 * @since 5.8.0 312 * The latest version of the schema in use. 313 * 314 * @since 5.8.0 315 * @since 5.9.0 Changed value. 261 316 * @var int 262 317 */ 263 const LATEST_SCHEMA = 1;318 const LATEST_SCHEMA = 2; 264 319 265 320 /** … … 277 332 } 278 333 279 if ( ! isset( $theme_json['version'] ) || self::LATEST_SCHEMA !== $theme_json['version'] ) { 280 $this->theme_json = array(); 281 return; 282 } 283 284 $this->theme_json = self::sanitize( $theme_json ); 334 $this->theme_json = WP_Theme_JSON_Schema::migrate( $theme_json ); 335 $valid_block_names = array_keys( self::get_blocks_metadata() ); 336 $valid_element_names = array_keys( self::ELEMENTS ); 337 $this->theme_json = self::sanitize( $this->theme_json, $valid_block_names, $valid_element_names ); 285 338 286 339 // Internally, presets are keyed by origin. 287 340 $nodes = self::get_setting_nodes( $this->theme_json ); 288 341 foreach ( $nodes as $node ) { 289 foreach ( self::PRESETS_METADATA as $preset ) {290 $path = array_merge( $node['path'], $preset ['path'] );342 foreach ( self::PRESETS_METADATA as $preset_metadata ) { 343 $path = array_merge( $node['path'], $preset_metadata['path'] ); 291 344 $preset = _wp_array_get( $this->theme_json, $path, null ); 292 345 if ( null !== $preset ) { … … 301 354 * 302 355 * @since 5.8.0 356 * @since 5.9.0 Has new parameters. 303 357 * 304 358 * @param array $input Structure to sanitize. 359 * @param array $valid_block_names List of valid block names. 360 * @param array $valid_element_names List of valid element names. 305 361 * @return array The sanitized output. 306 362 */ 307 private static function sanitize( $input ) {363 private static function sanitize( $input, $valid_block_names, $valid_element_names ) { 308 364 $output = array(); 309 365 … … 312 368 } 313 369 314 $allowed_top_level_keys = self::ALLOWED_TOP_LEVEL_KEYS; 315 $allowed_settings = self::ALLOWED_SETTINGS; 316 $allowed_styles = self::ALLOWED_STYLES; 317 $allowed_blocks = array_keys( self::get_blocks_metadata() ); 318 $allowed_elements = array_keys( self::ELEMENTS ); 319 320 $output = array_intersect_key( $input, array_flip( $allowed_top_level_keys ) ); 321 322 // Build the schema. 370 $output = array_intersect_key( $input, array_flip( self::VALID_TOP_LEVEL_KEYS ) ); 371 372 // Build the schema based on valid block & element names. 323 373 $schema = array(); 324 374 $schema_styles_elements = array(); 325 foreach ( $ allowed_elements as $element ) {326 $schema_styles_elements[ $element ] = $allowed_styles;375 foreach ( $valid_element_names as $element ) { 376 $schema_styles_elements[ $element ] = self::VALID_STYLES; 327 377 } 328 378 $schema_styles_blocks = array(); 329 379 $schema_settings_blocks = array(); 330 foreach ( $ allowed_blocks as $block ) {331 $schema_settings_blocks[ $block ] = $allowed_settings;332 $schema_styles_blocks[ $block ] = $allowed_styles;380 foreach ( $valid_block_names as $block ) { 381 $schema_settings_blocks[ $block ] = self::VALID_SETTINGS; 382 $schema_styles_blocks[ $block ] = self::VALID_STYLES; 333 383 $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; 334 384 } 335 $schema['styles'] = $allowed_styles;385 $schema['styles'] = self::VALID_STYLES; 336 386 $schema['styles']['blocks'] = $schema_styles_blocks; 337 387 $schema['styles']['elements'] = $schema_styles_elements; 338 $schema['settings'] = $allowed_settings;388 $schema['settings'] = self::VALID_SETTINGS; 339 389 $schema['settings']['blocks'] = $schema_settings_blocks; 340 390 … … 361 411 return $output; 362 412 } 363 364 413 /** 365 414 * Returns the metadata for each block. … … 378 427 * 'selector': 'h1', 379 428 * 'elements': {} 380 * } 381 * 'core/group': { 382 * 'selector': '.wp-block-group', 429 * }, 430 * 'core/image': { 431 * 'selector': '.wp-block-image', 432 * 'duotone': 'img', 383 433 * 'elements': {} 384 434 * } … … 386 436 * 387 437 * @since 5.8.0 438 * @since 5.9.0 Added duotone key with CSS selector. 388 439 * 389 440 * @return array Block metadata. … … 408 459 } 409 460 410 /* 411 * Assign defaults, then overwrite those that the block sets by itself. 412 * If the block selector is compounded, will append the element to each 413 * individual block selector. 414 */ 461 if ( 462 isset( $block_type->supports['color']['__experimentalDuotone'] ) && 463 is_string( $block_type->supports['color']['__experimentalDuotone'] ) 464 ) { 465 self::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone']; 466 } 467 468 // Assign defaults, then overwrite those that the block sets by itself. 469 // If the block selector is compounded, will append the element to each 470 // individual block selector. 415 471 $block_selectors = explode( ',', self::$blocks_metadata[ $block_name ]['selector'] ); 416 472 foreach ( self::ELEMENTS as $el_name => $el_selector ) { … … 494 550 * 495 551 * @since 5.8.0 496 * 497 * @param string $type Optional. Type of stylesheet we want. Accepts 'all', 498 * 'block_styles', and 'css_variables'. Default 'all'. 552 * @since 5.9.0 Changed the arguments passed to the function. 553 * 554 * @param array $types Types of styles to load. Will load all by default. It accepts: 555 * 'variables': only the CSS Custom Properties for presets & custom ones. 556 * 'styles': only the styles section in theme.json. 557 * 'presets': only the classes for the presets. 558 * @param array $origins A list of origins to include. By default it includes self::VALID_ORIGINS. 499 559 * @return string Stylesheet. 500 560 */ 501 public function get_stylesheet( $type = 'all' ) { 561 public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = self::VALID_ORIGINS ) { 562 if ( is_string( $types ) ) { 563 // Dispatch error and map old arguments to new ones. 564 _deprecated_argument( __FUNCTION__, '5.9' ); 565 if ( 'block_styles' === $types ) { 566 $types = array( 'styles', 'presets' ); 567 } elseif ( 'css_variables' === $types ) { 568 $types = array( 'variables' ); 569 } else { 570 $types = array( 'variables', 'styles', 'presets' ); 571 } 572 } 573 502 574 $blocks_metadata = self::get_blocks_metadata(); 503 575 $style_nodes = self::get_style_nodes( $this->theme_json, $blocks_metadata ); 504 576 $setting_nodes = self::get_setting_nodes( $this->theme_json, $blocks_metadata ); 505 577 506 switch ( $type ) { 507 case 'block_styles': 508 return $this->get_block_styles( $style_nodes, $setting_nodes ); 509 case 'css_variables': 510 return $this->get_css_variables( $setting_nodes ); 511 default: 512 return $this->get_css_variables( $setting_nodes ) . $this->get_block_styles( $style_nodes, $setting_nodes ); 513 } 514 578 $stylesheet = ''; 579 580 if ( in_array( 'variables', $types, true ) ) { 581 $stylesheet .= $this->get_css_variables( $setting_nodes, $origins ); 582 } 583 584 if ( in_array( 'styles', $types, true ) ) { 585 $stylesheet .= $this->get_block_classes( $style_nodes ); 586 } 587 588 if ( in_array( 'presets', $types, true ) ) { 589 $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins ); 590 } 591 592 return $stylesheet; 593 } 594 595 /** 596 * Returns the page templates of the current theme. 597 * 598 * @since 5.9.0 599 * 600 * @return array 601 */ 602 public function get_custom_templates() { 603 $custom_templates = array(); 604 if ( ! isset( $this->theme_json['customTemplates'] ) ) { 605 return $custom_templates; 606 } 607 608 foreach ( $this->theme_json['customTemplates'] as $item ) { 609 if ( isset( $item['name'] ) ) { 610 $custom_templates[ $item['name'] ] = array( 611 'title' => isset( $item['title'] ) ? $item['title'] : '', 612 'postTypes' => isset( $item['postTypes'] ) ? $item['postTypes'] : array( 'page' ), 613 ); 614 } 615 } 616 return $custom_templates; 617 } 618 619 /** 620 * Returns the template part data of current theme. 621 * 622 * @since 5.9.0 623 * 624 * @return array 625 */ 626 public function get_template_parts() { 627 $template_parts = array(); 628 if ( ! isset( $this->theme_json['templateParts'] ) ) { 629 return $template_parts; 630 } 631 632 foreach ( $this->theme_json['templateParts'] as $item ) { 633 if ( isset( $item['name'] ) ) { 634 $template_parts[ $item['name'] ] = array( 635 'title' => isset( $item['title'] ) ? $item['title'] : '', 636 'area' => isset( $item['area'] ) ? $item['area'] : '', 637 ); 638 } 639 } 640 return $template_parts; 515 641 } 516 642 … … 527 653 * } 528 654 * 529 * Additionally, it'll also create new rulesets 530 * as classes for each preset value such as: 531 * 532 * .has-value-color { 533 * color: value; 534 * } 535 * 536 * .has-value-background-color { 537 * background-color: value; 538 * } 539 * 540 * .has-value-font-size { 541 * font-size: value; 542 * } 543 * 544 * .has-value-gradient-background { 545 * background: value; 546 * } 547 * 548 * p.has-value-gradient-background { 549 * background: value; 550 * } 551 * 552 * @since 5.8.0 553 * 554 * @param array $style_nodes Nodes with styles. 555 * @param array $setting_nodes Nodes with settings. 655 * @since 5.8.0 656 * @since 5.9.0 Renamed to get_block_classes and no longer returns preset classes. 657 * 658 * @param array $style_nodes Nodes with styles. 556 659 * @return string The new stylesheet. 557 660 */ 558 private function get_block_ styles( $style_nodes, $setting_nodes ) {661 private function get_block_classes( $style_nodes ) { 559 662 $block_rules = ''; 663 560 664 foreach ( $style_nodes as $metadata ) { 561 665 if ( null === $metadata['selector'] ) { … … 565 669 $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); 566 670 $selector = $metadata['selector']; 567 $declarations = self::compute_style_properties( $node ); 671 $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); 672 $declarations = self::compute_style_properties( $node, $settings ); 673 674 // 1. Separate the ones who use the general selector 675 // and the ones who use the duotone selector. 676 $declarations_duotone = array(); 677 foreach ( $declarations as $index => $declaration ) { 678 if ( 'filter' === $declaration['name'] ) { 679 unset( $declarations[ $index ] ); 680 $declarations_duotone[] = $declaration; 681 } 682 } 683 684 // 2. Generate the rules that use the general selector. 568 685 $block_rules .= self::to_ruleset( $selector, $declarations ); 569 } 570 686 687 // 3. Generate the rules that use the duotone selector. 688 if ( isset( $metadata['duotone'] ) && ! empty( $declarations_duotone ) ) { 689 $selector_duotone = self::scope_selector( $metadata['selector'], $metadata['duotone'] ); 690 $block_rules .= self::to_ruleset( $selector_duotone, $declarations_duotone ); 691 } 692 693 if ( self::ROOT_BLOCK_SELECTOR === $selector ) { 694 $block_rules .= 'body { margin: 0; }'; 695 $block_rules .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; 696 $block_rules .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; 697 $block_rules .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; 698 699 $has_block_gap_support = _wp_array_get( $this->theme_json, array( 'settings', 'spacing', 'blockGap' ) ) !== null; 700 if ( $has_block_gap_support ) { 701 $block_rules .= '.wp-site-blocks > * { margin-top: 0; margin-bottom: 0; }'; 702 $block_rules .= '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }'; 703 } 704 } 705 } 706 707 return $block_rules; 708 } 709 710 /** 711 * Creates new rulesets as classes for each preset value such as: 712 * 713 * .has-value-color { 714 * color: value; 715 * } 716 * 717 * .has-value-background-color { 718 * background-color: value; 719 * } 720 * 721 * .has-value-font-size { 722 * font-size: value; 723 * } 724 * 725 * .has-value-gradient-background { 726 * background: value; 727 * } 728 * 729 * p.has-value-gradient-background { 730 * background: value; 731 * } 732 * 733 * @since 5.9.0 734 * 735 * @param array $setting_nodes Nodes with settings. 736 * @param array $origins List of origins to process presets from. 737 * @return string The new stylesheet. 738 */ 739 private function get_preset_classes( $setting_nodes, $origins ) { 571 740 $preset_rules = ''; 741 572 742 foreach ( $setting_nodes as $metadata ) { 573 743 if ( null === $metadata['selector'] ) { … … 577 747 $selector = $metadata['selector']; 578 748 $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); 579 $preset_rules .= self::compute_preset_classes( $node, $selector );580 } 581 582 return $ block_rules . $preset_rules;749 $preset_rules .= self::compute_preset_classes( $node, $selector, $origins ); 750 } 751 752 return $preset_rules; 583 753 } 584 754 … … 598 768 * 599 769 * @since 5.8.0 770 * @since 5.9.0 Added origins parameter. 600 771 * 601 772 * @param array $nodes Nodes with settings. 773 * @param array $origins List of origins to process. 602 774 * @return string The new stylesheet. 603 775 */ 604 private function get_css_variables( $nodes ) {776 private function get_css_variables( $nodes, $origins ) { 605 777 $stylesheet = ''; 606 778 foreach ( $nodes as $metadata ) { … … 612 784 613 785 $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); 614 $declarations = array_merge( self::compute_preset_vars( $node ), self::compute_theme_vars( $node ) );786 $declarations = array_merge( self::compute_preset_vars( $node, $origins ), self::compute_theme_vars( $node ) ); 615 787 616 788 $stylesheet .= self::to_ruleset( $selector, $declarations ); … … 669 841 670 842 /** 671 * Given an array of presets keyed by origin and the value key of the preset,672 * it returns an array where each key is the preset slug and each value the preset value.673 *674 * @since 5.8.0675 *676 * @param array $preset_per_origin Array of presets keyed by origin.677 * @param string $value_key The property of the preset that contains its value.678 * @return array Array of presets where each key is a slug and each value is the preset value.679 */680 private static function get_merged_preset_by_slug( $preset_per_origin, $value_key ) {681 $result = array();682 foreach ( self::VALID_ORIGINS as $origin ) {683 if ( ! isset( $preset_per_origin[ $origin ] ) ) {684 continue;685 }686 foreach ( $preset_per_origin[ $origin ] as $preset ) {687 /*688 * We don't want to use kebabCase here,689 * see https://github.com/WordPress/gutenberg/issues/32347690 * However, we need to make sure the generated class or CSS variable691 * doesn't contain spaces.692 */693 $result[ preg_replace( '/\s+/', '-', $preset['slug'] ) ] = $preset[ $value_key ];694 }695 }696 return $result;697 }698 699 /**700 843 * Given a settings array, it returns the generated rulesets 701 844 * for the preset classes. 702 845 * 703 846 * @since 5.8.0 847 * @since 5.9.0 Added origins parameter. 704 848 * 705 849 * @param array $settings Settings to process. 706 850 * @param string $selector Selector wrapping the classes. 851 * @param array $origins List of origins to process. 707 852 * @return string The result of processing the presets. 708 853 */ 709 private static function compute_preset_classes( $settings, $selector ) {854 private static function compute_preset_classes( $settings, $selector, $origins ) { 710 855 if ( self::ROOT_BLOCK_SELECTOR === $selector ) { 711 856 // Classes at the global level do not need any CSS prefixed, … … 715 860 716 861 $stylesheet = ''; 717 foreach ( self::PRESETS_METADATA as $preset ) { 718 $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() ); 719 $preset_by_slug = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] ); 720 foreach ( $preset['classes'] as $class ) { 721 foreach ( $preset_by_slug as $slug => $value ) { 862 foreach ( self::PRESETS_METADATA as $preset_metadata ) { 863 $slugs = self::get_settings_slugs( $settings, $preset_metadata, $origins ); 864 foreach ( $preset_metadata['classes'] as $class => $property ) { 865 foreach ( $slugs as $slug ) { 866 $css_var = self::replace_slug_in_string( $preset_metadata['css_vars'], $slug ); 867 $class_name = self::replace_slug_in_string( $class, $slug ); 722 868 $stylesheet .= self::to_ruleset( 723 self::append_to_selector( $selector, '.has-' . _wp_to_kebab_case( $slug ) . '-' . $class['class_suffix']),869 self::append_to_selector( $selector, $class_name ), 724 870 array( 725 871 array( 726 'name' => $ class['property_name'],727 'value' => 'var( --wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case( $slug ). ') !important',872 'name' => $property, 873 'value' => 'var(' . $css_var . ') !important', 728 874 ), 729 875 ) … … 737 883 738 884 /** 885 * Function that scopes a selector with another one. This works a bit like 886 * SCSS nesting except the `&` operator isn't supported. 887 * 888 * <code> 889 * $scope = '.a, .b .c'; 890 * $selector = '> .x, .y'; 891 * $merged = scope_selector( $scope, $selector ); 892 * // $merged is '.a > .x, .a .y, .b .c > .x, .b .c .y' 893 * </code> 894 * 895 * @since 5.9.0 896 * 897 * @param string $scope Selector to scope to. 898 * @param string $selector Original selector. 899 * 900 * @return string Scoped selector. 901 */ 902 private static function scope_selector( $scope, $selector ) { 903 $scopes = explode( ',', $scope ); 904 $selectors = explode( ',', $selector ); 905 906 $selectors_scoped = array(); 907 foreach ( $scopes as $outer ) { 908 foreach ( $selectors as $inner ) { 909 $selectors_scoped[] = trim( $outer ) . ' ' . trim( $inner ); 910 } 911 } 912 913 return implode( ', ', $selectors_scoped ); 914 } 915 916 /** 917 * Gets preset values keyed by slugs based on settings and metadata. 918 * 919 * <code> 920 * $settings = array( 921 * 'typography' => array( 922 * 'fontFamilies' => array( 923 * array( 924 * 'slug' => 'sansSerif', 925 * 'fontFamily' => '"Helvetica Neue", sans-serif', 926 * ), 927 * array( 928 * 'slug' => 'serif', 929 * 'colors' => 'Georgia, serif', 930 * ) 931 * ), 932 * ), 933 * ); 934 * $meta = array( 935 * 'path' => array( 'typography', 'fontFamilies' ), 936 * 'value_key' => 'fontFamily', 937 * ); 938 * $values_by_slug = get_settings_values_by_slug(); 939 * // $values_by_slug === array( 940 * // 'sans-serif' => '"Helvetica Neue", sans-serif', 941 * // 'serif' => 'Georgia, serif', 942 * // ); 943 * </code> 944 * 945 * @since 5.9.0 946 * 947 * @param array $settings Settings to process. 948 * @param array $preset_metadata One of the PRESETS_METADATA values. 949 * @param array $origins List of origins to process. 950 * @return array Array of presets where each key is a slug and each value is the preset value. 951 */ 952 private static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) { 953 $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); 954 955 $result = array(); 956 foreach ( $origins as $origin ) { 957 if ( ! isset( $preset_per_origin[ $origin ] ) ) { 958 continue; 959 } 960 foreach ( $preset_per_origin[ $origin ] as $preset ) { 961 $slug = _wp_to_kebab_case( $preset['slug'] ); 962 963 $value = ''; 964 if ( isset( $preset_metadata['value_key'] ) ) { 965 $value_key = $preset_metadata['value_key']; 966 $value = $preset[ $value_key ]; 967 } elseif ( 968 isset( $preset_metadata['value_func'] ) && 969 is_callable( $preset_metadata['value_func'] ) 970 ) { 971 $value_func = $preset_metadata['value_func']; 972 $value = call_user_func( $value_func, $preset ); 973 } else { 974 // If we don't have a value, then don't add it to the result. 975 continue; 976 } 977 978 $result[ $slug ] = $value; 979 } 980 } 981 return $result; 982 } 983 984 /** 985 * Similar to get_settings_values_by_slug, but doesn't compute the value. 986 * 987 * @since 5.9.0 988 * 989 * @param array $settings Settings to process. 990 * @param array $preset_metadata One of the PRESETS_METADATA values. 991 * @param array $origins List of origins to process. 992 * @return array Array of presets where the key and value are both the slug. 993 */ 994 private static function get_settings_slugs( $settings, $preset_metadata, $origins = self::VALID_ORIGINS ) { 995 $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); 996 997 $result = array(); 998 foreach ( $origins as $origin ) { 999 if ( ! isset( $preset_per_origin[ $origin ] ) ) { 1000 continue; 1001 } 1002 foreach ( $preset_per_origin[ $origin ] as $preset ) { 1003 $slug = _wp_to_kebab_case( $preset['slug'] ); 1004 1005 // Use the array as a set so we don't get duplicates. 1006 $result[ $slug ] = $slug; 1007 } 1008 } 1009 return $result; 1010 } 1011 1012 /** 1013 * Transform a slug into a CSS Custom Property. 1014 * 1015 * @since 5.9.0 1016 * 1017 * @param string $input String to replace. 1018 * @param string $slug The slug value to use to generate the custom property. 1019 * @return string The CSS Custom Property. Something along the lines of --wp--preset--color--black. 1020 */ 1021 private static function replace_slug_in_string( $input, $slug ) { 1022 return strtr( $input, array( '$slug' => $slug ) ); 1023 } 1024 1025 /** 739 1026 * Given the block settings, it extracts the CSS Custom Properties 740 1027 * for the presets and adds them to the $declarations array … … 749 1036 * 750 1037 * @param array $settings Settings to process. 1038 * @param array $origins List of origins to process. 751 1039 * @return array Returns the modified $declarations. 752 1040 */ 753 private static function compute_preset_vars( $settings ) {1041 private static function compute_preset_vars( $settings, $origins ) { 754 1042 $declarations = array(); 755 foreach ( self::PRESETS_METADATA as $preset ) { 756 $preset_per_origin = _wp_array_get( $settings, $preset['path'], array() ); 757 $preset_by_slug = self::get_merged_preset_by_slug( $preset_per_origin, $preset['value_key'] ); 758 foreach ( $preset_by_slug as $slug => $value ) { 1043 foreach ( self::PRESETS_METADATA as $preset_metadata ) { 1044 $values_by_slug = self::get_settings_values_by_slug( $settings, $preset_metadata, $origins ); 1045 foreach ( $values_by_slug as $slug => $value ) { 759 1046 $declarations[] = array( 760 'name' => '--wp--preset--' . $preset['css_var_infix'] . '--' . _wp_to_kebab_case($slug ),1047 'name' => self::replace_slug_in_string( $preset_metadata['css_vars'], $slug ), 761 1048 'value' => $value, 762 1049 ); … … 865 1152 * 866 1153 * @since 5.8.0 1154 * @since 5.9.0 Added theme setting and properties parameters. 867 1155 * 868 1156 * @param array $styles Styles to process. 1157 * @param array $settings Theme settings. 1158 * @param array $properties Properties metadata. 869 1159 * @return array Returns the modified $declarations. 870 1160 */ 871 private static function compute_style_properties( $styles ) {1161 private static function compute_style_properties( $styles, $settings = array(), $properties = self::PROPERTIES_METADATA ) { 872 1162 $declarations = array(); 873 1163 if ( empty( $styles ) ) { … … 875 1165 } 876 1166 877 $properties = array(); 878 foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { 879 /* 880 * Some properties can be shorthand properties, meaning that 881 * they contain multiple values instead of a single one. 882 * An example of this is the padding property. 883 */ 884 if ( self::has_properties( $metadata ) ) { 885 foreach ( $metadata['properties'] as $property ) { 886 $properties[] = array( 887 'name' => $name . '-' . $property, 888 'value' => array_merge( $metadata['value'], array( $property ) ), 889 ); 1167 foreach ( $properties as $css_property => $value_path ) { 1168 $value = self::get_property_value( $styles, $value_path ); 1169 1170 // Look up protected properties, keyed by value path. 1171 // Skip protected properties that are explicitly set to `null`. 1172 if ( is_array( $value_path ) ) { 1173 $path_string = implode( '.', $value_path ); 1174 if ( 1175 array_key_exists( $path_string, self::PROTECTED_PROPERTIES ) && 1176 _wp_array_get( $settings, self::PROTECTED_PROPERTIES[ $path_string ], null ) === null 1177 ) { 1178 continue; 890 1179 } 891 } else { 892 $properties[] = array( 893 'name' => $name, 894 'value' => $metadata['value'], 895 ); 896 } 897 } 898 899 foreach ( $properties as $prop ) { 900 $value = self::get_property_value( $styles, $prop['value'] ); 901 if ( empty( $value ) ) { 1180 } 1181 1182 // Skip if empty and not "0" or value represents array of longhand values. 1183 $has_missing_value = empty( $value ) && ! is_numeric( $value ); 1184 if ( $has_missing_value || is_array( $value ) ) { 902 1185 continue; 903 1186 } 904 1187 905 1188 $declarations[] = array( 906 'name' => $ prop['name'],1189 'name' => $css_property, 907 1190 'value' => $value, 908 1191 ); … … 910 1193 911 1194 return $declarations; 912 }913 914 /**915 * Whether the metadata contains a key named properties.916 *917 * @since 5.8.0918 *919 * @param array $metadata Description of the style property.920 * @return bool True if properties exists, false otherwise.921 */922 private static function has_properties( $metadata ) {923 if ( array_key_exists( 'properties', $metadata ) ) {924 return true;925 }926 927 return false;928 1195 } 929 1196 … … 936 1203 * 937 1204 * @since 5.8.0 1205 * @since 5.9.0 Consider $value that are arrays as well. 938 1206 * 939 1207 * @param array $styles Styles subtree. … … 944 1212 $value = _wp_array_get( $styles, $path, '' ); 945 1213 946 if ( '' === $value ) {1214 if ( '' === $value || is_array( $value ) ) { 947 1215 return $value; 948 1216 } … … 1016 1284 } 1017 1285 1018 1019 1286 /** 1020 1287 * Builds metadata for the style nodes, which returns in the form of: … … 1023 1290 * [ 1024 1291 * 'path' => [ 'path', 'to', 'some', 'node' ], 1025 * 'selector' => 'CSS selector for some node' 1292 * 'selector' => 'CSS selector for some node', 1293 * 'duotone' => 'CSS selector for duotone for some node' 1026 1294 * ], 1027 1295 * [ 1028 1296 * 'path' => ['path', 'to', 'other', 'node' ], 1029 * 'selector' => 'CSS selector for other node' 1297 * 'selector' => 'CSS selector for other node', 1298 * 'duotone' => null 1030 1299 * ], 1031 1300 * ] … … 1069 1338 } 1070 1339 1340 $duotone_selector = null; 1341 if ( isset( $selectors[ $name ]['duotone'] ) ) { 1342 $duotone_selector = $selectors[ $name ]['duotone']; 1343 } 1344 1071 1345 $nodes[] = array( 1072 1346 'path' => array( 'styles', 'blocks', $name ), 1073 1347 'selector' => $selector, 1348 'duotone' => $duotone_selector, 1074 1349 ); 1075 1350 … … 1091 1366 * 1092 1367 * @since 5.8.0 1368 * @since 5.9.0 Duotone preset also has origins. 1093 1369 * 1094 1370 * @param WP_Theme_JSON $incoming Data to merge. … … 1099 1375 1100 1376 /* 1101 * The array_replace_recursive ()algorithm merges at the leaf level.1377 * The array_replace_recursive algorithm merges at the leaf level. 1102 1378 * For leaf values that are arrays it will use the numeric indexes for replacement. 1103 1379 * In those cases, we want to replace the existing with the incoming value, if it exists. … … 1105 1381 $to_replace = array(); 1106 1382 $to_replace[] = array( 'spacing', 'units' ); 1107 $to_replace[] = array( 'color', 'duotone' );1108 1383 foreach ( self::VALID_ORIGINS as $origin ) { 1384 $to_replace[] = array( 'color', 'duotone', $origin ); 1109 1385 $to_replace[] = array( 'color', 'palette', $origin ); 1110 1386 $to_replace[] = array( 'color', 'gradients', $origin ); … … 1123 1399 } 1124 1400 } 1401 1402 } 1403 1404 /** 1405 * Removes insecure data from theme.json. 1406 * 1407 * @since 5.9.0 1408 * 1409 * @param array $theme_json Structure to sanitize. 1410 * @return array Sanitized structure. 1411 */ 1412 public static function remove_insecure_properties( $theme_json ) { 1413 $sanitized = array(); 1414 1415 $theme_json = WP_Theme_JSON_Schema::migrate( $theme_json ); 1416 1417 $valid_block_names = array_keys( self::get_blocks_metadata() ); 1418 $valid_element_names = array_keys( self::ELEMENTS ); 1419 $theme_json = self::sanitize( $theme_json, $valid_block_names, $valid_element_names ); 1420 1421 $blocks_metadata = self::get_blocks_metadata(); 1422 $style_nodes = self::get_style_nodes( $theme_json, $blocks_metadata ); 1423 foreach ( $style_nodes as $metadata ) { 1424 $input = _wp_array_get( $theme_json, $metadata['path'], array() ); 1425 if ( empty( $input ) ) { 1426 continue; 1427 } 1428 1429 $output = self::remove_insecure_styles( $input ); 1430 if ( ! empty( $output ) ) { 1431 _wp_array_set( $sanitized, $metadata['path'], $output ); 1432 } 1433 } 1434 1435 $setting_nodes = self::get_setting_nodes( $theme_json ); 1436 foreach ( $setting_nodes as $metadata ) { 1437 $input = _wp_array_get( $theme_json, $metadata['path'], array() ); 1438 if ( empty( $input ) ) { 1439 continue; 1440 } 1441 1442 $output = self::remove_insecure_settings( $input ); 1443 if ( ! empty( $output ) ) { 1444 _wp_array_set( $sanitized, $metadata['path'], $output ); 1445 } 1446 } 1447 1448 if ( empty( $sanitized['styles'] ) ) { 1449 unset( $theme_json['styles'] ); 1450 } else { 1451 $theme_json['styles'] = $sanitized['styles']; 1452 } 1453 1454 if ( empty( $sanitized['settings'] ) ) { 1455 unset( $theme_json['settings'] ); 1456 } else { 1457 $theme_json['settings'] = $sanitized['settings']; 1458 } 1459 1460 return $theme_json; 1461 } 1462 1463 /** 1464 * Processes a setting node and returns the same node 1465 * without the insecure settings. 1466 * 1467 * @since 5.9.0 1468 * 1469 * @param array $input Node to process. 1470 * @return array 1471 */ 1472 private static function remove_insecure_settings( $input ) { 1473 $output = array(); 1474 foreach ( self::PRESETS_METADATA as $preset_metadata ) { 1475 $presets = _wp_array_get( $input, $preset_metadata['path'], null ); 1476 if ( null === $presets ) { 1477 continue; 1478 } 1479 1480 $escaped_preset = array(); 1481 foreach ( $presets as $preset ) { 1482 if ( 1483 esc_attr( esc_html( $preset['name'] ) ) === $preset['name'] && 1484 sanitize_html_class( $preset['slug'] ) === $preset['slug'] 1485 ) { 1486 $value = null; 1487 if ( isset( $preset_metadata['value_key'] ) ) { 1488 $value = $preset[ $preset_metadata['value_key'] ]; 1489 } elseif ( 1490 isset( $preset_metadata['value_func'] ) && 1491 is_callable( $preset_metadata['value_func'] ) 1492 ) { 1493 $value = call_user_func( $preset_metadata['value_func'], $preset ); 1494 } 1495 1496 $preset_is_valid = true; 1497 foreach ( $preset_metadata['properties'] as $property ) { 1498 if ( ! self::is_safe_css_declaration( $property, $value ) ) { 1499 $preset_is_valid = false; 1500 break; 1501 } 1502 } 1503 1504 if ( $preset_is_valid ) { 1505 $escaped_preset[] = $preset; 1506 } 1507 } 1508 } 1509 1510 if ( ! empty( $escaped_preset ) ) { 1511 _wp_array_set( $output, $preset_metadata['path'], $escaped_preset ); 1512 } 1513 } 1514 1515 return $output; 1516 } 1517 1518 /** 1519 * Processes a style node and returns the same node 1520 * without the insecure styles. 1521 * 1522 * @since 5.9.0 1523 * 1524 * @param array $input Node to process. 1525 * @return array 1526 */ 1527 private static function remove_insecure_styles( $input ) { 1528 $output = array(); 1529 $declarations = self::compute_style_properties( $input ); 1530 1531 foreach ( $declarations as $declaration ) { 1532 if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) { 1533 $path = self::PROPERTIES_METADATA[ $declaration['name'] ]; 1534 1535 // Check the value isn't an array before adding so as to not 1536 // double up shorthand and longhand styles. 1537 $value = _wp_array_get( $input, $path, array() ); 1538 if ( ! is_array( $value ) ) { 1539 _wp_array_set( $output, $path, $value ); 1540 } 1541 } 1542 } 1543 return $output; 1544 } 1545 1546 /** 1547 * Checks that a declaration provided by the user is safe. 1548 * 1549 * @since 5.9.0 1550 * 1551 * @param string $property_name Property name in a CSS declaration, i.e. the `color` in `color: red`. 1552 * @param string $property_value Value in a CSS declaration, i.e. the `red` in `color: red`. 1553 * @return boolean 1554 */ 1555 private static function is_safe_css_declaration( $property_name, $property_value ) { 1556 $style_to_validate = $property_name . ': ' . $property_value; 1557 $filtered = esc_html( safecss_filter_attr( $style_to_validate ) ); 1558 return ! empty( trim( $filtered ) ); 1125 1559 } 1126 1560 … … 1177 1611 $theme_settings['settings']['typography'] = array(); 1178 1612 } 1179 $theme_settings['settings']['typography'][' customLineHeight'] = $settings['enableCustomLineHeight'];1613 $theme_settings['settings']['typography']['lineHeight'] = $settings['enableCustomLineHeight']; 1180 1614 } 1181 1615 … … 1221 1655 $theme_settings['settings']['spacing'] = array(); 1222 1656 } 1223 $theme_settings['settings']['spacing'][' customPadding'] = $settings['enableCustomSpacing'];1657 $theme_settings['settings']['spacing']['padding'] = $settings['enableCustomSpacing']; 1224 1658 } 1225 1659 -
trunk/src/wp-includes/default-filters.php
r51388 r52049 337 337 add_action( 'init', array( 'WP_Block_Supports', 'init' ), 22 ); 338 338 add_action( 'switch_theme', array( 'WP_Theme_JSON_Resolver', 'clean_cached_data' ) ); 339 add_action( 'start_previewing_theme', array( 'WP_Theme_JSON_Resolver', 'clean_cached_data' ) ); 339 340 add_action( 'after_switch_theme', '_wp_menus_changed' ); 340 341 add_action( 'after_switch_theme', '_wp_sidebars_changed' ); -
trunk/src/wp-includes/kses.php
r51963 r52049 2261 2261 'border-bottom-style', 2262 2262 'border-bottom-width', 2263 'border-bottom-right-radius', 2264 'border-bottom-left-radius', 2263 2265 'border-left', 2264 2266 'border-left-color', … … 2269 2271 'border-top-style', 2270 2272 'border-top-width', 2273 'border-top-left-radius', 2274 'border-top-right-radius', 2271 2275 2272 2276 'border-spacing', … … 2283 2287 2284 2288 'color', 2289 'filter', 2285 2290 'font', 2286 2291 'font-family', -
trunk/src/wp-includes/script-loader.php
r52036 r52049 2322 2322 2323 2323 if ( null === $stylesheet ) { 2324 $settings = get_default_block_editor_settings(); 2325 $theme_json = WP_Theme_JSON_Resolver::get_merged_data( $settings ); 2324 $theme_json = WP_Theme_JSON_Resolver::get_merged_data(); 2326 2325 $stylesheet = $theme_json->get_stylesheet(); 2327 2326 -
trunk/src/wp-includes/theme-i18n.json
r50959 r52049 5 5 { 6 6 "name": "Font size name" 7 } 8 ], 9 "fontFamilies": [ 10 { 11 "name": "Font family name" 7 12 } 8 13 ] … … 32 37 "name": "Font size name" 33 38 } 39 ], 40 "fontFamilies": [ 41 { 42 "name": "Font family name" 43 } 34 44 ] 35 45 }, … … 48 58 } 49 59 } 50 } 60 }, 61 "customTemplates": [ 62 { 63 "title": "Custom template name" 64 } 65 ], 66 "templateParts": [ 67 { 68 "title": "Template part name" 69 } 70 ] 51 71 } -
trunk/src/wp-includes/theme.json
r51538 r52049 1 1 { 2 "version": 1,2 "version": 2, 3 3 "settings": { 4 4 "border": { 5 "customRadius": false 5 "color": false, 6 "radius": false, 7 "style": false, 8 "width": false 6 9 }, 7 10 "color": { … … 10 13 "customGradient": true, 11 14 "link": false, 15 "background": true, 16 "text": true, 12 17 "duotone": [ 13 18 { … … 178 183 }, 179 184 "spacing": { 180 "customMargin": false, 181 "customPadding": false, 185 "blockGap": null, 186 "margin": false, 187 "padding": false, 182 188 "units": [ "px", "em", "rem", "vh", "vw", "%" ] 183 189 }, 184 190 "typography": { 185 191 "customFontSize": true, 186 "customLineHeight": false,187 192 "dropCap": true, 193 "fontStyle": true, 194 "fontWeight": true, 195 "letterSpacing": true, 196 "lineHeight": false, 197 "textDecoration": true, 198 "textTransform": true, 188 199 "fontSizes": [ 189 200 { … … 217 228 "core/button": { 218 229 "border": { 219 "customRadius": true 230 "radius": true 231 } 232 }, 233 "core/pullquote": { 234 "border": { 235 "color": true, 236 "radius": true, 237 "style": true, 238 "width": true 220 239 } 221 240 } 222 241 } 242 }, 243 "styles": { 244 "spacing": { "blockGap": "24px" } 223 245 } 224 246 } -
trunk/src/wp-settings.php
r52026 r52049 171 171 require ABSPATH . WPINC . '/theme.php'; 172 172 require ABSPATH . WPINC . '/class-wp-theme.php'; 173 require ABSPATH . WPINC . '/class-wp-theme-json-schema.php'; 173 174 require ABSPATH . WPINC . '/class-wp-theme-json.php'; 174 175 require ABSPATH . WPINC . '/class-wp-theme-json-resolver.php'; -
trunk/tests/phpunit/data/languages/themes/block-theme-pl_PL.po
r51370 r52049 23 23 msgstr "Szablon strony głównej" 24 24 25 msgctxt "Template part name" 26 msgid "Small Header" 27 msgstr "Mały nagłówek" 28 25 29 msgctxt "Color name" 26 30 msgid "Light" -
trunk/tests/phpunit/data/themedir1/block-theme/theme.json
r51370 r52049 65 65 { 66 66 "name": "small-header", 67 "title": "Small Header", 67 68 "area": "header" 68 69 } -
trunk/tests/phpunit/tests/theme/themeDir.php
r52010 r52049 163 163 'REST Theme', 164 164 'Block Theme', 165 'Block Theme Child Theme', 165 166 ); 166 167 -
trunk/tests/phpunit/tests/theme/wpThemeJson.php
r51443 r52049 16 16 /** 17 17 * @ticket 52991 18 * @ticket 54336 18 19 */ 19 20 public function test_get_settings() { … … 24 25 'color' => array( 25 26 'custom' => false, 27 ), 28 'layout' => array( 29 'contentSize' => 'value', 30 'invalid/key' => 'value', 26 31 ), 27 32 'invalid/key' => 'value', … … 53 58 'custom' => false, 54 59 ), 60 'layout' => array( 61 'contentSize' => 'value', 62 ), 55 63 'blocks' => array( 56 64 'core/group' => array( … … 62 70 ); 63 71 64 $this->assert SameSetsWithIndex( $expected, $actual );72 $this->assertEqualSetsWithIndex( $expected, $actual ); 65 73 } 66 74 … … 194 202 195 203 /** 204 * @ticket 54336 205 */ 206 public function test_get_stylesheet_support_for_shorthand_and_longhand_values() { 207 $theme_json = new WP_Theme_JSON( 208 array( 209 'version' => WP_Theme_JSON::LATEST_SCHEMA, 210 'styles' => array( 211 'blocks' => array( 212 'core/group' => array( 213 'border' => array( 214 'radius' => '10px', 215 ), 216 'spacing' => array( 217 'padding' => '24px', 218 'margin' => '1em', 219 ), 220 ), 221 'core/image' => array( 222 'border' => array( 223 'radius' => array( 224 'topLeft' => '10px', 225 'bottomRight' => '1em', 226 ), 227 ), 228 'spacing' => array( 229 'padding' => array( 230 'top' => '15px', 231 ), 232 'margin' => array( 233 'bottom' => '30px', 234 ), 235 ), 236 ), 237 ), 238 ), 239 ) 240 ); 241 242 $styles = 'body { margin: 0; }.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-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}'; 243 $this->assertEquals( $styles, $theme_json->get_stylesheet() ); 244 $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) ); 245 } 246 247 /** 248 * @ticket 54336 249 */ 250 public function test_get_stylesheet_skips_disabled_protected_properties() { 251 $theme_json = new WP_Theme_JSON( 252 array( 253 'version' => WP_Theme_JSON::LATEST_SCHEMA, 254 'settings' => array( 255 'spacing' => array( 256 'blockGap' => null, 257 ), 258 ), 259 'styles' => array( 260 'spacing' => array( 261 'blockGap' => '1em', 262 ), 263 'blocks' => array( 264 'core/columns' => array( 265 'spacing' => array( 266 'blockGap' => '24px', 267 ), 268 ), 269 ), 270 ), 271 ) 272 ); 273 274 $expected = 'body { margin: 0; }.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; }'; 275 $this->assertEquals( $expected, $theme_json->get_stylesheet() ); 276 $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); 277 } 278 279 /** 280 * @ticket 54336 281 */ 282 public function test_get_stylesheet_renders_enabled_protected_properties() { 283 $theme_json = new WP_Theme_JSON( 284 array( 285 'version' => WP_Theme_JSON::LATEST_SCHEMA, 286 'settings' => array( 287 'spacing' => array( 288 'blockGap' => true, 289 ), 290 ), 291 'styles' => array( 292 'spacing' => array( 293 'blockGap' => '1em', 294 ), 295 'blocks' => array( 296 'core/columns' => array( 297 'spacing' => array( 298 'blockGap' => '24px', 299 ), 300 ), 301 ), 302 ), 303 ) 304 ); 305 306 $expected = 'body{--wp--style--block-gap: 1em;}body { margin: 0; }.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-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }.wp-block-columns{--wp--style--block-gap: 24px;}'; 307 $this->assertEquals( $expected, $theme_json->get_stylesheet() ); 308 $this->assertEquals( $expected, $theme_json->get_stylesheet( array( 'styles' ) ) ); 309 } 310 311 /** 196 312 * @ticket 53175 313 * @ticket 54336 197 314 */ 198 315 public function test_get_stylesheet() { … … 221 338 ), 222 339 ), 340 ), 341 'spacing' => array( 342 'blockGap' => false, 223 343 ), 224 344 'misc' => 'value', … … 251 371 'blocks' => array( 252 372 'core/group' => array( 373 'border' => array( 374 'radius' => '10px', 375 ), 253 376 'elements' => array( 254 377 'link' => array( … … 259 382 ), 260 383 'spacing' => array( 261 'padding' => array( 262 'top' => '12px', 263 'bottom' => '24px', 264 ), 384 'padding' => '24px', 265 385 ), 266 386 ), … … 294 414 ), 295 415 ), 416 'core/image' => array( 417 'border' => array( 418 'radius' => array( 419 'topLeft' => '10px', 420 'bottomRight' => '1em', 421 ), 422 ), 423 'spacing' => array( 424 'margin' => array( 425 'bottom' => '30px', 426 ), 427 ), 428 ), 296 429 ), 297 430 ), … … 300 433 ); 301 434 302 $this->assertSame( 303 'body{--wp--preset--color--grey: grey;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}', 304 $theme_json->get_stylesheet() 305 ); 306 $this->assertSame( 307 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}', 308 $theme_json->get_stylesheet( 'block_styles' ) 309 ); 310 $this->assertSame( 311 'body{--wp--preset--color--grey: grey;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}', 312 $theme_json->get_stylesheet( 'css_variables' ) 313 ); 435 $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; 436 $styles = 'body{color: var(--wp--preset--color--grey);}body { margin: 0; }.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-site-blocks > * { margin-top: 0; margin-bottom: 0; }.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}'; 437 $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; 438 $all = $variables . $styles . $presets; 439 $this->assertEquals( $all, $theme_json->get_stylesheet() ); 440 $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) ); 441 $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) ); 442 $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) ); 314 443 } 315 444 316 445 /** 317 446 * @ticket 52991 447 * @ticket 54336 318 448 */ 319 449 public function test_get_stylesheet_preset_classes_work_with_compounded_selectors() { … … 338 468 ); 339 469 340 $this->assert Same(341 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;} ',342 $theme_json->get_stylesheet( 'block_styles')470 $this->assertEquals( 471 'h1.has-white-color,h2.has-white-color,h3.has-white-color,h4.has-white-color,h5.has-white-color,h6.has-white-color{color: var(--wp--preset--color--white) !important;}h1.has-white-background-color,h2.has-white-background-color,h3.has-white-background-color,h4.has-white-background-color,h5.has-white-background-color,h6.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}h1.has-white-border-color,h2.has-white-border-color,h3.has-white-border-color,h4.has-white-border-color,h5.has-white-border-color,h6.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}', 472 $theme_json->get_stylesheet( array( 'presets' ) ) 343 473 ); 344 474 } … … 346 476 /** 347 477 * @ticket 53175 478 * @ticket 54336 348 479 */ 349 480 public function test_get_stylesheet_preset_rules_come_after_block_rules() { … … 377 508 ); 378 509 379 $this->assertSame( 380 '.wp-block-group{--wp--preset--color--grey: grey;}.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}', 381 $theme_json->get_stylesheet() 382 ); 383 $this->assertSame( 384 '.wp-block-group{color: red;}.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}', 385 $theme_json->get_stylesheet( 'block_styles' ) 386 ); 510 $styles = 'body { margin: 0; }.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-block-group{color: red;}'; 511 $presets = '.wp-block-group.has-grey-color{color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.wp-block-group.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}'; 512 $variables = '.wp-block-group{--wp--preset--color--grey: grey;}'; 513 $all = $variables . $styles . $presets; 514 $this->assertEquals( $all, $theme_json->get_stylesheet() ); 515 $this->assertEquals( $styles, $theme_json->get_stylesheet( array( 'styles' ) ) ); 516 $this->assertEquals( $presets, $theme_json->get_stylesheet( array( 'presets' ) ) ); 517 $this->assertEquals( $variables, $theme_json->get_stylesheet( array( 'variables' ) ) ); 518 } 519 520 /** 521 * @ticket 54336 522 */ 523 public function test_get_stylesheet_generates_proper_classes_from_slugs() { 524 $theme_json = new WP_Theme_JSON( 525 array( 526 'version' => WP_Theme_JSON::LATEST_SCHEMA, 527 'settings' => array( 528 'color' => array( 529 'palette' => array( 530 array( 531 'slug' => 'grey', 532 'color' => 'grey', 533 ), 534 array( 535 'slug' => 'dark grey', 536 'color' => 'grey', 537 ), 538 array( 539 'slug' => 'light-grey', 540 'color' => 'grey', 541 ), 542 array( 543 'slug' => 'white2black', 544 'color' => 'grey', 545 ), 546 ), 547 ), 548 ), 549 ) 550 ); 551 552 $this->assertEquals( 553 '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-dark-grey-color{color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-color{color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-color{color: var(--wp--preset--color--white-2-black) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-background-color{background-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-background-color{background-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-background-color{background-color: var(--wp--preset--color--white-2-black) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-dark-grey-border-color{border-color: var(--wp--preset--color--dark-grey) !important;}.has-light-grey-border-color{border-color: var(--wp--preset--color--light-grey) !important;}.has-white-2-black-border-color{border-color: var(--wp--preset--color--white-2-black) !important;}', 554 $theme_json->get_stylesheet( array( 'presets' ) ) 555 ); 556 $this->assertEquals( 557 'body{--wp--preset--color--grey: grey;--wp--preset--color--dark-grey: grey;--wp--preset--color--light-grey: grey;--wp--preset--color--white-2-black: grey;}', 558 $theme_json->get_stylesheet( array( 'variables' ) ) 559 ); 560 387 561 } 388 562 389 563 /** 390 564 * @ticket 53175 565 * @ticket 54336 391 566 */ 392 567 public function test_get_stylesheet_preset_values_are_marked_as_important() { … … 422 597 ); 423 598 424 $this->assert Same(425 'body{--wp--preset--color--grey: grey;} p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}',599 $this->assertEquals( 600 'body{--wp--preset--color--grey: grey;}body { margin: 0; }.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; }p{background-color: blue;color: red;font-size: 12px;line-height: 1.3;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', 426 601 $theme_json->get_stylesheet() 427 602 ); … … 430 605 /** 431 606 * @ticket 52991 607 * @ticket 54336 432 608 */ 433 609 public function test_merge_incoming_data() { … … 609 785 ), 610 786 'typography' => array( 611 'fontSizes' => array(787 'fontSizes' => array( 612 788 'theme' => array( 613 789 array( 614 790 'slug' => 'fontSize', 615 791 'size' => 'fontSize', 792 ), 793 ), 794 ), 795 'fontFamilies' => array( 796 'theme' => array( 797 array( 798 'slug' => 'fontFamily', 799 'fontFamily' => 'fontFamily', 616 800 ), 617 801 ), … … 670 854 /** 671 855 * @ticket 53175 856 * @ticket 54336 672 857 */ 673 858 public function test_merge_incoming_data_empty_presets() { … … 737 922 'settings' => array( 738 923 'color' => array( 739 'duotone' => array(), 924 'duotone' => array( 925 'theme' => array(), 926 ), 740 927 'gradients' => array( 741 928 'theme' => array(), … … 761 948 /** 762 949 * @ticket 53175 950 * @ticket 54336 763 951 */ 764 952 public function test_merge_incoming_data_null_presets() { … … 811 999 ), 812 1000 'spacing' => array( 813 ' customMargin' => false,1001 'margin' => false, 814 1002 ), 815 1003 'typography' => array( 816 ' customLineHeight' => false,1004 'lineHeight' => false, 817 1005 ), 818 1006 ), … … 828 1016 'custom' => false, 829 1017 'duotone' => array( 830 array( 831 'slug' => 'value', 832 'colors' => array( 'red', 'green' ), 1018 'theme' => array( 1019 array( 1020 'slug' => 'value', 1021 'colors' => array( 'red', 'green' ), 1022 ), 833 1023 ), 834 1024 ), … … 851 1041 ), 852 1042 'spacing' => array( 853 ' customMargin' => false,854 'units' 1043 'margin' => false, 1044 'units' => array( 'px', 'em' ), 855 1045 ), 856 1046 'typography' => array( 857 ' customLineHeight' => false,858 'fontSizes' 1047 'lineHeight' => false, 1048 'fontSizes' => array( 859 1049 'theme' => array( 860 1050 array( … … 869 1059 870 1060 $this->assertEqualSetsWithIndex( $expected, $actual ); 1061 } 1062 1063 /** 1064 * @ticket 54336 1065 */ 1066 public function test_remove_insecure_properties_removes_unsafe_styles() { 1067 $actual = WP_Theme_JSON::remove_insecure_properties( 1068 array( 1069 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1070 'styles' => array( 1071 'color' => array( 1072 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')', 1073 'text' => 'var:preset|color|dark-red', 1074 ), 1075 'elements' => array( 1076 'link' => array( 1077 'color' => array( 1078 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')', 1079 'text' => 'var:preset|color|dark-pink', 1080 'background' => 'var:preset|color|dark-red', 1081 ), 1082 ), 1083 ), 1084 'blocks' => array( 1085 'core/image' => array( 1086 'filter' => array( 1087 'duotone' => 'var:preset|duotone|blue-red', 1088 ), 1089 ), 1090 'core/cover' => array( 1091 'filter' => array( 1092 'duotone' => 'var(--wp--preset--duotone--blue-red, var(--fallback-unsafe))', 1093 ), 1094 ), 1095 'core/group' => array( 1096 'color' => array( 1097 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')', 1098 'text' => 'var:preset|color|dark-gray', 1099 ), 1100 'elements' => array( 1101 'link' => array( 1102 'color' => array( 1103 'gradient' => 'url(\'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PHNjcmlwdD5hbGVydCgnb2snKTwvc2NyaXB0PjxsaW5lYXJHcmFkaWVudCBpZD0nZ3JhZGllbnQnPjxzdG9wIG9mZnNldD0nMTAlJyBzdG9wLWNvbG9yPScjRjAwJy8+PHN0b3Agb2Zmc2V0PSc5MCUnIHN0b3AtY29sb3I9JyNmY2MnLz4gPC9saW5lYXJHcmFkaWVudD48cmVjdCBmaWxsPSd1cmwoI2dyYWRpZW50KScgeD0nMCcgeT0nMCcgd2lkdGg9JzEwMCUnIGhlaWdodD0nMTAwJScvPjwvc3ZnPg==\')', 1104 'text' => 'var:preset|color|dark-pink', 1105 ), 1106 ), 1107 ), 1108 ), 1109 'invalid/key' => array( 1110 'background' => 'green', 1111 ), 1112 ), 1113 ), 1114 ) 1115 ); 1116 1117 $expected = array( 1118 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1119 'styles' => array( 1120 'color' => array( 1121 'text' => 'var:preset|color|dark-red', 1122 ), 1123 'elements' => array( 1124 'link' => array( 1125 'color' => array( 1126 'text' => 'var:preset|color|dark-pink', 1127 'background' => 'var:preset|color|dark-red', 1128 ), 1129 ), 1130 ), 1131 'blocks' => array( 1132 'core/image' => array( 1133 'filter' => array( 1134 'duotone' => 'var:preset|duotone|blue-red', 1135 ), 1136 ), 1137 'core/group' => array( 1138 'color' => array( 1139 'text' => 'var:preset|color|dark-gray', 1140 ), 1141 'elements' => array( 1142 'link' => array( 1143 'color' => array( 1144 'text' => 'var:preset|color|dark-pink', 1145 ), 1146 ), 1147 ), 1148 ), 1149 ), 1150 ), 1151 ); 1152 $this->assertEqualSetsWithIndex( $expected, $actual ); 1153 } 1154 1155 /** 1156 * @ticket 54336 1157 */ 1158 public function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() { 1159 $actual = WP_Theme_JSON::remove_insecure_properties( 1160 array( 1161 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1162 'styles' => array( 1163 'border' => array( 1164 'radius' => array( 1165 'topLeft' => '6px', 1166 'topRight' => 'var(--top-right, var(--unsafe-fallback))', 1167 'bottomRight' => '6px', 1168 'bottomLeft' => '6px', 1169 ), 1170 ), 1171 'spacing' => array( 1172 'padding' => array( 1173 'top' => '1px', 1174 'right' => '1px', 1175 'bottom' => 'var(--bottom, var(--unsafe-fallback))', 1176 'left' => '1px', 1177 ), 1178 ), 1179 'elements' => array( 1180 'link' => array( 1181 'spacing' => array( 1182 'padding' => array( 1183 'top' => '2px', 1184 'right' => '2px', 1185 'bottom' => 'var(--bottom, var(--unsafe-fallback))', 1186 'left' => '2px', 1187 ), 1188 ), 1189 ), 1190 ), 1191 'blocks' => array( 1192 'core/group' => array( 1193 'border' => array( 1194 'radius' => array( 1195 'topLeft' => '5px', 1196 'topRight' => 'var(--top-right, var(--unsafe-fallback))', 1197 'bottomRight' => '5px', 1198 'bottomLeft' => '5px', 1199 ), 1200 ), 1201 'spacing' => array( 1202 'padding' => array( 1203 'top' => '3px', 1204 'right' => '3px', 1205 'bottom' => 'var(bottom, var(--unsafe-fallback))', 1206 'left' => '3px', 1207 ), 1208 ), 1209 'elements' => array( 1210 'link' => array( 1211 'spacing' => array( 1212 'padding' => array( 1213 'top' => '4px', 1214 'right' => '4px', 1215 'bottom' => 'var(--bottom, var(--unsafe-fallback))', 1216 'left' => '4px', 1217 ), 1218 ), 1219 ), 1220 ), 1221 ), 1222 ), 1223 ), 1224 ), 1225 true 1226 ); 1227 1228 $expected = array( 1229 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1230 'styles' => array( 1231 'border' => array( 1232 'radius' => array( 1233 'topLeft' => '6px', 1234 'bottomRight' => '6px', 1235 'bottomLeft' => '6px', 1236 ), 1237 ), 1238 'spacing' => array( 1239 'padding' => array( 1240 'top' => '1px', 1241 'right' => '1px', 1242 'left' => '1px', 1243 ), 1244 ), 1245 'elements' => array( 1246 'link' => array( 1247 'spacing' => array( 1248 'padding' => array( 1249 'top' => '2px', 1250 'right' => '2px', 1251 'left' => '2px', 1252 ), 1253 ), 1254 ), 1255 ), 1256 'blocks' => array( 1257 'core/group' => array( 1258 'border' => array( 1259 'radius' => array( 1260 'topLeft' => '5px', 1261 'bottomRight' => '5px', 1262 'bottomLeft' => '5px', 1263 ), 1264 ), 1265 'spacing' => array( 1266 'padding' => array( 1267 'top' => '3px', 1268 'right' => '3px', 1269 'left' => '3px', 1270 ), 1271 ), 1272 'elements' => array( 1273 'link' => array( 1274 'spacing' => array( 1275 'padding' => array( 1276 'top' => '4px', 1277 'right' => '4px', 1278 'left' => '4px', 1279 ), 1280 ), 1281 ), 1282 ), 1283 ), 1284 ), 1285 ), 1286 ); 1287 $this->assertEqualSetsWithIndex( $expected, $actual ); 1288 } 1289 1290 /** 1291 * @ticket 54336 1292 */ 1293 public function test_remove_insecure_properties_removes_non_preset_settings() { 1294 $actual = WP_Theme_JSON::remove_insecure_properties( 1295 array( 1296 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1297 'settings' => array( 1298 'color' => array( 1299 'custom' => true, 1300 'palette' => array( 1301 array( 1302 'name' => 'Red', 1303 'slug' => 'red', 1304 'color' => '#ff0000', 1305 ), 1306 array( 1307 'name' => 'Green', 1308 'slug' => 'green', 1309 'color' => '#00ff00', 1310 ), 1311 array( 1312 'name' => 'Blue', 1313 'slug' => 'blue', 1314 'color' => '#0000ff', 1315 ), 1316 ), 1317 ), 1318 'spacing' => array( 1319 'padding' => false, 1320 ), 1321 'blocks' => array( 1322 'core/group' => array( 1323 'color' => array( 1324 'custom' => true, 1325 'palette' => array( 1326 array( 1327 'name' => 'Yellow', 1328 'slug' => 'yellow', 1329 'color' => '#ff0000', 1330 ), 1331 array( 1332 'name' => 'Pink', 1333 'slug' => 'pink', 1334 'color' => '#00ff00', 1335 ), 1336 array( 1337 'name' => 'Orange', 1338 'slug' => 'orange', 1339 'color' => '#0000ff', 1340 ), 1341 ), 1342 ), 1343 'spacing' => array( 1344 'padding' => false, 1345 ), 1346 ), 1347 ), 1348 ), 1349 ) 1350 ); 1351 1352 $expected = array( 1353 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1354 'settings' => array( 1355 'color' => array( 1356 'palette' => array( 1357 array( 1358 'name' => 'Red', 1359 'slug' => 'red', 1360 'color' => '#ff0000', 1361 ), 1362 array( 1363 'name' => 'Green', 1364 'slug' => 'green', 1365 'color' => '#00ff00', 1366 ), 1367 array( 1368 'name' => 'Blue', 1369 'slug' => 'blue', 1370 'color' => '#0000ff', 1371 ), 1372 ), 1373 ), 1374 'blocks' => array( 1375 'core/group' => array( 1376 'color' => array( 1377 'palette' => array( 1378 array( 1379 'name' => 'Yellow', 1380 'slug' => 'yellow', 1381 'color' => '#ff0000', 1382 ), 1383 array( 1384 'name' => 'Pink', 1385 'slug' => 'pink', 1386 'color' => '#00ff00', 1387 ), 1388 array( 1389 'name' => 'Orange', 1390 'slug' => 'orange', 1391 'color' => '#0000ff', 1392 ), 1393 ), 1394 ), 1395 ), 1396 ), 1397 ), 1398 ); 1399 $this->assertEqualSetsWithIndex( $expected, $actual ); 1400 } 1401 1402 /** 1403 * @ticket 54336 1404 */ 1405 public function test_remove_insecure_properties_removes_unsafe_preset_settings() { 1406 $actual = WP_Theme_JSON::remove_insecure_properties( 1407 array( 1408 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1409 'settings' => array( 1410 'color' => array( 1411 'palette' => array( 1412 array( 1413 'name' => 'Red/><b>ok</ok>', 1414 'slug' => 'red', 1415 'color' => '#ff0000', 1416 ), 1417 array( 1418 'name' => 'Green', 1419 'slug' => 'a" attr', 1420 'color' => '#00ff00', 1421 ), 1422 array( 1423 'name' => 'Blue', 1424 'slug' => 'blue', 1425 'color' => 'var(--color, var(--unsafe-fallback))', 1426 ), 1427 array( 1428 'name' => 'Pink', 1429 'slug' => 'pink', 1430 'color' => '#FFC0CB', 1431 ), 1432 ), 1433 ), 1434 'typography' => array( 1435 'fontFamilies' => array( 1436 array( 1437 'name' => 'Helvetica Arial/><b>test</b>', 1438 'slug' => 'helvetica-arial', 1439 'fontFamily' => 'Helvetica Neue, Helvetica, Arial, sans-serif', 1440 ), 1441 array( 1442 'name' => 'Geneva', 1443 'slug' => 'geneva#asa', 1444 'fontFamily' => 'Geneva, Tahoma, Verdana, sans-serif', 1445 ), 1446 array( 1447 'name' => 'Cambria', 1448 'slug' => 'cambria', 1449 'fontFamily' => 'Cambria, Georgia, serif', 1450 ), 1451 array( 1452 'name' => 'Helvetica Arial', 1453 'slug' => 'helvetica-arial', 1454 'fontFamily' => 'var(--fontFamily, var(--unsafe-fallback))', 1455 ), 1456 ), 1457 ), 1458 'blocks' => array( 1459 'core/group' => array( 1460 'color' => array( 1461 'palette' => array( 1462 array( 1463 'name' => 'Red/><b>ok</ok>', 1464 'slug' => 'red', 1465 'color' => '#ff0000', 1466 ), 1467 array( 1468 'name' => 'Green', 1469 'slug' => 'a" attr', 1470 'color' => '#00ff00', 1471 ), 1472 array( 1473 'name' => 'Blue', 1474 'slug' => 'blue', 1475 'color' => 'var(--color, var(--unsafe--fallback))', 1476 ), 1477 array( 1478 'name' => 'Pink', 1479 'slug' => 'pink', 1480 'color' => '#FFC0CB', 1481 ), 1482 ), 1483 ), 1484 ), 1485 ), 1486 ), 1487 ) 1488 ); 1489 1490 $expected = array( 1491 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1492 'settings' => array( 1493 'color' => array( 1494 'palette' => array( 1495 array( 1496 'name' => 'Pink', 1497 'slug' => 'pink', 1498 'color' => '#FFC0CB', 1499 ), 1500 ), 1501 ), 1502 'typography' => array( 1503 'fontFamilies' => array( 1504 array( 1505 'name' => 'Cambria', 1506 'slug' => 'cambria', 1507 'fontFamily' => 'Cambria, Georgia, serif', 1508 ), 1509 ), 1510 ), 1511 'blocks' => array( 1512 'core/group' => array( 1513 'color' => array( 1514 'palette' => array( 1515 array( 1516 'name' => 'Pink', 1517 'slug' => 'pink', 1518 'color' => '#FFC0CB', 1519 ), 1520 ), 1521 ), 1522 ), 1523 ), 1524 ), 1525 ); 1526 $this->assertEqualSetsWithIndex( $expected, $actual ); 1527 } 1528 1529 /** 1530 * @ticket 54336 1531 */ 1532 public function test_remove_insecure_properties_applies_safe_styles() { 1533 $actual = WP_Theme_JSON::remove_insecure_properties( 1534 array( 1535 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1536 'styles' => array( 1537 'color' => array( 1538 'text' => '#abcabc ', // Trailing space. 1539 ), 1540 ), 1541 ), 1542 true 1543 ); 1544 1545 $expected = array( 1546 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1547 'styles' => array( 1548 'color' => array( 1549 'text' => '#abcabc ', 1550 ), 1551 ), 1552 ); 1553 $this->assertEqualSetsWithIndex( $expected, $actual ); 1554 } 1555 1556 /** 1557 * @ticket 54336 1558 */ 1559 public function test_get_custom_templates() { 1560 $theme_json = new WP_Theme_JSON( 1561 array( 1562 'version' => 1, 1563 'customTemplates' => array( 1564 array( 1565 'name' => 'page-home', 1566 'title' => 'Homepage template', 1567 ), 1568 ), 1569 ) 1570 ); 1571 1572 $page_templates = $theme_json->get_custom_templates(); 1573 1574 $this->assertEqualSetsWithIndex( 1575 $page_templates, 1576 array( 1577 'page-home' => array( 1578 'title' => 'Homepage template', 1579 'postTypes' => array( 'page' ), 1580 ), 1581 ) 1582 ); 1583 } 1584 1585 /** 1586 * @ticket 54336 1587 */ 1588 public function test_get_template_parts() { 1589 $theme_json = new WP_Theme_JSON( 1590 array( 1591 'version' => 1, 1592 'templateParts' => array( 1593 array( 1594 'name' => 'small-header', 1595 'title' => 'Small Header', 1596 'area' => 'header', 1597 ), 1598 ), 1599 ) 1600 ); 1601 1602 $template_parts = $theme_json->get_template_parts(); 1603 1604 $this->assertEqualSetsWithIndex( 1605 $template_parts, 1606 array( 1607 'small-header' => array( 1608 'title' => 'Small Header', 1609 'area' => 'header', 1610 ), 1611 ) 1612 ); 871 1613 } 872 1614 … … 929 1671 ), 930 1672 'typography' => array( 931 'customFontSize' 932 ' customLineHeight'=> true,933 'fontSizes' 1673 'customFontSize' => false, 1674 'lineHeight' => true, 1675 'fontSizes' => array( 934 1676 array( 935 1677 'slug' => 'size-slug', … … 949 1691 /** 950 1692 * @ticket 52991 1693 * @ticket 54336 951 1694 */ 952 1695 public function test_get_editor_settings_no_theme_support() { … … 991 1734 ), 992 1735 'typography' => array( 993 'customFontSize' 994 ' customLineHeight'=> false,1736 'customFontSize' => true, 1737 'lineHeight' => false, 995 1738 ), 996 1739 ), … … 1004 1747 /** 1005 1748 * @ticket 52991 1749 * @ticket 54336 1006 1750 */ 1007 1751 public function test_get_editor_settings_blank() { … … 1012 1756 $actual = WP_Theme_JSON::get_from_editor_settings( array() ); 1013 1757 1014 $this->assert SameSetsWithIndex( $expected, $actual );1758 $this->assertEqualSetsWithIndex( $expected, $actual ); 1015 1759 } 1016 1760 1017 1761 /** 1018 1762 * @ticket 52991 1763 * @ticket 54336 1019 1764 */ 1020 1765 public function test_get_editor_settings_custom_units_can_be_disabled() { 1021 1766 add_theme_support( 'custom-units', array() ); 1022 $input = get_default_block_editor_settings(); 1767 $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() ); 1768 remove_theme_support( 'custom-units' ); 1023 1769 1024 1770 $expected = array( 1025 'units' => array( array() ), 1026 'customPadding' => false, 1027 ); 1028 1029 $actual = WP_Theme_JSON::get_from_editor_settings( $input ); 1030 1031 $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1771 'units' => array( array() ), 1772 'padding' => false, 1773 ); 1774 1775 $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1032 1776 } 1033 1777 1034 1778 /** 1035 1779 * @ticket 52991 1780 * @ticket 54336 1036 1781 */ 1037 1782 public function test_get_editor_settings_custom_units_can_be_enabled() { 1038 1783 add_theme_support( 'custom-units' ); 1039 $input = get_default_block_editor_settings(); 1784 $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() ); 1785 remove_theme_support( 'custom-units' ); 1040 1786 1041 1787 $expected = array( 1042 'units' => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ), 1043 'customPadding' => false, 1044 ); 1045 1046 $actual = WP_Theme_JSON::get_from_editor_settings( $input ); 1047 1048 $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1788 'units' => array( 'px', 'em', 'rem', 'vh', 'vw', '%' ), 1789 'padding' => false, 1790 ); 1791 1792 $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1049 1793 } 1050 1794 1051 1795 /** 1052 1796 * @ticket 52991 1797 * @ticket 54336 1053 1798 */ 1054 1799 public function test_get_editor_settings_custom_units_can_be_filtered() { 1055 1800 add_theme_support( 'custom-units', 'rem', 'em' ); 1056 $input = get_default_block_editor_settings(); 1801 $actual = WP_Theme_JSON::get_from_editor_settings( get_default_block_editor_settings() ); 1802 remove_theme_support( 'custom-units' ); 1057 1803 1058 1804 $expected = array( 1059 'units' => array( 'rem', 'em' ), 1060 'customPadding' => false, 1061 ); 1062 1063 $actual = WP_Theme_JSON::get_from_editor_settings( $input ); 1064 1065 $this->assertSameSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1805 'units' => array( 'rem', 'em' ), 1806 'padding' => false, 1807 ); 1808 $this->assertEqualSetsWithIndex( $expected, $actual['settings']['spacing'] ); 1066 1809 } 1067 1810 -
trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php
r51599 r52049 47 47 /** 48 48 * @ticket 52991 49 * @ticket 54336 49 50 */ 50 51 public function test_translations_are_applied() { … … 53 54 54 55 switch_theme( 'block-theme' ); 55 56 56 $actual = WP_Theme_JSON_Resolver::get_theme_data(); 57 57 … … 63 63 array( 64 64 'color' => array( 65 'custom' => false, 66 'customGradient' => false, 65 67 'palette' => array( 66 68 'theme' => array( … … 86 88 ), 87 89 ), 88 'custom' => false,89 'customGradient' => false,90 90 ), 91 91 'typography' => array( 92 'fontSizes' => array( 92 'customFontSize' => false, 93 'lineHeight' => true, 94 'fontSizes' => array( 93 95 'theme' => array( 94 96 array( … … 99 101 ), 100 102 ), 101 'customFontSize' => false,102 'customLineHeight' => true,103 103 ), 104 104 'spacing' => array( 105 'units' => array( 106 'rem', 107 ), 108 'customPadding' => true, 105 'units' => array( 'rem' ), 106 'padding' => true, 109 107 ), 110 108 'blocks' => array( … … 126 124 $actual->get_settings() 127 125 ); 126 $this->assertSame( 127 $actual->get_custom_templates(), 128 array( 129 'page-home' => array( 130 'title' => 'Szablon strony głównej', 131 'postTypes' => array( 'page' ), 132 ), 133 ) 134 ); 135 $this->assertSame( 136 $actual->get_template_parts(), 137 array( 138 'small-header' => array( 139 'title' => 'Mały nagłówek', 140 'area' => 'header', 141 ), 142 ) 143 ); 128 144 } 129 145 … … 144 160 } 145 161 162 /** 163 * @ticket 54336 164 */ 165 function test_add_theme_supports_are_loaded_for_themes_without_theme_json() { 166 switch_theme( 'default' ); 167 $color_palette = array( 168 array( 169 'name' => 'Primary', 170 'slug' => 'primary', 171 'color' => '#F00', 172 ), 173 array( 174 'name' => 'Secondary', 175 'slug' => 'secondary', 176 'color' => '#0F0', 177 ), 178 array( 179 'name' => 'Tertiary', 180 'slug' => 'tertiary', 181 'color' => '#00F', 182 ), 183 ); 184 add_theme_support( 'editor-color-palette', $color_palette ); 185 add_theme_support( 'custom-line-height' ); 186 187 $settings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); 188 189 remove_theme_support( 'custom-line-height' ); 190 remove_theme_support( 'editor-color-palette' ); 191 192 $this->assertFalse( WP_Theme_JSON_Resolver::theme_has_support() ); 193 $this->assertTrue( $settings['typography']['lineHeight'] ); 194 $this->assertSame( $color_palette, $settings['color']['palette']['theme'] ); 195 } 196 197 /** 198 * Recursively applies ksort to an array. 199 */ 200 private static function recursive_ksort( &$array ) { 201 foreach ( $array as &$value ) { 202 if ( is_array( $value ) ) { 203 self::recursive_ksort( $value ); 204 } 205 } 206 ksort( $array ); 207 } 208 209 /** 210 * @ticket 54336 211 */ 212 function test_merges_child_theme_json_into_parent_theme_json() { 213 switch_theme( 'block-theme-child' ); 214 215 $actual_settings = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); 216 $expected_settings = array( 217 'color' => array( 218 'custom' => false, 219 'customGradient' => false, 220 'gradients' => array( 221 'theme' => array( 222 array( 223 'name' => 'Custom gradient', 224 'gradient' => 'linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%)', 225 'slug' => 'custom-gradient', 226 ), 227 ), 228 ), 229 'palette' => array( 230 'theme' => array( 231 array( 232 'slug' => 'light', 233 'name' => 'Light', 234 'color' => '#f3f4f6', 235 ), 236 array( 237 'slug' => 'primary', 238 'name' => 'Primary', 239 'color' => '#3858e9', 240 ), 241 array( 242 'slug' => 'dark', 243 'name' => 'Dark', 244 'color' => '#111827', 245 ), 246 ), 247 ), 248 'link' => true, 249 ), 250 'typography' => array( 251 'customFontSize' => false, 252 'lineHeight' => true, 253 'fontSizes' => array( 254 'theme' => array( 255 array( 256 'name' => 'Custom', 257 'slug' => 'custom', 258 'size' => '100px', 259 ), 260 ), 261 ), 262 ), 263 'spacing' => array( 264 'units' => array( 'rem' ), 265 'padding' => true, 266 ), 267 'blocks' => array( 268 'core/paragraph' => array( 269 'color' => array( 270 'palette' => array( 271 'theme' => array( 272 array( 273 'slug' => 'light', 274 'name' => 'Light', 275 'color' => '#f5f7f9', 276 ), 277 ), 278 ), 279 ), 280 ), 281 'core/post-title' => array( 282 'color' => array( 283 'palette' => array( 284 'theme' => array( 285 array( 286 'slug' => 'light', 287 'name' => 'Light', 288 'color' => '#f3f4f6', 289 ), 290 ), 291 ), 292 ), 293 ), 294 ), 295 ); 296 self::recursive_ksort( $actual_settings ); 297 self::recursive_ksort( $expected_settings ); 298 299 // Should merge settings. 300 $this->assertSame( 301 $expected_settings, 302 $actual_settings 303 ); 304 305 $this->assertSame( 306 WP_Theme_JSON_Resolver::get_theme_data()->get_custom_templates(), 307 array( 308 'page-home' => array( 309 'title' => 'Homepage', 310 'postTypes' => array( 'page' ), 311 ), 312 ) 313 ); 314 } 146 315 }
Note: See TracChangeset
for help on using the changeset viewer.