[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-global-styles-controller.php (source)

   1  <?php
   2  /**
   3   * REST API: WP_REST_Global_Styles_Controller class
   4   *
   5   * @package    WordPress
   6   * @subpackage REST_API
   7   * @since 5.9.0
   8   */
   9  
  10  /**
  11   * Base Global Styles REST API Controller.
  12   */
  13  class WP_REST_Global_Styles_Controller extends WP_REST_Controller {
  14  
  15      /**
  16       * Post type.
  17       *
  18       * @since 5.9.0
  19       * @var string
  20       */
  21      protected $post_type;
  22  
  23      /**
  24       * Constructor.
  25       * @since 5.9.0
  26       */
  27  	public function __construct() {
  28          $this->namespace = 'wp/v2';
  29          $this->rest_base = 'global-styles';
  30          $this->post_type = 'wp_global_styles';
  31      }
  32  
  33      /**
  34       * Registers the controllers routes.
  35       *
  36       * @since 5.9.0
  37       */
  38  	public function register_routes() {
  39          register_rest_route(
  40              $this->namespace,
  41              '/' . $this->rest_base . '/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)/variations',
  42              array(
  43                  array(
  44                      'methods'             => WP_REST_Server::READABLE,
  45                      'callback'            => array( $this, 'get_theme_items' ),
  46                      'permission_callback' => array( $this, 'get_theme_items_permissions_check' ),
  47                      'args'                => array(
  48                          'stylesheet' => array(
  49                              'description' => __( 'The theme identifier' ),
  50                              'type'        => 'string',
  51                          ),
  52                      ),
  53                  ),
  54              )
  55          );
  56  
  57          // List themes global styles.
  58          register_rest_route(
  59              $this->namespace,
  60              // The route.
  61              sprintf(
  62                  '/%s/themes/(?P<stylesheet>%s)',
  63                  $this->rest_base,
  64                  /*
  65                   * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
  66                   * Excludes invalid directory name characters: `/:<>*?"|`.
  67                   */
  68                  '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?'
  69              ),
  70              array(
  71                  array(
  72                      'methods'             => WP_REST_Server::READABLE,
  73                      'callback'            => array( $this, 'get_theme_item' ),
  74                      'permission_callback' => array( $this, 'get_theme_item_permissions_check' ),
  75                      'args'                => array(
  76                          'stylesheet' => array(
  77                              'description'       => __( 'The theme identifier' ),
  78                              'type'              => 'string',
  79                              'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
  80                          ),
  81                      ),
  82                  ),
  83              )
  84          );
  85  
  86          // Lists/updates a single global style variation based on the given id.
  87          register_rest_route(
  88              $this->namespace,
  89              '/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
  90              array(
  91                  array(
  92                      'methods'             => WP_REST_Server::READABLE,
  93                      'callback'            => array( $this, 'get_item' ),
  94                      'permission_callback' => array( $this, 'get_item_permissions_check' ),
  95                      'args'                => array(
  96                          'id' => array(
  97                              'description'       => __( 'The id of a template' ),
  98                              'type'              => 'string',
  99                              'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
 100                          ),
 101                      ),
 102                  ),
 103                  array(
 104                      'methods'             => WP_REST_Server::EDITABLE,
 105                      'callback'            => array( $this, 'update_item' ),
 106                      'permission_callback' => array( $this, 'update_item_permissions_check' ),
 107                      'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
 108                  ),
 109                  'schema' => array( $this, 'get_public_item_schema' ),
 110              )
 111          );
 112      }
 113  
 114      /**
 115       * Sanitize the global styles ID or stylesheet to decode endpoint.
 116       * For example, `wp/v2/global-styles/twentytwentytwo%200.4.0`
 117       * would be decoded to `twentytwentytwo 0.4.0`.
 118       *
 119       * @since 5.9.0
 120       *
 121       * @param string $id_or_stylesheet Global styles ID or stylesheet.
 122       * @return string Sanitized global styles ID or stylesheet.
 123       */
 124  	public function _sanitize_global_styles_callback( $id_or_stylesheet ) {
 125          return urldecode( $id_or_stylesheet );
 126      }
 127  
 128      /**
 129       * Get the post, if the ID is valid.
 130       *
 131       * @since 5.9.0
 132       *
 133       * @param int $id Supplied ID.
 134       * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
 135       */
 136  	protected function get_post( $id ) {
 137          $error = new WP_Error(
 138              'rest_global_styles_not_found',
 139              __( 'No global styles config exist with that id.' ),
 140              array( 'status' => 404 )
 141          );
 142  
 143          $id = (int) $id;
 144          if ( $id <= 0 ) {
 145              return $error;
 146          }
 147  
 148          $post = get_post( $id );
 149          if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
 150              return $error;
 151          }
 152  
 153          return $post;
 154      }
 155  
 156      /**
 157       * Checks if a given request has access to read a single global style.
 158       *
 159       * @since 5.9.0
 160       *
 161       * @param WP_REST_Request $request Full details about the request.
 162       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 163       */
 164  	public function get_item_permissions_check( $request ) {
 165          $post = $this->get_post( $request['id'] );
 166          if ( is_wp_error( $post ) ) {
 167              return $post;
 168          }
 169  
 170          if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
 171              return new WP_Error(
 172                  'rest_forbidden_context',
 173                  __( 'Sorry, you are not allowed to edit this global style.' ),
 174                  array( 'status' => rest_authorization_required_code() )
 175              );
 176          }
 177  
 178          if ( ! $this->check_read_permission( $post ) ) {
 179              return new WP_Error(
 180                  'rest_cannot_view',
 181                  __( 'Sorry, you are not allowed to view this global style.' ),
 182                  array( 'status' => rest_authorization_required_code() )
 183              );
 184          }
 185  
 186          return true;
 187      }
 188  
 189      /**
 190       * Checks if a global style can be read.
 191       *
 192       * @since 5.9.0
 193       *
 194       * @param WP_Post $post Post object.
 195       * @return bool Whether the post can be read.
 196       */
 197  	protected function check_read_permission( $post ) {
 198          return current_user_can( 'read_post', $post->ID );
 199      }
 200  
 201      /**
 202       * Returns the given global styles config.
 203       *
 204       * @since 5.9.0
 205       *
 206       * @param WP_REST_Request $request The request instance.
 207       *
 208       * @return WP_REST_Response|WP_Error
 209       */
 210  	public function get_item( $request ) {
 211          $post = $this->get_post( $request['id'] );
 212          if ( is_wp_error( $post ) ) {
 213              return $post;
 214          }
 215  
 216          return $this->prepare_item_for_response( $post, $request );
 217      }
 218  
 219      /**
 220       * Checks if a given request has access to write a single global styles config.
 221       *
 222       * @since 5.9.0
 223       *
 224       * @param WP_REST_Request $request Full details about the request.
 225       * @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
 226       */
 227  	public function update_item_permissions_check( $request ) {
 228          $post = $this->get_post( $request['id'] );
 229          if ( is_wp_error( $post ) ) {
 230              return $post;
 231          }
 232  
 233          if ( $post && ! $this->check_update_permission( $post ) ) {
 234              return new WP_Error(
 235                  'rest_cannot_edit',
 236                  __( 'Sorry, you are not allowed to edit this global style.' ),
 237                  array( 'status' => rest_authorization_required_code() )
 238              );
 239          }
 240  
 241          return true;
 242      }
 243  
 244      /**
 245       * Checks if a global style can be edited.
 246       *
 247       * @since 5.9.0
 248       *
 249       * @param WP_Post $post Post object.
 250       * @return bool Whether the post can be edited.
 251       */
 252  	protected function check_update_permission( $post ) {
 253          return current_user_can( 'edit_post', $post->ID );
 254      }
 255  
 256      /**
 257       * Updates a single global style config.
 258       *
 259       * @since 5.9.0
 260       *
 261       * @param WP_REST_Request $request Full details about the request.
 262       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 263       */
 264  	public function update_item( $request ) {
 265          $post_before = $this->get_post( $request['id'] );
 266          if ( is_wp_error( $post_before ) ) {
 267              return $post_before;
 268          }
 269  
 270          $changes = $this->prepare_item_for_database( $request );
 271          if ( is_wp_error( $changes ) ) {
 272              return $changes;
 273          }
 274  
 275          $result = wp_update_post( wp_slash( (array) $changes ), true, false );
 276          if ( is_wp_error( $result ) ) {
 277              return $result;
 278          }
 279  
 280          $post          = get_post( $request['id'] );
 281          $fields_update = $this->update_additional_fields_for_object( $post, $request );
 282          if ( is_wp_error( $fields_update ) ) {
 283              return $fields_update;
 284          }
 285  
 286          wp_after_insert_post( $post, true, $post_before );
 287  
 288          $response = $this->prepare_item_for_response( $post, $request );
 289  
 290          return rest_ensure_response( $response );
 291      }
 292  
 293      /**
 294       * Prepares a single global styles config for update.
 295       *
 296       * @since 5.9.0
 297       * @since 6.2.0 Added validation of styles.css property.
 298       *
 299       * @param WP_REST_Request $request Request object.
 300       * @return stdClass|WP_Error Prepared item on success. WP_Error on when the custom CSS is not valid.
 301       */
 302  	protected function prepare_item_for_database( $request ) {
 303          $changes     = new stdClass();
 304          $changes->ID = $request['id'];
 305  
 306          $post            = get_post( $request['id'] );
 307          $existing_config = array();
 308          if ( $post ) {
 309              $existing_config     = json_decode( $post->post_content, true );
 310              $json_decoding_error = json_last_error();
 311              if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) ||
 312                  ! $existing_config['isGlobalStylesUserThemeJSON'] ) {
 313                  $existing_config = array();
 314              }
 315          }
 316  
 317          if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) {
 318              $config = array();
 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                  }
 326                  $config['styles'] = $request['styles'];
 327              } elseif ( isset( $existing_config['styles'] ) ) {
 328                  $config['styles'] = $existing_config['styles'];
 329              }
 330              if ( isset( $request['settings'] ) ) {
 331                  $config['settings'] = $request['settings'];
 332              } elseif ( isset( $existing_config['settings'] ) ) {
 333                  $config['settings'] = $existing_config['settings'];
 334              }
 335              $config['isGlobalStylesUserThemeJSON'] = true;
 336              $config['version']                     = WP_Theme_JSON::LATEST_SCHEMA;
 337              $changes->post_content                 = wp_json_encode( $config );
 338          }
 339  
 340          // Post title.
 341          if ( isset( $request['title'] ) ) {
 342              if ( is_string( $request['title'] ) ) {
 343                  $changes->post_title = $request['title'];
 344              } elseif ( ! empty( $request['title']['raw'] ) ) {
 345                  $changes->post_title = $request['title']['raw'];
 346              }
 347          }
 348  
 349          return $changes;
 350      }
 351  
 352      /**
 353       * Prepare a global styles config output for response.
 354       *
 355       * @since 5.9.0
 356       *
 357       * @param WP_Post         $post    Global Styles post object.
 358       * @param WP_REST_Request $request Request object.
 359       * @return WP_REST_Response Response object.
 360       */
 361  	public function prepare_item_for_response( $post, $request ) {
 362          $raw_config                       = json_decode( $post->post_content, true );
 363          $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON'];
 364          $config                           = array();
 365          if ( $is_global_styles_user_theme_json ) {
 366              $config = ( new WP_Theme_JSON( $raw_config, 'custom' ) )->get_raw_data();
 367          }
 368  
 369          // Base fields for every post.
 370          $fields = $this->get_fields_for_response( $request );
 371          $data   = array();
 372  
 373          if ( rest_is_field_included( 'id', $fields ) ) {
 374              $data['id'] = $post->ID;
 375          }
 376  
 377          if ( rest_is_field_included( 'title', $fields ) ) {
 378              $data['title'] = array();
 379          }
 380          if ( rest_is_field_included( 'title.raw', $fields ) ) {
 381              $data['title']['raw'] = $post->post_title;
 382          }
 383          if ( rest_is_field_included( 'title.rendered', $fields ) ) {
 384              add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
 385  
 386              $data['title']['rendered'] = get_the_title( $post->ID );
 387  
 388              remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
 389          }
 390  
 391          if ( rest_is_field_included( 'settings', $fields ) ) {
 392              $data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass();
 393          }
 394  
 395          if ( rest_is_field_included( 'styles', $fields ) ) {
 396              $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass();
 397          }
 398  
 399          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 400          $data    = $this->add_additional_fields_to_object( $data, $request );
 401          $data    = $this->filter_response_by_context( $data, $context );
 402  
 403          // Wrap the data in a response object.
 404          $response = rest_ensure_response( $data );
 405  
 406          if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
 407              $links = $this->prepare_links( $post->ID );
 408              $response->add_links( $links );
 409              if ( ! empty( $links['self']['href'] ) ) {
 410                  $actions = $this->get_available_actions();
 411                  $self    = $links['self']['href'];
 412                  foreach ( $actions as $rel ) {
 413                      $response->add_link( $rel, $self );
 414                  }
 415              }
 416          }
 417  
 418          return $response;
 419      }
 420  
 421      /**
 422       * Prepares links for the request.
 423       *
 424       * @since 5.9.0
 425       * @since 6.3.0 Adds revisions count and rest URL href to version-history.
 426       *
 427       * @param integer $id ID.
 428       * @return array Links for the given post.
 429       */
 430  	protected function prepare_links( $id ) {
 431          $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
 432  
 433          $links = array(
 434              'self' => array(
 435                  'href' => rest_url( trailingslashit( $base ) . $id ),
 436              ),
 437          );
 438  
 439          if ( post_type_supports( $this->post_type, 'revisions' ) ) {
 440              $revisions                = wp_get_latest_revision_id_and_total_count( $id );
 441              $revisions_count          = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
 442              $revisions_base           = sprintf( '/%s/%d/revisions', $base, $id );
 443              $links['version-history'] = array(
 444                  'href'  => rest_url( $revisions_base ),
 445                  'count' => $revisions_count,
 446              );
 447          }
 448  
 449          return $links;
 450      }
 451  
 452      /**
 453       * Get the link relations available for the post and current user.
 454       *
 455       * @since 5.9.0
 456       * @since 6.2.0 Added 'edit-css' action.
 457       *
 458       * @return array List of link relations.
 459       */
 460  	protected function get_available_actions() {
 461          $rels = array();
 462  
 463          $post_type = get_post_type_object( $this->post_type );
 464          if ( current_user_can( $post_type->cap->publish_posts ) ) {
 465              $rels[] = 'https://api.w.org/action-publish';
 466          }
 467  
 468          if ( current_user_can( 'edit_css' ) ) {
 469              $rels[] = 'https://api.w.org/action-edit-css';
 470          }
 471  
 472          return $rels;
 473      }
 474  
 475      /**
 476       * Overwrites the default protected title format.
 477       *
 478       * By default, WordPress will show password protected posts with a title of
 479       * "Protected: %s", as the REST API communicates the protected status of a post
 480       * in a machine readable format, we remove the "Protected: " prefix.
 481       *
 482       * @since 5.9.0
 483       *
 484       * @return string Protected title format.
 485       */
 486  	public function protected_title_format() {
 487          return '%s';
 488      }
 489  
 490      /**
 491       * Retrieves the query params for the global styles collection.
 492       *
 493       * @since 5.9.0
 494       *
 495       * @return array Collection parameters.
 496       */
 497  	public function get_collection_params() {
 498          return array();
 499      }
 500  
 501      /**
 502       * Retrieves the global styles type' schema, conforming to JSON Schema.
 503       *
 504       * @since 5.9.0
 505       *
 506       * @return array Item schema data.
 507       */
 508  	public function get_item_schema() {
 509          if ( $this->schema ) {
 510              return $this->add_additional_fields_schema( $this->schema );
 511          }
 512  
 513          $schema = array(
 514              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 515              'title'      => $this->post_type,
 516              'type'       => 'object',
 517              'properties' => array(
 518                  'id'       => array(
 519                      'description' => __( 'ID of global styles config.' ),
 520                      'type'        => 'string',
 521                      'context'     => array( 'embed', 'view', 'edit' ),
 522                      'readonly'    => true,
 523                  ),
 524                  'styles'   => array(
 525                      'description' => __( 'Global styles.' ),
 526                      'type'        => array( 'object' ),
 527                      'context'     => array( 'view', 'edit' ),
 528                  ),
 529                  'settings' => array(
 530                      'description' => __( 'Global settings.' ),
 531                      'type'        => array( 'object' ),
 532                      'context'     => array( 'view', 'edit' ),
 533                  ),
 534                  'title'    => array(
 535                      'description' => __( 'Title of the global styles variation.' ),
 536                      'type'        => array( 'object', 'string' ),
 537                      'default'     => '',
 538                      'context'     => array( 'embed', 'view', 'edit' ),
 539                      'properties'  => array(
 540                          'raw'      => array(
 541                              'description' => __( 'Title for the global styles variation, as it exists in the database.' ),
 542                              'type'        => 'string',
 543                              'context'     => array( 'view', 'edit', 'embed' ),
 544                          ),
 545                          'rendered' => array(
 546                              'description' => __( 'HTML title for the post, transformed for display.' ),
 547                              'type'        => 'string',
 548                              'context'     => array( 'view', 'edit', 'embed' ),
 549                              'readonly'    => true,
 550                          ),
 551                      ),
 552                  ),
 553              ),
 554          );
 555  
 556          $this->schema = $schema;
 557  
 558          return $this->add_additional_fields_schema( $this->schema );
 559      }
 560  
 561      /**
 562       * Checks if a given request has access to read a single theme global styles config.
 563       *
 564       * @since 5.9.0
 565       *
 566       * @param WP_REST_Request $request Full details about the request.
 567       * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
 568       */
 569  	public function get_theme_item_permissions_check( $request ) {
 570          /*
 571           * Verify if the current user has edit_theme_options capability.
 572           * This capability is required to edit/view/delete templates.
 573           */
 574          if ( ! current_user_can( 'edit_theme_options' ) ) {
 575              return new WP_Error(
 576                  'rest_cannot_manage_global_styles',
 577                  __( 'Sorry, you are not allowed to access the global styles on this site.' ),
 578                  array(
 579                      'status' => rest_authorization_required_code(),
 580                  )
 581              );
 582          }
 583  
 584          return true;
 585      }
 586  
 587      /**
 588       * Returns the given theme global styles config.
 589       *
 590       * @since 5.9.0
 591       *
 592       * @param WP_REST_Request $request The request instance.
 593       * @return WP_REST_Response|WP_Error
 594       */
 595  	public function get_theme_item( $request ) {
 596          if ( get_stylesheet() !== $request['stylesheet'] ) {
 597              // This endpoint only supports the active theme for now.
 598              return new WP_Error(
 599                  'rest_theme_not_found',
 600                  __( 'Theme not found.' ),
 601                  array( 'status' => 404 )
 602              );
 603          }
 604  
 605          $theme  = WP_Theme_JSON_Resolver::get_merged_data( 'theme' );
 606          $fields = $this->get_fields_for_response( $request );
 607          $data   = array();
 608  
 609          if ( rest_is_field_included( 'settings', $fields ) ) {
 610              $data['settings'] = $theme->get_settings();
 611          }
 612  
 613          if ( rest_is_field_included( 'styles', $fields ) ) {
 614              $raw_data       = $theme->get_raw_data();
 615              $data['styles'] = isset( $raw_data['styles'] ) ? $raw_data['styles'] : array();
 616          }
 617  
 618          $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 619          $data    = $this->add_additional_fields_to_object( $data, $request );
 620          $data    = $this->filter_response_by_context( $data, $context );
 621  
 622          $response = rest_ensure_response( $data );
 623  
 624          if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
 625              $links = array(
 626                  'self' => array(
 627                      'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ),
 628                  ),
 629              );
 630              $response->add_links( $links );
 631          }
 632  
 633          return $response;
 634      }
 635  
 636      /**
 637       * Checks if a given request has access to read a single theme global styles config.
 638       *
 639       * @since 6.0.0
 640       *
 641       * @param WP_REST_Request $request Full details about the request.
 642       * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
 643       */
 644  	public function get_theme_items_permissions_check( $request ) {
 645          /*
 646           * Verify if the current user has edit_theme_options capability.
 647           * This capability is required to edit/view/delete templates.
 648           */
 649          if ( ! current_user_can( 'edit_theme_options' ) ) {
 650              return new WP_Error(
 651                  'rest_cannot_manage_global_styles',
 652                  __( 'Sorry, you are not allowed to access the global styles on this site.' ),
 653                  array(
 654                      'status' => rest_authorization_required_code(),
 655                  )
 656              );
 657          }
 658  
 659          return true;
 660      }
 661  
 662      /**
 663       * Returns the given theme global styles variations.
 664       *
 665       * @since 6.0.0
 666       * @since 6.2.0 Returns parent theme variations, if they exist.
 667       *
 668       * @param WP_REST_Request $request The request instance.
 669       *
 670       * @return WP_REST_Response|WP_Error
 671       */
 672  	public function get_theme_items( $request ) {
 673          if ( get_stylesheet() !== $request['stylesheet'] ) {
 674              // This endpoint only supports the active theme for now.
 675              return new WP_Error(
 676                  'rest_theme_not_found',
 677                  __( 'Theme not found.' ),
 678                  array( 'status' => 404 )
 679              );
 680          }
 681  
 682          $variations = WP_Theme_JSON_Resolver::get_style_variations();
 683  
 684          return rest_ensure_response( $variations );
 685      }
 686  
 687      /**
 688       * Validate style.css as valid CSS.
 689       *
 690       * Currently just checks for invalid markup.
 691       *
 692       * @since 6.2.0
 693       * @since 6.4.0 Changed method visibility to protected.
 694       *
 695       * @param string $css CSS to validate.
 696       * @return true|WP_Error True if the input was validated, otherwise WP_Error.
 697       */
 698  	protected function validate_custom_css( $css ) {
 699          if ( preg_match( '#</?\w+#', $css ) ) {
 700              return new WP_Error(
 701                  'rest_custom_css_illegal_markup',
 702                  __( 'Markup is not allowed in CSS.' ),
 703                  array( 'status' => 400 )
 704              );
 705          }
 706          return true;
 707      }
 708  }


Generated : Thu Apr 25 08:20:02 2024 Cross-referenced by PHPXref