| | <?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; |
| | } |
| | } |
| |
|