Changeset 55192
- Timestamp:
- 02/02/2023 06:50:54 PM (2 years ago)
- Location:
- trunk
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/block-editor.php
r55181 r55192 411 411 $global_styles[] = $block_classes; 412 412 } 413 414 /* 415 * Add the custom CSS as a separate stylesheet so any invalid CSS 416 * entered by users does not break other global styles. 417 */ 418 $editor_settings['styles'][] = array( 419 'css' => wp_get_global_styles_custom_css(), 420 '__unstableType' => 'user', 421 'isGlobalStyles' => true, 422 ); 413 423 } else { 414 424 // If there is no `theme.json` file, ensure base layout styles are still available. -
trunk/src/wp-includes/class-wp-theme-json.php
r55176 r55192 426 426 'textTransform' => null, 427 427 ), 428 'css' => null, 428 429 ); 429 430 … … 1001 1002 if ( in_array( 'presets', $types, true ) ) { 1002 1003 $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins ); 1004 } 1005 1006 return $stylesheet; 1007 } 1008 1009 /** 1010 * Returns the global styles custom css. 1011 * 1012 * @since 6.2.0 1013 * 1014 * @return string 1015 */ 1016 public function get_custom_css() { 1017 // Add the global styles root CSS. 1018 $stylesheet = _wp_array_get( $this->theme_json, array( 'styles', 'css' ), '' ); 1019 1020 // Add the global styles block CSS. 1021 if ( isset( $this->theme_json['styles']['blocks'] ) ) { 1022 foreach ( $this->theme_json['styles']['blocks'] as $name => $node ) { 1023 $custom_block_css = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $name, 'css' ) ); 1024 if ( $custom_block_css ) { 1025 $selector = static::$blocks_metadata[ $name ]['selector']; 1026 $stylesheet .= $this->process_blocks_custom_css( $custom_block_css, $selector ); 1027 } 1028 } 1003 1029 } 1004 1030 … … 2741 2767 } 2742 2768 2743 $output = static::remove_insecure_styles( $input ); 2769 // The global styles custom CSS is not sanitized, but can only be edited by users with 'edit_css' capability. 2770 if ( isset( $input['css'] ) && current_user_can( 'edit_css' ) ) { 2771 $output = $input; 2772 } else { 2773 $output = static::remove_insecure_styles( $input ); 2774 } 2744 2775 2745 2776 /* -
trunk/src/wp-includes/default-filters.php
r55086 r55192 578 578 add_action( 'wp_footer', 'wp_enqueue_global_styles', 1 ); 579 579 580 // Global styles custom CSS. 581 add_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles_custom_css' ); 582 580 583 // Block supports, and other styles parsed and stored in the Style Engine. 581 584 add_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' ); -
trunk/src/wp-includes/global-styles-and-settings.php
r55185 r55192 219 219 220 220 $stylesheet = $styles_variables . $styles_rest; 221 if ( $can_use_cached ) { 222 wp_cache_set( $cache_key, $stylesheet, $cache_group ); 223 } 224 225 return $stylesheet; 226 } 227 228 /** 229 * Gets the global styles custom css from theme.json. 230 * 231 * @since 6.2.0 232 * 233 * @return string Stylesheet. 234 */ 235 function wp_get_global_styles_custom_css() { 236 if ( ! wp_theme_has_theme_json() ) { 237 return ''; 238 } 239 /* 240 * Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme 241 * developer's workflow. 242 * 243 * @todo Replace `WP_DEBUG` once an "in development mode" check is available in Core. 244 */ 245 $can_use_cached = ! WP_DEBUG; 246 247 /* 248 * By using the 'theme_json' group, this data is marked to be non-persistent across requests. 249 * @see `wp_cache_add_non_persistent_groups()`. 250 * 251 * The rationale for this is to make sure derived data from theme.json 252 * is always fresh from the potential modifications done via hooks 253 * that can use dynamic data (modify the stylesheet depending on some option, 254 * settings depending on user permissions, etc.). 255 * See some of the existing hooks to modify theme.json behavior: 256 * @see https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/ 257 * 258 * A different alternative considered was to invalidate the cache upon certain 259 * events such as options add/update/delete, user meta, etc. 260 * It was judged not enough, hence this approach. 261 * @see https://github.com/WordPress/gutenberg/pull/45372 262 */ 263 $cache_key = 'wp_get_global_styles_custom_css'; 264 $cache_group = 'theme_json'; 265 if ( $can_use_cached ) { 266 $cached = wp_cache_get( $cache_key, $cache_group ); 267 if ( $cached ) { 268 return $cached; 269 } 270 } 271 272 $tree = WP_Theme_JSON_Resolver::get_merged_data(); 273 $stylesheet = $tree->get_custom_css(); 274 221 275 if ( $can_use_cached ) { 222 276 wp_cache_set( $cache_key, $stylesheet, $cache_group ); … … 370 424 wp_cache_delete( 'wp_get_global_settings_custom', 'theme_json' ); 371 425 wp_cache_delete( 'wp_get_global_settings_theme', 'theme_json' ); 426 wp_cache_delete( 'wp_get_global_styles_custom_css', 'theme_json' ); 372 427 WP_Theme_JSON_Resolver::clean_cached_data(); 373 428 } -
trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php
r55177 r55192 269 269 270 270 $changes = $this->prepare_item_for_database( $request ); 271 if ( is_wp_error( $changes ) ) { 272 return $changes; 273 } 274 271 275 $result = wp_update_post( wp_slash( (array) $changes ), true, false ); 272 276 if ( is_wp_error( $result ) ) { … … 291 295 * 292 296 * @since 5.9.0 297 * @since 6.2.0 Added validation of styles.css property. 293 298 * 294 299 * @param WP_REST_Request $request Request object. 295 * @return stdClass Changes to pass to wp_update_post.300 * @return stdClass|WP_Error Prepared item on success. WP_Error on when the custom CSS is not valid. 296 301 */ 297 302 protected function prepare_item_for_database( $request ) { … … 313 318 $config = array(); 314 319 if ( isset( $request['styles'] ) ) { 320 if ( isset( $request['styles']['css'] ) ) { 321 $css_validation_result = $this->validate_custom_css( $request['styles']['css'] ); 322 if ( is_wp_error( $css_validation_result ) ) { 323 return $css_validation_result; 324 } 325 } 315 326 $config['styles'] = $request['styles']; 316 327 } elseif ( isset( $existing_config['styles'] ) ) { … … 658 669 return $response; 659 670 } 671 672 /** 673 * Validate style.css as valid CSS. 674 * 675 * Currently just checks for invalid markup. 676 * 677 * @since 6.2.0 678 * 679 * @param string $css CSS to validate. 680 * @return true|WP_Error True if the input was validated, otherwise WP_Error. 681 */ 682 private function validate_custom_css( $css ) { 683 if ( preg_match( '#</?\w+#', $css ) ) { 684 return new WP_Error( 685 'rest_custom_css_illegal_markup', 686 __( 'Markup is not allowed in CSS.' ), 687 array( 'status' => 400 ) 688 ); 689 } 690 return true; 691 } 660 692 } -
trunk/src/wp-includes/script-loader.php
r55179 r55192 2456 2456 2457 2457 /** 2458 * Enqueues the global styles custom css defined via theme.json. 2459 * 2460 * @since 6.2.0 2461 */ 2462 function wp_enqueue_global_styles_custom_css() { 2463 if ( ! wp_is_block_theme() ) { 2464 return; 2465 } 2466 2467 // Don't enqueue Customizer's custom CSS separately. 2468 remove_action( 'wp_head', 'wp_custom_css_cb', 101 ); 2469 2470 $custom_css = wp_get_custom_css(); 2471 $custom_css .= wp_get_global_styles_custom_css(); 2472 2473 if ( ! empty( $custom_css ) ) { 2474 wp_add_inline_style( 'global-styles', $custom_css ); 2475 } 2476 } 2477 2478 /** 2458 2479 * Renders the SVG filters supplied by theme.json. 2459 2480 * -
trunk/tests/phpunit/tests/rest-api/rest-global-styles-controller.php
r55177 r55192 533 533 } 534 534 } 535 536 /** 537 * @covers WP_REST_Global_Styles_Controller::update_item 538 * @ticket 57536 539 */ 540 public function test_update_item_valid_styles_css() { 541 wp_set_current_user( self::$admin_id ); 542 if ( is_multisite() ) { 543 grant_super_admin( self::$admin_id ); 544 } 545 $request = new WP_REST_Request( 'PUT', '/wp/v2/global-styles/' . self::$global_styles_id ); 546 $request->set_body_params( 547 array( 548 'styles' => array( 'css' => 'body { color: red; }' ), 549 ) 550 ); 551 $response = rest_get_server()->dispatch( $request ); 552 $data = $response->get_data(); 553 $this->assertSame( 'body { color: red; }', $data['styles']['css'] ); 554 } 555 556 /** 557 * @covers WP_REST_Global_Styles_Controller::update_item 558 * @ticket 57536 559 */ 560 public function test_update_item_invalid_styles_css() { 561 wp_set_current_user( self::$admin_id ); 562 if ( is_multisite() ) { 563 grant_super_admin( self::$admin_id ); 564 } 565 $request = new WP_REST_Request( 'PUT', '/wp/v2/global-styles/' . self::$global_styles_id ); 566 $request->set_body_params( 567 array( 568 'styles' => array( 'css' => '<p>test</p> body { color: red; }' ), 569 ) 570 ); 571 $response = rest_get_server()->dispatch( $request ); 572 $this->assertErrorResponse( 'rest_custom_css_illegal_markup', $response, 400 ); 573 } 535 574 } -
trunk/tests/phpunit/tests/theme/wpThemeJson.php
r55176 r55192 14 14 */ 15 15 class Tests_Theme_wpThemeJson extends WP_UnitTestCase { 16 17 /** 18 * Administrator ID. 19 * 20 * @var int 21 */ 22 private static $administrator_id; 23 24 /** 25 * User ID. 26 * 27 * @var int 28 */ 29 private static $user_id; 30 31 public static function set_up_before_class() { 32 parent::set_up_before_class(); 33 34 static::$administrator_id = self::factory()->user->create( 35 array( 36 'role' => 'administrator', 37 ) 38 ); 39 40 if ( is_multisite() ) { 41 grant_super_admin( self::$administrator_id ); 42 } 43 44 static::$user_id = self::factory()->user->create(); 45 } 16 46 17 47 /** … … 4506 4536 $this->assertSame( $expected_styles, $theme_json->get_stylesheet() ); 4507 4537 } 4538 4539 /** 4540 * @ticket 57536 4541 */ 4542 public function test_get_custom_css_handles_global_custom_css() { 4543 $theme_json = new WP_Theme_JSON( 4544 array( 4545 'version' => WP_Theme_JSON::LATEST_SCHEMA, 4546 'styles' => array( 4547 'css' => 'body { color:purple; }', 4548 ), 4549 ) 4550 ); 4551 $custom_css = 'body { color:purple; }'; 4552 $this->assertSame( $custom_css, $theme_json->get_custom_css() ); 4553 } 4554 4555 /** 4556 * Tests that custom CSS is kept for users with correct capabilities and removed for others. 4557 * 4558 * @ticket 57536 4559 * 4560 * @dataProvider data_custom_css_for_user_caps 4561 * 4562 * @param string $user_property The property name for current user. 4563 * @param array $expected Expected results. 4564 */ 4565 public function test_custom_css_for_user_caps( $user_property, array $expected ) { 4566 wp_set_current_user( static::${$user_property} ); 4567 4568 $actual = WP_Theme_JSON::remove_insecure_properties( 4569 array( 4570 'version' => WP_Theme_JSON::LATEST_SCHEMA, 4571 'styles' => array( 4572 'css' => 'body { color:purple; }', 4573 'blocks' => array( 4574 'core/separator' => array( 4575 'color' => array( 4576 'background' => 'blue', 4577 ), 4578 ), 4579 ), 4580 ), 4581 ) 4582 ); 4583 4584 $this->assertSameSetsWithIndex( $expected, $actual ); 4585 } 4586 4587 /** 4588 * Data provider. 4589 * 4590 * @return array[] 4591 */ 4592 public function data_custom_css_for_user_caps() { 4593 return array( 4594 'allows custom css for users with caps' => array( 4595 'user_property' => 'administrator_id', 4596 'expected' => array( 4597 'version' => WP_Theme_JSON::LATEST_SCHEMA, 4598 'styles' => array( 4599 'css' => 'body { color:purple; }', 4600 'blocks' => array( 4601 'core/separator' => array( 4602 'color' => array( 4603 'background' => 'blue', 4604 ), 4605 ), 4606 ), 4607 ), 4608 ), 4609 ), 4610 'removes custom css for users without caps' => array( 4611 'user_property' => 'user_id', 4612 'expected' => array( 4613 'version' => WP_Theme_JSON::LATEST_SCHEMA, 4614 'styles' => array( 4615 'blocks' => array( 4616 'core/separator' => array( 4617 'color' => array( 4618 'background' => 'blue', 4619 ), 4620 ), 4621 ), 4622 ), 4623 ), 4624 ), 4625 ); 4626 } 4508 4627 }
Note: See TracChangeset
for help on using the changeset viewer.