diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index 9b39213fd1422..d2f9e0e3c8dd0 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -429,12 +429,14 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post $user_cpt = array(); $post_type_filter = 'wp_global_styles'; $stylesheet = $theme->get_stylesheet(); + $post_name = "wp-global-styles-{urlencode($stylesheet)}"; $args = array( 'posts_per_page' => 1, 'orderby' => 'date', - 'order' => 'desc', + 'order' => 'asc', 'post_type' => $post_type_filter, 'post_status' => $post_status_filter, + 'post_name' => $post_name, 'ignore_sticky_posts' => true, 'no_found_rows' => true, 'update_post_meta_cache' => false, @@ -453,19 +455,15 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post if ( count( $recent_posts ) === 1 ) { $user_cpt = get_object_vars( $recent_posts[0] ); } elseif ( $create_post ) { - $cpt_post_id = wp_insert_post( + $cpt_post_id = self::add_user_global_styles_variation( array( - 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', - 'post_status' => 'publish', - 'post_title' => 'Custom Styles', // Do not make string translatable, see https://core.trac.wordpress.org/ticket/54518. - 'post_type' => $post_type_filter, - 'post_name' => sprintf( 'wp-global-styles-%s', urlencode( $stylesheet ) ), - 'tax_input' => array( - 'wp_theme' => array( $stylesheet ), - ), - ), - true + 'title' => 'Custom Styles', // Do not make string translatable, see https://core.trac.wordpress.org/ticket/54518. + 'global_styles' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', + 'stylesheet' => $stylesheet, + 'post_name' => $post_name, + ) ); + if ( ! is_wp_error( $cpt_post_id ) ) { $user_cpt = get_object_vars( get_post( $cpt_post_id ) ); } @@ -474,6 +472,120 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post return $user_cpt; } + /** + * Saves a new user variation into the database. + * + * @param array $args { + * Arguments to add a style variation. + * + * @type string title Required. Global styles variation name. + * @type string global_styles Required. Global styles settings as a JSON string. + * @type string $stylesheet Required. Slug of the theme associated with these global styles. + * @type string $post_name Optional. Post name. + * } + * + * @return int|WP_Error Post ID of the new variation or error if insertion failed. + */ + public static function add_user_global_styles_variation( $args ) { + $theme = wp_get_theme(); + + /* + * Bail early if the theme does not support a theme.json. + * + * Since wp_theme_has_theme_json only supports the active + * theme, the extra condition for whether $theme is the active theme is + * present here. + */ + if ( $theme->get_stylesheet() === $args['stylesheet'] && ! wp_theme_has_theme_json() ) { + return new WP_Error( __( 'Theme does not have theme.json', 'gutenberg' ) ); + } + + $post_args = array( + 'post_content' => $args['global_styles'], + 'post_status' => 'publish', + 'post_title' => $args['title'], + 'post_type' => 'wp_global_styles', + 'tax_input' => array( + 'wp_theme' => array( $args['stylesheet'] ), + ), + ); + + if ( ! empty( $args['post_name'] ) ) { + $post_args['post_name'] = $args['post_name']; + } + + $post_id = wp_insert_post( + $post_args, + true + ); + + if ( empty( $args['post_name'] ) ) { + $post_id = wp_update_post( + array( + 'ID' => $post_id, + 'post_name' => self::generate_user_style_variation_post_name( $post_id, $args['stylesheet'] ), + ) + ); + } + + return $post_id; + } + + /** + * Generate a post name for a variation with the given ID. + * + * @since 6.2 + * + * @param int|null $variation_id Variation ID. + * @param string $stylesheet Stylesheet. + * @return string Post name. + */ + public static function generate_user_style_variation_post_name( $variation_id, $stylesheet ) { + return "wp-global-styles-{$stylesheet}-variation-{$variation_id}"; + } + + /** + * Make an association between post $id and post containing current user + * global styles + * + * @since 6.2 + * + * @param int|null $id ID of the associated post. Null to delete the asociation. + * @return int|false Meta ID on success, false on failure. + */ + public static function associate_user_variation_with_global_styles_post( $id ) { + $current_gs_id = static::get_user_global_styles_post_id(); + + if ( ( (int) $id ) === $current_gs_id ) { + return false; + } + + $prev_id = get_post_meta( $current_gs_id, 'associated_user_variation', true ); + + if ( empty( $prev_id ) && ! empty( $id ) ) { + return add_post_meta( $current_gs_id, 'associated_user_variation', $id, true ); + } + + if ( empty( $id ) ) { + return delete_post_meta( $current_gs_id, 'associated_user_variation' ); + } + + return update_post_meta( $current_gs_id, 'associated_user_variation', $id ); + } + + /** + * Get associated variation ID. + * + * @since 6.2 + * + * @return int|null|false Meta ID or null on success, false on failure. + */ + public static function get_associated_user_variation_id() { + $current_gs_id = static::get_user_global_styles_post_id(); + + return get_post_meta( $current_gs_id, 'associated_user_variation', true ); + } + /** * Returns the user's origin config. * @@ -724,4 +836,47 @@ public static function get_style_variations() { } return $variations; } + /** + * Returns all style variations. + * + * @since 6.2.0 + * + * @return array + */ + public static function get_user_style_variations() { + $stylesheet = get_stylesheet(); + + $args = array( + 'posts_per_page' => -1, + 'orderby' => 'date', + 'order' => 'desc', + 'post_type' => 'wp_global_styles', + 'post_status' => 'publish', + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'tax_query' => array( + array( + 'taxonomy' => 'wp_theme', + 'field' => 'name', + 'terms' => $stylesheet, + ), + ), + ); + + $global_style_query = new WP_Query(); + $variation_posts = $global_style_query->query( $args ); + $variations = array(); + + foreach ( $variation_posts as $variation_post ) { + $decoded = json_decode( $variation_post->post_content, true ); + $variation = ( new WP_Theme_JSON_Gutenberg( $decoded ) )->get_raw_data(); + $variation['title'] = $variation_post->post_title; + $variation['id'] = $variation_post->ID; + $variations[] = $variation; + } + + return $variations; + } } diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php index 591c41128f839..f95e8ea588a78 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php @@ -16,7 +16,288 @@ class Gutenberg_REST_Global_Styles_Controller_6_2 extends WP_REST_Global_Styles_ * @return void */ public function register_routes() { - parent::register_routes(); + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'create_item' ), + 'permission_callback' => array( $this, 'create_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), + ), + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/themes/(?P[\/\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', 'gutenberg' ), + 'type' => 'string', + ), + ), + ), + ) + ); + + // List themes global styles. + register_rest_route( + $this->namespace, + // The route. + sprintf( + '/%s/themes/(?P%s)', + $this->rest_base, + // Matches theme's directory: `/themes///` or `/themes//`. + // Excludes invalid directory name characters: `/:<>*?"|`. + '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?' + ), + 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', 'gutenberg' ), + 'type' => 'string', + 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), + ), + ), + ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\/\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', 'gutenberg' ), + 'type' => 'string', + 'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ), + ), + ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), + 'permission_callback' => array( $this, 'delete_item_permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'The id of a template', 'gutenberg' ), + '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' ), + ) + ); + + } + + /** + * Checks if a given request has access to read all theme global styles configs. + * + * @since 6.0.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. + */ + public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + // Verify if the current user has edit_theme_options capability. + // This capability is required to edit/view/delete templates. + 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.', 'gutenberg' ), + array( + 'status' => rest_authorization_required_code(), + ) + ); + } + + return true; + } + + /** + * Gets all user global styles. + * + * @since 6.2 + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $variations = WP_Theme_JSON_Resolver_Gutenberg::get_user_style_variations(); + $response = rest_ensure_response( $variations ); + return $response; + } + + /** + * Checks if a given request has access to delete a single global style. + * + * @since 6.2 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + */ + public function delete_item_permissions_check( $request ) { + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } + + if ( $post && $this->check_delete_permission( $post ) ) { + return new WP_Error( + 'rest_forbidden_context', + __( 'Sorry, you are not allowed to delete this global style.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; + } + + /** + * Deletes the given global styles config. + * + * @since 6.2 + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function delete_item( $request ) { + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } + + $previous = $this->prepare_item_for_response( $post, $request ); + $result = wp_delete_post( $post->ID, true ); + $response = new WP_REST_Response(); + $response->set_data( + array( + 'deleted' => true, + 'previous' => $previous->get_data(), + ) + ); + + $association_result = true; + + // Delete association if we're deleting an associated style. + if ( WP_Theme_JSON_Resolver_Gutenberg::get_associated_user_variation_id() === $post->ID ) { + $association_result = WP_Theme_JSON_Resolver_Gutenberg::associate_user_variation_with_global_styles_post( null ); + } + + if ( ! $result || ! $association_result ) { + return new WP_Error( + 'rest_cannot_delete', + __( 'The global style cannot be deleted.', 'gutenberg' ), + array( 'status' => 500 ) + ); + } + + return $response; + } + + /** + * Checks if a given request has access to write a single global styles config. + * + * @since 6.2 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise. + */ + public function create_item_permissions_check( $request ) { + if ( ! empty( $request['id'] ) ) { + return new WP_Error( + 'rest_post_exists', + __( 'Cannot create existing style variation.', 'gutenberg' ), + array( 'status' => 400 ) + ); + } + + $post_type = get_post_type_object( $this->post_type ); + + if ( ! current_user_can( $post_type->cap->create_posts ) ) { + return new WP_Error( + 'rest_cannot_create', + __( 'Sorry, you are not allowed to create global style variations as this user.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; + } + + /** + * Adds a single global style config. + * + * @since 6.2 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function create_item( $request ) { + $changes = $this->prepare_item_for_database( $request ); + + if ( is_wp_error( $changes ) ) { + return $changes; + } + + $stylesheet = get_stylesheet(); + + $post_id = WP_Theme_JSON_Resolver_Gutenberg::add_user_global_styles_variation( + array( + 'title' => sanitize_text_field( $request['title'] ), + 'global_styles' => $changes->post_content, + 'stylesheet' => $stylesheet, + ) + ); + + if ( is_wp_error( $post_id ) ) { + + if ( 'db_insert_error' === $post_id->get_error_code() ) { + $post_id->add_data( array( 'status' => 500 ) ); + } else { + $post_id->add_data( array( 'status' => 400 ) ); + } + + return $post_id; + } + + $post = get_post( $post_id ); + + $response = $this->prepare_item_for_response( $post, $request ); + + return rest_ensure_response( $response ); } /** @@ -67,6 +348,16 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass(); } + if ( rest_is_field_included( 'associated_style_id', $fields ) ) { + $associated_style_id = get_post_meta( $post->ID, 'associated_user_variation', true ); + + if ( ! empty( $associated_style_id ) ) { + $data['associated_style_id'] = (int) $associated_style_id; + } else { + $data['associated_style_id'] = null; + } + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -132,6 +423,24 @@ public function update_item( $request ) { return $changes; } + if ( isset( $request['associated_style_id'] ) ) { + if ( ( (int) $request['id'] ) !== WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id() ) { + return new WP_Error( + 'rest_cannot_edit', + __( 'Sorry, you are not allowed to add an associated style id to this global style.', 'gutenberg' ), + array( 'status' => 400 ) + ); + } + + if ( is_numeric( $request['associated_style_id'] ) && $request['associated_style_id'] <= 0 ) { + $id = null; + } else { + $id = $request['associated_style_id']; + } + + WP_Theme_JSON_Resolver_Gutenberg::associate_user_variation_with_global_styles_post( $id ); + } + $result = wp_update_post( wp_slash( (array) $changes ), true, false ); if ( is_wp_error( $result ) ) { return $result; @@ -159,10 +468,16 @@ public function update_item( $request ) { * @return stdClass Changes to pass to wp_update_post. */ protected function prepare_item_for_database( $request ) { - $changes = new stdClass(); - $changes->ID = $request['id']; - $post = get_post( $request['id'] ); + $changes = new stdClass(); + if ( ! empty( $request['id'] ) ) { + $changes->ID = $request['id']; + $post = get_post( $request['id'] ); + } else { + $post = null; + } + $existing_config = array(); + if ( $post ) { $existing_config = json_decode( $post->post_content, true ); $json_decoding_error = json_last_error(); @@ -201,6 +516,7 @@ protected function prepare_item_for_database( $request ) { $changes->post_title = $request['title']['raw']; } } + return $changes; } @@ -276,6 +592,109 @@ public function get_theme_item( $request ) { return $response; } + /** + * Checks if a global style can be deleted. + * + * @since 6.2 + * + * @param WP_Post $post Post object. + * @return bool Whether the post can be deleted. + */ + protected function check_delete_permission( $post ) { + $post_type = get_post_type_object( $post->post_type ); + + if ( ! $this->check_is_post_type_allowed( $post_type ) ) { + return false; + } + + return current_user_can( 'delete_post', $post->ID ); + } + + /** + * Checks if a given post type can be viewed or managed. + * + * @since 6.2 + * + * @param WP_Post_Type|string $post_type Post type name or object. + * @return bool Whether the post type is allowed in REST. + */ + protected function check_is_post_type_allowed( $post_type ) { + if ( ! is_object( $post_type ) ) { + $post_type = get_post_type_object( $post_type ); + } + + if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) { + return true; + } + + return false; + } + + /** + * Retrieves the global styles type' schema, conforming to JSON Schema. + * + * @since 5.9.0 + * + * @return array Item schema data. + */ + 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.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'styles' => array( + 'description' => __( 'Global styles.', 'gutenberg' ), + 'type' => array( 'object' ), + 'context' => array( 'view', 'edit' ), + ), + 'settings' => array( + 'description' => __( 'Global settings.', 'gutenberg' ), + 'type' => array( 'object' ), + 'context' => array( 'view', 'edit' ), + ), + 'title' => array( + 'description' => __( 'Title of the global styles variation.', 'gutenberg' ), + '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.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'rendered' => array( + 'description' => __( 'HTML title for the post, transformed for display.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), + ), + ), + 'associated_style_id' => array( + 'description' => __( 'ID of the associated variation style.', 'gutenberg' ), + 'type' => array( 'null', 'integer' ), + 'context' => array( 'view', 'edit' ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } + /** * Returns the given theme global styles variations. * diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-themes-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-themes-controller-6-2.php new file mode 100644 index 0000000000000..0eb91ee88ff74 --- /dev/null +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-themes-controller-6-2.php @@ -0,0 +1,51 @@ + array( + 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_stylesheet() ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), + ), + ); + + if ( $this->is_same_theme( $theme, wp_get_theme() ) ) { + // This creates a record for the active theme if not existent. + $id = WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id(); + } else { + $user_cpt = WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_wp_global_styles( $theme ); + $id = isset( $user_cpt['ID'] ) ? $user_cpt['ID'] : null; + } + + if ( $id ) { + $links['https://api.w.org/user-global-styles'] = array( + 'href' => rest_url( 'wp/v2/global-styles/' . $id ), + ); + } + + return $links; + } +} diff --git a/lib/compat/wordpress-6.2/rest-api.php b/lib/compat/wordpress-6.2/rest-api.php index 6161885d020d7..9ddb84e196678 100644 --- a/lib/compat/wordpress-6.2/rest-api.php +++ b/lib/compat/wordpress-6.2/rest-api.php @@ -102,6 +102,15 @@ function gutenberg_register_global_styles_endpoints() { } add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); +/** + * Registers the themes REST API routes. + */ +function gutenberg_register_themes_endpoints() { + $controller = new Gutenberg_REST_Themes_Controller_6_2(); + $controller->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_themes_endpoints' ); + /** * Updates REST API response for the sidebars and marks them as 'inactive'. * diff --git a/lib/load.php b/lib/load.php index 71e48264a73f3..73d88454d37ef 100644 --- a/lib/load.php +++ b/lib/load.php @@ -44,6 +44,7 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php'; require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php'; require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php'; + require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-themes-controller-6-2.php'; require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php'; require_once __DIR__ . '/compat/wordpress-6.2/block-patterns.php'; require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php'; diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 73e2f6bb8e0e1..35d821654918e 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -336,6 +336,19 @@ function LinkControl( { { errorMessage } ) } +
+ + +
) } diff --git a/phpunit/class-gutenberg-rest-global-styles-controller-test.php b/phpunit/class-gutenberg-rest-global-styles-controller-test.php index 01063aa7e51bc..eba7910dad598 100644 --- a/phpunit/class-gutenberg-rest-global-styles-controller-test.php +++ b/phpunit/class-gutenberg-rest-global-styles-controller-test.php @@ -24,7 +24,6 @@ private function find_and_normalize_global_styles_by_id( $global_styles, $id ) { public function set_up() { parent::set_up(); - switch_theme( 'emptytheme' ); } /** @@ -38,20 +37,11 @@ public static function wpSetupBeforeClass( $factory ) { 'role' => 'administrator', ) ); + + switch_theme( 'emptytheme' ); + // This creates the global styles for the current theme. - self::$global_styles_id = wp_insert_post( - array( - 'post_content' => '{"version": ' . WP_Theme_JSON_Gutenberg::LATEST_SCHEMA . ', "isGlobalStylesUserThemeJSON": true }', - 'post_status' => 'publish', - 'post_title' => __( 'Custom Styles', 'default' ), - 'post_type' => 'wp_global_styles', - 'post_name' => 'wp-global-styles-emptytheme', - 'tax_input' => array( - 'wp_theme' => 'emptytheme', - ), - ), - true - ); + self::$global_styles_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_global_styles_post_id(); } @@ -129,20 +119,44 @@ public function test_get_item() { $this->assertEquals( array( - 'id' => self::$global_styles_id, - 'title' => array( + 'id' => self::$global_styles_id, + 'title' => array( 'raw' => 'Custom Styles', 'rendered' => 'Custom Styles', ), - 'settings' => new stdClass(), - 'styles' => new stdClass(), + 'settings' => new stdClass(), + 'styles' => new stdClass(), + 'associated_style_id' => null, ), $data ); } public function test_create_item() { - $this->markTestIncomplete(); + wp_set_current_user( self::$admin_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/global-styles' ); + $request->set_body_params( + array( + 'title' => 'Custom user variation', + 'settings' => new stdClass(), + 'styles' => new stdClass(), + ) + ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + unset( $data['_links'] ); + + $this->assertEquals( + array( + 'title' => array( + 'raw' => 'Custom user variation', + 'rendered' => 'Custom user variation', + ), + ), + array( + 'title' => $data['title'], + ) + ); } public function test_update_item() { @@ -159,7 +173,23 @@ public function test_update_item() { } public function test_delete_item() { - $this->markTestIncomplete(); + wp_set_current_user( self::$admin_id ); + $post_id = WP_Theme_JSON_Resolver_Gutenberg::add_user_global_styles_variation( + array( + 'title' => 'Title', + 'global_styles' => \wp_json_encode( + array( + 'settings' => new stdClass, + 'styles' => new stdClass, + ) + ), + 'stylesheet' => get_stylesheet(), + ) + ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/global-styles/' . $post_id ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertTrue( $data['deleted'] ); } public function test_prepare_item() {