| <?php |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| class WP_REST_Global_Styles_Controller extends WP_REST_Posts_Controller { |
| |
| |
| |
| |
| |
| |
| protected $allow_batch = array( 'v1' => false ); |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function __construct( $post_type = 'wp_global_styles' ) { |
| parent::__construct( $post_type ); |
| } |
|
|
| |
| |
| |
| |
| |
| public function register_routes() { |
| register_rest_route( |
| $this->namespace, |
| '/' . $this->rest_base . '/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)/variations', |
| array( |
| array( |
| 'methods' => WP_REST_Server::READABLE, |
| 'callback' => array( $this, 'get_theme_items' ), |
| 'permission_callback' => array( $this, 'get_theme_items_permissions_check' ), |
| 'args' => array( |
| 'stylesheet' => array( |
| 'description' => __( 'The theme identifier' ), |
| 'type' => 'string', |
| ), |
| ), |
| 'allow_batch' => $this->allow_batch, |
| ), |
| ) |
| ); |
|
|
| |
| register_rest_route( |
| $this->namespace, |
| // The route. |
| sprintf( |
| '/%s/themes/(?P<stylesheet>%s)', |
| $this->rest_base, |
| |
| |
| |
| |
| '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?' |
| ), |
| array( |
| array( |
| 'methods' => WP_REST_Server::READABLE, |
| 'callback' => array( $this, 'get_theme_item' ), |
| 'permission_callback' => array( $this, 'get_theme_item_permissions_check' ), |
| 'args' => array( |
| 'stylesheet' => array( |
| 'description' => __( 'The theme identifier' ), |
| 'type' => 'string', |
| 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), |
| ), |
| ), |
| 'allow_batch' => $this->allow_batch, |
| ), |
| ) |
| ); |
|
|
| |
| register_rest_route( |
| $this->namespace, |
| '/' . $this->rest_base . '/(?P<id>[\/\w-]+)', |
| array( |
| array( |
| 'methods' => WP_REST_Server::READABLE, |
| 'callback' => array( $this, 'get_item' ), |
| 'permission_callback' => array( $this, 'get_item_permissions_check' ), |
| 'args' => array( |
| 'id' => array( |
| 'description' => __( 'The id of a template' ), |
| 'type' => 'string', |
| 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), |
| ), |
| ), |
| ), |
| array( |
| 'methods' => WP_REST_Server::EDITABLE, |
| 'callback' => array( $this, 'update_item' ), |
| 'permission_callback' => array( $this, 'update_item_permissions_check' ), |
| 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), |
| ), |
| 'schema' => array( $this, 'get_public_item_schema' ), |
| 'allow_batch' => $this->allow_batch, |
| ) |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function _sanitize_global_styles_callback( $id_or_stylesheet ) { |
| return urldecode( $id_or_stylesheet ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| protected function get_post( $id ) { |
| $error = new WP_Error( |
| 'rest_global_styles_not_found', |
| __( 'No global styles config exist with that id.' ), |
| array( 'status' => 404 ) |
| ); |
|
|
| $id = (int) $id; |
| if ( $id <= 0 ) { |
| return $error; |
| } |
|
|
| $post = get_post( $id ); |
| if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { |
| return $error; |
| } |
|
|
| return $post; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function get_item_permissions_check( $request ) { |
| $post = $this->get_post( $request['id'] ); |
| if ( is_wp_error( $post ) ) { |
| return $post; |
| } |
|
|
| if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) { |
| return new WP_Error( |
| 'rest_forbidden_context', |
| __( 'Sorry, you are not allowed to edit this global style.' ), |
| array( 'status' => rest_authorization_required_code() ) |
| ); |
| } |
|
|
| if ( ! $this->check_read_permission( $post ) ) { |
| return new WP_Error( |
| 'rest_cannot_view', |
| __( 'Sorry, you are not allowed to view this global style.' ), |
| array( 'status' => rest_authorization_required_code() ) |
| ); |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function check_read_permission( $post ) { |
| return current_user_can( 'read_post', $post->ID ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function update_item_permissions_check( $request ) { |
| $post = $this->get_post( $request['id'] ); |
| if ( is_wp_error( $post ) ) { |
| return $post; |
| } |
|
|
| if ( $post && ! $this->check_update_permission( $post ) ) { |
| return new WP_Error( |
| 'rest_cannot_edit', |
| __( 'Sorry, you are not allowed to edit this global style.' ), |
| array( 'status' => rest_authorization_required_code() ) |
| ); |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function prepare_item_for_database( $request ) { |
| $changes = new stdClass(); |
| $changes->ID = $request['id']; |
|
|
| $post = get_post( $request['id'] ); |
| $existing_config = array(); |
| if ( $post ) { |
| $existing_config = json_decode( $post->post_content, true ); |
| $json_decoding_error = json_last_error(); |
| if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) || |
| ! $existing_config['isGlobalStylesUserThemeJSON'] ) { |
| $existing_config = array(); |
| } |
| } |
|
|
| if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) { |
| $config = array(); |
| if ( isset( $request['styles'] ) ) { |
| if ( isset( $request['styles']['css'] ) ) { |
| $css_validation_result = $this->validate_custom_css( $request['styles']['css'] ); |
| if ( is_wp_error( $css_validation_result ) ) { |
| return $css_validation_result; |
| } |
| } |
| $config['styles'] = $request['styles']; |
| } elseif ( isset( $existing_config['styles'] ) ) { |
| $config['styles'] = $existing_config['styles']; |
| } |
|
|
| |
| $variations = WP_Theme_JSON_Resolver::get_style_variations( 'block' ); |
| wp_register_block_style_variations_from_theme_json_partials( $variations ); |
|
|
| if ( isset( $request['settings'] ) ) { |
| $config['settings'] = $request['settings']; |
| } elseif ( isset( $existing_config['settings'] ) ) { |
| $config['settings'] = $existing_config['settings']; |
| } |
| $config['isGlobalStylesUserThemeJSON'] = true; |
| $config['version'] = WP_Theme_JSON::LATEST_SCHEMA; |
| $changes->post_content = wp_json_encode( $config ); |
| } |
|
|
| |
| if ( isset( $request['title'] ) ) { |
| if ( is_string( $request['title'] ) ) { |
| $changes->post_title = $request['title']; |
| } elseif ( ! empty( $request['title']['raw'] ) ) { |
| $changes->post_title = $request['title']['raw']; |
| } |
| } |
|
|
| return $changes; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function prepare_item_for_response( $post, $request ) { |
| $raw_config = json_decode( $post->post_content, true ); |
| $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON']; |
| $config = array(); |
| $theme_json = null; |
| if ( $is_global_styles_user_theme_json ) { |
| $theme_json = new WP_Theme_JSON( $raw_config, 'custom' ); |
| $config = $theme_json->get_raw_data(); |
| } |
|
|
| |
| $fields = $this->get_fields_for_response( $request ); |
| $data = array(); |
|
|
| if ( rest_is_field_included( 'id', $fields ) ) { |
| $data['id'] = $post->ID; |
| } |
|
|
| if ( rest_is_field_included( 'title', $fields ) ) { |
| $data['title'] = array(); |
| } |
| if ( rest_is_field_included( 'title.raw', $fields ) ) { |
| $data['title']['raw'] = $post->post_title; |
| } |
| if ( rest_is_field_included( 'title.rendered', $fields ) ) { |
| add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); |
|
|
| $data['title']['rendered'] = get_the_title( $post->ID ); |
|
|
| remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); |
| } |
|
|
| if ( rest_is_field_included( 'settings', $fields ) ) { |
| $data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass(); |
| } |
|
|
| if ( rest_is_field_included( 'styles', $fields ) ) { |
| $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass(); |
| } |
|
|
| $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
| $data = $this->add_additional_fields_to_object( $data, $request ); |
| $data = $this->filter_response_by_context( $data, $context ); |
|
|
| |
| $response = rest_ensure_response( $data ); |
|
|
| if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { |
| $links = $this->prepare_links( $post->ID ); |
|
|
| |
| if ( $theme_json ) { |
| $resolved_theme_uris = WP_Theme_JSON_Resolver::get_resolved_theme_uris( $theme_json ); |
| if ( ! empty( $resolved_theme_uris ) ) { |
| $links['https://api.w.org/theme-file'] = $resolved_theme_uris; |
| } |
| } |
|
|
| $response->add_links( $links ); |
| if ( ! empty( $links['self']['href'] ) ) { |
| $actions = $this->get_available_actions( $post, $request ); |
| $self = $links['self']['href']; |
| foreach ( $actions as $rel ) { |
| $response->add_link( $rel, $self ); |
| } |
| } |
| } |
|
|
| return $response; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function prepare_links( $id ) { |
| $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); |
|
|
| $links = array( |
| 'self' => array( |
| 'href' => rest_url( trailingslashit( $base ) . $id ), |
| ), |
| 'about' => array( |
| 'href' => rest_url( 'wp/v2/types/' . $this->post_type ), |
| ), |
| ); |
|
|
| if ( post_type_supports( $this->post_type, 'revisions' ) ) { |
| $revisions = wp_get_latest_revision_id_and_total_count( $id ); |
| $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0; |
| $revisions_base = sprintf( '/%s/%d/revisions', $base, $id ); |
| $links['version-history'] = array( |
| 'href' => rest_url( $revisions_base ), |
| 'count' => $revisions_count, |
| ); |
| } |
|
|
| return $links; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function get_available_actions( $post, $request ) { |
| $rels = array(); |
|
|
| $post_type = get_post_type_object( $post->post_type ); |
| if ( current_user_can( $post_type->cap->publish_posts ) ) { |
| $rels[] = 'https://api.w.org/action-publish'; |
| } |
|
|
| if ( current_user_can( 'edit_css' ) ) { |
| $rels[] = 'https://api.w.org/action-edit-css'; |
| } |
|
|
| return $rels; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function get_collection_params() { |
| return array(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function get_item_schema() { |
| if ( $this->schema ) { |
| return $this->add_additional_fields_schema( $this->schema ); |
| } |
|
|
| $schema = array( |
| '$schema' => 'http://json-schema.org/draft-04/schema#', |
| 'title' => $this->post_type, |
| 'type' => 'object', |
| 'properties' => array( |
| 'id' => array( |
| 'description' => __( 'ID of global styles config.' ), |
| 'type' => 'string', |
| 'context' => array( 'embed', 'view', 'edit' ), |
| 'readonly' => true, |
| ), |
| 'styles' => array( |
| 'description' => __( 'Global styles.' ), |
| 'type' => array( 'object' ), |
| 'context' => array( 'view', 'edit' ), |
| ), |
| 'settings' => array( |
| 'description' => __( 'Global settings.' ), |
| 'type' => array( 'object' ), |
| 'context' => array( 'view', 'edit' ), |
| ), |
| 'title' => array( |
| 'description' => __( 'Title of the global styles variation.' ), |
| 'type' => array( 'object', 'string' ), |
| 'default' => '', |
| 'context' => array( 'embed', 'view', 'edit' ), |
| 'properties' => array( |
| 'raw' => array( |
| 'description' => __( 'Title for the global styles variation, as it exists in the database.' ), |
| 'type' => 'string', |
| 'context' => array( 'view', 'edit', 'embed' ), |
| ), |
| 'rendered' => array( |
| 'description' => __( 'HTML title for the post, transformed for display.' ), |
| 'type' => 'string', |
| 'context' => array( 'view', 'edit', 'embed' ), |
| 'readonly' => true, |
| ), |
| ), |
| ), |
| ), |
| ); |
|
|
| $this->schema = $schema; |
|
|
| return $this->add_additional_fields_schema( $this->schema ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function get_theme_item_permissions_check( $request ) { |
| |
| |
| |
| |
| if ( ! current_user_can( 'edit_theme_options' ) ) { |
| return new WP_Error( |
| 'rest_cannot_manage_global_styles', |
| __( 'Sorry, you are not allowed to access the global styles on this site.' ), |
| array( |
| 'status' => rest_authorization_required_code(), |
| ) |
| ); |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function get_theme_item( $request ) { |
| if ( get_stylesheet() !== $request['stylesheet'] ) { |
| |
| return new WP_Error( |
| 'rest_theme_not_found', |
| __( 'Theme not found.' ), |
| array( 'status' => 404 ) |
| ); |
| } |
|
|
| $theme = WP_Theme_JSON_Resolver::get_merged_data( 'theme' ); |
| $fields = $this->get_fields_for_response( $request ); |
| $data = array(); |
|
|
| if ( rest_is_field_included( 'settings', $fields ) ) { |
| $data['settings'] = $theme->get_settings(); |
| } |
|
|
| if ( rest_is_field_included( 'styles', $fields ) ) { |
| $raw_data = $theme->get_raw_data(); |
| $data['styles'] = isset( $raw_data['styles'] ) ? $raw_data['styles'] : array(); |
| } |
|
|
| $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
| $data = $this->add_additional_fields_to_object( $data, $request ); |
| $data = $this->filter_response_by_context( $data, $context ); |
|
|
| $response = rest_ensure_response( $data ); |
|
|
| if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { |
| $links = array( |
| 'self' => array( |
| 'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ), |
| ), |
| ); |
| $resolved_theme_uris = WP_Theme_JSON_Resolver::get_resolved_theme_uris( $theme ); |
| if ( ! empty( $resolved_theme_uris ) ) { |
| $links['https://api.w.org/theme-file'] = $resolved_theme_uris; |
| } |
| $response->add_links( $links ); |
| } |
|
|
| return $response; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function get_theme_items_permissions_check( $request ) { |
| |
| |
| |
| |
| if ( ! current_user_can( 'edit_theme_options' ) ) { |
| return new WP_Error( |
| 'rest_cannot_manage_global_styles', |
| __( 'Sorry, you are not allowed to access the global styles on this site.' ), |
| array( |
| 'status' => rest_authorization_required_code(), |
| ) |
| ); |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function get_theme_items( $request ) { |
| if ( get_stylesheet() !== $request['stylesheet'] ) { |
| |
| return new WP_Error( |
| 'rest_theme_not_found', |
| __( 'Theme not found.' ), |
| array( 'status' => 404 ) |
| ); |
| } |
|
|
| $response = array(); |
|
|
| |
| $partials = WP_Theme_JSON_Resolver::get_style_variations( 'block' ); |
| wp_register_block_style_variations_from_theme_json_partials( $partials ); |
|
|
| $variations = WP_Theme_JSON_Resolver::get_style_variations(); |
| foreach ( $variations as $variation ) { |
| $variation_theme_json = new WP_Theme_JSON( $variation ); |
| $resolved_theme_uris = WP_Theme_JSON_Resolver::get_resolved_theme_uris( $variation_theme_json ); |
| $data = rest_ensure_response( $variation ); |
| if ( ! empty( $resolved_theme_uris ) ) { |
| $data->add_links( |
| array( |
| 'https://api.w.org/theme-file' => $resolved_theme_uris, |
| ) |
| ); |
| } |
| $response[] = $this->prepare_response_for_collection( $data ); |
| } |
|
|
| return rest_ensure_response( $response ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function validate_custom_css( $css ) { |
| if ( preg_match( '#</?\w+#', $css ) ) { |
| return new WP_Error( |
| 'rest_custom_css_illegal_markup', |
| __( 'Markup is not allowed in CSS.' ), |
| array( 'status' => 400 ) |
| ); |
| } |
| return true; |
| } |
| } |
|
|