From a9bc63f967551914ea9685cbfc28807265f06635 Mon Sep 17 00:00:00 2001 From: Copons Date: Tue, 12 Jan 2021 11:50:21 +0000 Subject: [PATCH 01/35] Add original_file_exists property to templates and parts --- lib/full-site-editing/block-templates.php | 46 ++++++++++--------- .../class-wp-block-template.php | 7 +++ .../class-wp-rest-templates-controller.php | 45 ++++++++++-------- 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index b1ee167d99961..805df226abc46 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -184,15 +184,16 @@ function _gutenberg_build_template_result_from_file( $template_file, $template_t $template_content = file_get_contents( $template_file['path'] ); $theme = wp_get_theme()->get_stylesheet(); - $template = new WP_Block_Template(); - $template->id = $theme . '//' . $template_file['slug']; - $template->theme = $theme; - $template->content = _inject_theme_attribute_in_content( $template_content ); - $template->slug = $template_file['slug']; - $template->is_custom = false; - $template->type = $template_type; - $template->title = $template_file['slug']; - $template->status = 'publish'; + $template = new WP_Block_Template(); + $template->id = $theme . '//' . $template_file['slug']; + $template->theme = $theme; + $template->content = _inject_theme_attribute_in_content( $template_content ); + $template->slug = $template_file['slug']; + $template->is_custom = false; + $template->type = $template_type; + $template->title = $template_file['slug']; + $template->status = 'publish'; + $template->original_file_exists = true; if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { $template->description = $default_template_types[ $template_file['slug'] ]['description']; @@ -225,18 +226,21 @@ function _gutenberg_build_template_result_from_post( $post ) { } $theme = $terms[0]->name; - - $template = new WP_Block_Template(); - $template->wp_id = $post->ID; - $template->id = $theme . '//' . $post->post_name; - $template->theme = $theme; - $template->content = $post->post_content; - $template->slug = $post->post_name; - $template->is_custom = true; - $template->type = $post->post_type; - $template->description = $post->post_excerpt; - $template->title = $post->post_title; - $template->status = $post->post_status; + $original_file_exists = wp_get_theme()->get_stylesheet() === $theme && + null !== _gutenberg_get_template_file( $post->post_type, $post->post_name ); + + $template = new WP_Block_Template(); + $template->wp_id = $post->ID; + $template->id = $theme . '//' . $post->post_name; + $template->theme = $theme; + $template->content = $post->post_content; + $template->slug = $post->post_name; + $template->is_custom = true; + $template->type = $post->post_type; + $template->description = $post->post_excerpt; + $template->title = $post->post_title; + $template->status = $post->post_status; + $template->original_file_exists = $original_file_exists; if ( 'wp_template_part' === $post->post_type ) { $type_terms = get_the_terms( $post, 'wp_template_part_area' ); diff --git a/lib/full-site-editing/class-wp-block-template.php b/lib/full-site-editing/class-wp-block-template.php index b5e051241bc39..4681aa95d427a 100644 --- a/lib/full-site-editing/class-wp-block-template.php +++ b/lib/full-site-editing/class-wp-block-template.php @@ -80,4 +80,11 @@ class WP_Block_Template { * @var string */ public $status; + + /** + * Whether a template is, or is based upon, an existing template file. + * + * @var boolean + */ + public $original_file_exists; } diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index a4d818fca256a..c561e4c17ce4c 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -384,19 +384,20 @@ protected function prepare_item_for_database( $request ) { */ public function prepare_item_for_response( $template, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $result = array( - 'id' => $template->id, - 'theme' => $template->theme, - 'content' => array( 'raw' => $template->content ), - 'slug' => $template->slug, - 'is_custom' => $template->is_custom, - 'type' => $template->type, - 'description' => $template->description, - 'title' => array( + 'id' => $template->id, + 'theme' => $template->theme, + 'content' => array( 'raw' => $template->content ), + 'slug' => $template->slug, + 'is_custom' => $template->is_custom, + 'type' => $template->type, + 'description' => $template->description, + 'title' => array( 'raw' => $template->title, 'rendered' => $template->title, ), - 'status' => $template->status, - 'wp_id' => $template->wp_id, + 'status' => $template->status, + 'wp_id' => $template->wp_id, + 'original_file_exists' => $template->original_file_exists ); if ( 'wp_template_part' === $template->type ) { @@ -495,13 +496,13 @@ public function get_item_schema() { 'title' => $this->post_type, 'type' => 'object', 'properties' => array( - 'id' => array( + 'id' => array( 'description' => __( 'ID of template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'slug' => array( + 'slug' => array( 'description' => __( 'Unique slug identifying the template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), @@ -509,47 +510,53 @@ public function get_item_schema() { 'minLength' => 1, 'pattern' => '[a-zA-Z_\-]+', ), - 'theme' => array( + 'theme' => array( 'description' => __( 'Theme identifier for the template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), ), - 'is_custom' => array( + 'is_custom' => array( 'description' => __( 'Whether the template is customized.', 'gutenberg' ), 'type' => 'bool', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'content' => array( + 'content' => array( 'description' => __( 'Content of template.', 'gutenberg' ), 'type' => array( 'object', 'string' ), 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'title' => array( + 'title' => array( 'description' => __( 'Title of template.', 'gutenberg' ), 'type' => array( 'object', 'string' ), 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'description' => array( + 'description' => array( 'description' => __( 'Description of template.', 'gutenberg' ), 'type' => 'string', 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'status' => array( + 'status' => array( 'description' => __( 'Status of template.', 'gutenberg' ), 'type' => 'string', 'default' => 'publish', 'context' => array( 'embed', 'view', 'edit' ), ), - 'wp_id' => array( + 'wp_id' => array( 'description' => __( 'Post ID.', 'gutenberg' ), 'type' => 'integer', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), + 'original_file_exists' => array( + 'description' => __( 'Original file exists.', 'gutenberg' ), + 'type' => 'bool', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), ), ); From 37ec486f295ab894ec968ad1d2bfb56bcb396e10 Mon Sep 17 00:00:00 2001 From: Copons Date: Tue, 12 Jan 2021 15:13:02 +0000 Subject: [PATCH 02/35] Add a revertTemplate action (not fully working) --- .../src/components/template-details/index.js | 23 ++++++- packages/edit-site/src/store/actions.js | 67 +++++++++++++++++++ .../src/utils/is-template-revertable.js | 14 ++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 packages/edit-site/src/utils/is-template-revertable.js diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js index a8896c24ace79..a0d148edde88f 100644 --- a/packages/edit-site/src/components/template-details/index.js +++ b/packages/edit-site/src/components/template-details/index.js @@ -8,6 +8,7 @@ import { useDispatch, useSelect } from '@wordpress/data'; /** * Internal dependencies */ +import isTemplateRevertable from '../../utils/is-template-revertable'; import { MENU_TEMPLATES } from '../navigation-sidebar/navigation-panel/constants'; import { store as editSiteStore } from '../../store'; @@ -17,7 +18,9 @@ export default function TemplateDetails( { template, onClose } ) { select( 'core/editor' ).__experimentalGetTemplateInfo( template ), [] ); - const { openNavigationPanelToMenu } = useDispatch( editSiteStore ); + const { openNavigationPanelToMenu, revertTemplate } = useDispatch( + editSiteStore + ); if ( ! template ) { return null; @@ -28,6 +31,11 @@ export default function TemplateDetails( { template, onClose } ) { openNavigationPanelToMenu( MENU_TEMPLATES ); }; + const revert = () => { + revertTemplate( template ); + onClose(); + }; + return ( <>
@@ -43,6 +51,19 @@ export default function TemplateDetails( { template, onClose } ) { ) }
+ { isTemplateRevertable( template ) && ( +
+ + + { __( + 'Reset this template to the theme supplied default' + ) } + +
+ ) } + - - { __( +
+ + onClick={ revert } + > + { __( 'Revert' ) } +
) } diff --git a/packages/edit-site/src/components/template-details/style.scss b/packages/edit-site/src/components/template-details/style.scss index 3835882531c14..d8e63b12865cc 100644 --- a/packages/edit-site/src/components/template-details/style.scss +++ b/packages/edit-site/src/components/template-details/style.scss @@ -1,10 +1,5 @@ .edit-site-template-details { - border-bottom: $border-width solid $gray-400; - padding: $grid-unit-10 $grid-unit-20; - - &:last-of-type { - border-bottom: none; - } + margin: $grid-unit-10 $grid-unit-20; p { padding: $grid-unit-10 0; @@ -15,6 +10,20 @@ color: $gray-700; } +.edit-site-template-details__revert { + border-top: $border-width solid $gray-400; + + .components-menu-item__button { + height: auto; + padding: $grid-unit-20; + text-align: left; + + &:focus:not(:disabled) { + box-shadow: inset 0 0 0 1.5px var(--wp-admin-theme-color), inset 0 0 0 3px #fff; + } + } +} + .edit-site-template-details__show-all-button.components-button { display: block; background: $gray-900; From 1cc29eb7e76e2f189956d6df559a5a016b0783cb Mon Sep 17 00:00:00 2001 From: James Koster Date: Wed, 13 Jan 2021 17:10:13 +0000 Subject: [PATCH 06/35] Adjust labels --- packages/edit-site/src/components/template-details/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js index b8f167076be17..93e30dc67f02a 100644 --- a/packages/edit-site/src/components/template-details/index.js +++ b/packages/edit-site/src/components/template-details/index.js @@ -59,11 +59,11 @@ export default function TemplateDetails( { template, onClose } ) {
- { __( 'Revert' ) } + { __( 'Clear customizations' ) }
) } From ff592f6607194be4706b2ab6fb9acca009469730 Mon Sep 17 00:00:00 2001 From: Copons Date: Fri, 15 Jan 2021 12:20:54 +0000 Subject: [PATCH 07/35] Handle revert on the endpoint --- .../class-wp-rest-templates-controller.php | 10 +++++++++ packages/edit-site/src/store/actions.js | 21 +++---------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index c561e4c17ce4c..d18af4c1ae92c 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -199,6 +199,16 @@ public function update_item( $request ) { return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.', 'gutenberg' ), array( 'status' => 404 ) ); } + // If the request explicitly sets `is_custom` to false, + // delete the template post, and return the template file. + if ( isset( $request['is_custom'] ) && false === $request['is_custom'] ) { + wp_delete_post( $template->wp_id, true ); + return $this->prepare_item_for_response( + gutenberg_get_block_template( $request['id'], $this->post_type ), + $request + ); + } + $changes = $this->prepare_item_for_database( $request ); if ( $template->is_custom ) { diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index a461e6655b5c2..f6bf3d562fb29 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -318,27 +318,12 @@ export function* revertTemplate( template ) { } try { - yield controls.dispatch( - 'core', - 'deleteEntityRecord', - 'postType', - 'wp_template', - template.id - ); - - yield controls.dispatch( - 'core', - 'invalidateResolution', - 'getEntityRecord', - [ 'postType', 'wp_template', template.id ] - ); - - const fileTemplate = yield controls.resolveSelect( + const fileTemplate = yield controls.dispatch( 'core', - 'getEntityRecord', + 'saveEntityRecord', 'postType', 'wp_template', - template.id + { id: template.id, is_custom: false } ); if ( ! fileTemplate ) { From abec6dde97e209bf52d62f522edc48cb71a9fe85 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Thu, 4 Mar 2021 15:18:05 +0100 Subject: [PATCH 08/35] Format --- lib/full-site-editing/block-templates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index 805df226abc46..b21f0331e4bcc 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -225,7 +225,7 @@ function _gutenberg_build_template_result_from_post( $post ) { return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'gutenberg' ) ); } - $theme = $terms[0]->name; + $theme = $terms[0]->name; $original_file_exists = wp_get_theme()->get_stylesheet() === $theme && null !== _gutenberg_get_template_file( $post->post_type, $post->post_name ); From 0a306ddf4bdca38bd258c8ad4ef0b23abd29be1c Mon Sep 17 00:00:00 2001 From: David Szabo Date: Thu, 4 Mar 2021 15:30:16 +0100 Subject: [PATCH 09/35] Format --- lib/full-site-editing/class-wp-rest-templates-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index d18af4c1ae92c..8a788422bf607 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -407,7 +407,7 @@ public function prepare_item_for_response( $template, $request ) { // phpcs:igno ), 'status' => $template->status, 'wp_id' => $template->wp_id, - 'original_file_exists' => $template->original_file_exists + 'original_file_exists' => $template->original_file_exists, ); if ( 'wp_template_part' === $template->type ) { From 1118c5e0e71cf51e4101a897f5af65d9a5d9349c Mon Sep 17 00:00:00 2001 From: David Szabo Date: Thu, 4 Mar 2021 15:30:28 +0100 Subject: [PATCH 10/35] Fix undo --- lib/full-site-editing/class-wp-rest-templates-controller.php | 3 ++- packages/edit-site/src/store/actions.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index 8a788422bf607..d0c6d6338b9ff 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -201,7 +201,8 @@ public function update_item( $request ) { // If the request explicitly sets `is_custom` to false, // delete the template post, and return the template file. - if ( isset( $request['is_custom'] ) && false === $request['is_custom'] ) { + $request_params_keys = array_keys( $request->get_json_params() ); + if ( count( $request_params_keys ) === 2 && isset( $request['is_custom'] ) && false === $request['is_custom'] ) { wp_delete_post( $template->wp_id, true ); return $this->prepare_item_for_response( gutenberg_get_block_template( $request['id'], $this->post_type ), diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index f6bf3d562fb29..61d9822076066 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -344,7 +344,7 @@ export function* revertTemplate( template ) { 'postType', 'wp_template', fileTemplate.id, - { blocks: parse( fileTemplate?.content?.raw ) } + { blocks: parse( fileTemplate?.content?.raw ), is_custom: false } ); yield controls.dispatch( From bd238250b6f8d6fe4bb76e939f66951f62926295 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Fri, 5 Mar 2021 20:51:49 +0100 Subject: [PATCH 11/35] Format --- packages/edit-site/src/components/template-details/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/template-details/index.js b/packages/edit-site/src/components/template-details/index.js index 93e30dc67f02a..103c4f0e4107b 100644 --- a/packages/edit-site/src/components/template-details/index.js +++ b/packages/edit-site/src/components/template-details/index.js @@ -58,9 +58,7 @@ export default function TemplateDetails( { template, onClose } ) { { isTemplateRevertable( template ) && (
{ __( 'Clear customizations' ) } From 108aff1c7dda20cfadf81f459cd9e6810f5c9bed Mon Sep 17 00:00:00 2001 From: David Szabo Date: Mon, 8 Mar 2021 17:32:26 +0100 Subject: [PATCH 12/35] Fix undo --- packages/edit-site/src/store/actions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 61d9822076066..fd1caa5888aaa 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -344,7 +344,11 @@ export function* revertTemplate( template ) { 'postType', 'wp_template', fileTemplate.id, - { blocks: parse( fileTemplate?.content?.raw ), is_custom: false } + { + blocks: parse( fileTemplate?.content?.raw ), + is_custom: false, + wp_id: null, + } ); yield controls.dispatch( From a8c1cf41483a4d616e796a568c751901cb50955e Mon Sep 17 00:00:00 2001 From: David Szabo Date: Mon, 8 Mar 2021 18:52:51 +0100 Subject: [PATCH 13/35] Fix PHP unit tests --- .../class-wp-rest-templates-controller.php | 17 ++-- ...class-wp-rest-template-controller-test.php | 80 ++++++++++--------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index d0c6d6338b9ff..75fd0ff573251 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -201,13 +201,16 @@ public function update_item( $request ) { // If the request explicitly sets `is_custom` to false, // delete the template post, and return the template file. - $request_params_keys = array_keys( $request->get_json_params() ); - if ( count( $request_params_keys ) === 2 && isset( $request['is_custom'] ) && false === $request['is_custom'] ) { - wp_delete_post( $template->wp_id, true ); - return $this->prepare_item_for_response( - gutenberg_get_block_template( $request['id'], $this->post_type ), - $request - ); + $request_params = $request->get_json_params(); + if ( is_array($request_params) ) { + $request_params_keys = array_keys( $request_params ); + if ( count( $request_params_keys ) === 2 && isset( $request['is_custom'] ) && false === $request['is_custom'] ) { + wp_delete_post( $template->wp_id, true ); + return $this->prepare_item_for_response( + gutenberg_get_block_template( $request['id'], $this->post_type ), + $request + ); + } } $changes = $this->prepare_item_for_database( $request ); diff --git a/phpunit/class-wp-rest-template-controller-test.php b/phpunit/class-wp-rest-template-controller-test.php index 659120d1ada86..8c0a6c7eb1685 100644 --- a/phpunit/class-wp-rest-template-controller-test.php +++ b/phpunit/class-wp-rest-template-controller-test.php @@ -62,18 +62,19 @@ function find_and_normalize_template_by_id( $templates, $id ) { $this->assertEquals( array( - 'id' => 'tt1-blocks//index', - 'theme' => 'tt1-blocks', - 'slug' => 'index', - 'title' => array( + 'id' => 'tt1-blocks//index', + 'theme' => 'tt1-blocks', + 'slug' => 'index', + 'title' => array( 'raw' => 'Index', 'rendered' => 'Index', ), - 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template', - 'wp_id' => null, + 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template', + 'wp_id' => null, + 'original_file_exists' => true, ), find_and_normalize_template_by_id( $data, 'tt1-blocks//index' ) ); @@ -85,20 +86,21 @@ function find_and_normalize_template_by_id( $templates, $id ) { $this->assertEquals( array( - 'id' => 'tt1-blocks//header', - 'theme' => 'tt1-blocks', - 'slug' => 'header', - 'title' => array( + 'id' => 'tt1-blocks//header', + 'theme' => 'tt1-blocks', + 'slug' => 'header', + 'title' => array( 'raw' => 'header', 'rendered' => 'header', ), - 'description' => '', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template_part', - 'wp_id' => null, + 'description' => '', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template_part', + 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. - 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'original_file_exists' => true, ), find_and_normalize_template_by_id( $data, 'tt1-blocks//header' ) ); @@ -114,18 +116,19 @@ public function test_get_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//index', - 'theme' => 'tt1-blocks', - 'slug' => 'index', - 'title' => array( + 'id' => 'tt1-blocks//index', + 'theme' => 'tt1-blocks', + 'slug' => 'index', + 'title' => array( 'raw' => 'Index', 'rendered' => 'Index', ), - 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template', - 'wp_id' => null, + 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template', + 'wp_id' => null, + 'original_file_exists' => true, ), $data ); @@ -152,6 +155,7 @@ public function test_get_item() { 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'original_file_exists' => true ), $data ); @@ -175,20 +179,21 @@ public function test_create_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//my_custom_template', - 'theme' => 'tt1-blocks', - 'slug' => 'my_custom_template', - 'title' => array( + 'id' => 'tt1-blocks//my_custom_template', + 'theme' => 'tt1-blocks', + 'slug' => 'my_custom_template', + 'title' => array( 'raw' => 'My Template', 'rendered' => 'My Template', ), - 'description' => 'Just a description', - 'status' => 'publish', - 'is_custom' => true, - 'type' => 'wp_template', - 'content' => array( + 'description' => 'Just a description', + 'status' => 'publish', + 'is_custom' => true, + 'type' => 'wp_template', + 'content' => array( 'raw' => 'Content', ), + 'original_file_exists' => false, ), $data ); @@ -226,6 +231,7 @@ public function test_create_item() { 'raw' => 'Content', ), 'area' => 'header', + 'original_file_exists' => false ), $data ); From b4b76b98366a0a41e773761d9ed17c71fcb9c0fd Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 12:11:32 +0100 Subject: [PATCH 14/35] Fix undo --- packages/edit-site/src/store/actions.js | 54 ++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index fd1caa5888aaa..f32c766505088 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { parse } from '@wordpress/blocks'; +import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; import { controls } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; import { getPathAndQueryString } from '@wordpress/url'; @@ -318,6 +318,34 @@ export function* revertTemplate( template ) { } try { + const serializeBlocks = ( { blocks: blocksForSerialization = [] } ) => + __unstableSerializeAndClean( blocksForSerialization ); + + const edited = yield controls.select( + 'core', + 'getEditedEntityRecord', + 'postType', + 'wp_template', + template.id + ); + + yield controls.dispatch( + 'core', + 'editEntityRecord', + 'postType', + 'wp_template', + template.id, + { + content: serializeBlocks, + blocks: edited.blocks, + wp_id: null, + is_custom: true, + }, + { + undoIgnore: true, + } + ); + const fileTemplate = yield controls.dispatch( 'core', 'saveEntityRecord', @@ -338,17 +366,31 @@ export function* revertTemplate( template ) { return; } + const blocks = parse( fileTemplate?.content?.raw ); + const edits = { + content: serializeBlocks, + blocks, + is_custom: false, + wp_id: null, + }; yield controls.dispatch( 'core', 'editEntityRecord', 'postType', 'wp_template', fileTemplate.id, - { - blocks: parse( fileTemplate?.content?.raw ), - is_custom: false, - wp_id: null, - } + edits + ); + + yield controls.dispatch( + 'core', + 'receiveEntityRecords', + 'postType', + 'wp_template', + fileTemplate, + undefined, + true, + edits ); yield controls.dispatch( From b434762ac29058b9652d670a782037c26e958f7a Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 12:49:32 +0100 Subject: [PATCH 15/35] Add undo to notice --- packages/edit-site/src/store/actions.js | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index f32c766505088..d304a06b7f016 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; -import { controls } from '@wordpress/data'; +import { controls, dispatch } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; import { getPathAndQueryString } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; @@ -328,7 +328,6 @@ export function* revertTemplate( template ) { 'wp_template', template.id ); - yield controls.dispatch( 'core', 'editEntityRecord', @@ -381,7 +380,6 @@ export function* revertTemplate( template ) { fileTemplate.id, edits ); - yield controls.dispatch( 'core', 'receiveEntityRecords', @@ -393,11 +391,38 @@ export function* revertTemplate( template ) { edits ); + const undoRevert = async () => { + await dispatch( 'core' ).editEntityRecord( + 'postType', + 'wp_template', + edited.id, + { + content: serializeBlocks, + blocks: edited.blocks, + wp_id: null, + is_custom: true, + } + ); + + await dispatch( 'core' ).saveEditedEntityRecord( + 'postType', + 'wp_template', + edited.id + ); + }; yield controls.dispatch( 'core/notices', 'createSuccessNotice', __( 'Template reverted.' ), - { type: 'snackbar' } + { + type: 'snackbar', + actions: [ + { + label: __( 'Undo' ), + onClick: undoRevert, + }, + ], + } ); } catch ( error ) { const errorMessage = From 0d331389d441850ab8d799b968e12b6fcaaf6970 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 14:36:01 +0100 Subject: [PATCH 16/35] Export wpDataSelect --- packages/e2e-test-utils/README.md | 14 ++++++++++++++ packages/e2e-test-utils/src/index.js | 1 + 2 files changed, 15 insertions(+) diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index 55e162ac6bec3..d7e3889ec9dd5 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -713,6 +713,20 @@ _Parameters_ - _width_ `number`: Width of the window. - _height_ `number`: Height of the window. +# **wpDataSelect** + +Queries the WordPress data module. + +_Parameters_ + +- _store_ `string`: Store to query e.g: core/editor, core/blocks... +- _selector_ `string`: Selector to exectute e.g: getBlocks. +- _parameters_ `...Object`: Parameters to pass to the selector. + +_Returns_ + +- `Promise`: Result of querying. + diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index 20fd36167a5b7..f71d2151ecd36 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -77,5 +77,6 @@ export { visitAdminPage } from './visit-admin-page'; export { waitForWindowDimensions } from './wait-for-window-dimensions'; export { showBlockToolbar } from './show-block-toolbar'; export { openPreviewPage } from './preview'; +export { wpDataSelect } from './wp-data-select'; export * from './mocks'; From 465331377ee301a5293e83c9eb3b7820498d8692 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 14:56:10 +0100 Subject: [PATCH 17/35] Add e2e tests --- packages/e2e-tests/experimental-features.js | 34 +++- .../specs/experiments/template-revert.test.js | 166 ++++++++++++++++++ 2 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 packages/e2e-tests/specs/experiments/template-revert.test.js diff --git a/packages/e2e-tests/experimental-features.js b/packages/e2e-tests/experimental-features.js index a8185b5f36a72..115b69d9e45e2 100644 --- a/packages/e2e-tests/experimental-features.js +++ b/packages/e2e-tests/experimental-features.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { addQueryArgs } from '@wordpress/url'; -import { visitAdminPage } from '@wordpress/e2e-test-utils'; +import { visitAdminPage, wpDataSelect } from '@wordpress/e2e-test-utils'; async function setExperimentalFeaturesState( features, enable ) { const query = addQueryArgs( '', { @@ -129,4 +129,36 @@ export const siteEditor = { await elementToClick.click(); }, + + async getEditedPostContent() { + const postId = await wpDataSelect( + 'core/edit-site', + 'getEditedPostId' + ); + const postType = await wpDataSelect( + 'core/edit-site', + 'getEditedPostType' + ); + const record = await wpDataSelect( + 'core', + 'getEditedEntityRecord', + 'postType', + postType, + postId + ); + if ( record ) { + if ( typeof record.content === 'function' ) { + return record.content( record ); + } else if ( record.blocks ) { + return await page.evaluate( + ( blocks ) => + window.wp.blocks.__unstableSerializeAndClean( blocks ), + record.blocks + ); + } else if ( record.content ) { + return record.content; + } + } + return ''; + }, }; diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js new file mode 100644 index 0000000000000..46e3cb8953117 --- /dev/null +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -0,0 +1,166 @@ +/** + * WordPress dependencies + */ +import { + insertBlock, + trashAllPosts, + activateTheme, +} from '@wordpress/e2e-test-utils'; + +/** + * Internal dependencies + */ +import { siteEditor } from '../../experimental-features'; + +const clickUndoInHeaderToolbar = () => + page.click( '.edit-site-header__toolbar button[aria-label="Undo"]' ); + +const clickRedoInHeaderToolbar = () => + page.click( '.edit-site-header__toolbar button[aria-label="Redo"]' ); + +const waitForNotice = () => + page.waitForSelector( '.components-snackbar', { visible: true } ); + +const clickButtonInNotice = async () => { + const selector = '.components-snackbar button'; + await page.waitForSelector( selector, { + visible: true, + } ); + await page.click( selector ); +}; + +const addDummyText = async () => { + await insertBlock( 'Paragraph' ); + await page.keyboard.type( 'Test' ); +}; + +const save = async () => { + await page.click( '.edit-site-save-button__button' ); + await page.click( '.editor-entities-saved-states__save-button' ); + await page.waitForSelector( + '.edit-site-save-button__button:not(.is-busy)' + ); +}; + +const revertTemplate = async () => { + await page.click( '.edit-site-document-actions__get-info' ); + await page.click( '.edit-site-template-details__revert button' ); + await waitForNotice(); +}; + +describe( 'Template Revert', () => { + beforeAll( async () => { + await activateTheme( 'tt1-blocks' ); + await trashAllPosts( 'wp_template' ); + await trashAllPosts( 'wp_template_part' ); + } ); + afterAll( async () => { + await trashAllPosts( 'wp_template' ); + await trashAllPosts( 'wp_template_part' ); + await activateTheme( 'twentytwentyone' ); + } ); + beforeEach( async () => { + await trashAllPosts( 'wp_template' ); + await siteEditor.visit(); + } ); + + it( 'should show the original content after revert', async () => { + const blocksBefore = await siteEditor.getEditedPostContent(); + + await addDummyText(); + await save(); + await revertTemplate(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the original content after revert and page reload', async () => { + const blocksBefore = await siteEditor.getEditedPostContent(); + + await addDummyText(); + await save(); + await revertTemplate(); + await siteEditor.visit(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the edited content after revert and clicking undo in the header toolbar', async () => { + await addDummyText(); + await save(); + const blocksBefore = await siteEditor.getEditedPostContent(); + + await revertTemplate(); + await clickUndoInHeaderToolbar(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the edited content after revert and clicking undo in the notice', async () => { + await addDummyText(); + await save(); + const blocksBefore = await siteEditor.getEditedPostContent(); + + await revertTemplate(); + await clickButtonInNotice(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the original content after revert, clicking undo then redo in the header toolbar', async () => { + const blocksBefore = await siteEditor.getEditedPostContent(); + + await addDummyText(); + await save(); + await revertTemplate(); + await clickUndoInHeaderToolbar(); + await clickRedoInHeaderToolbar(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the original content after revert, clicking undo in the notice then undo in the header toolbar', async () => { + const blocksBefore = await siteEditor.getEditedPostContent(); + + await addDummyText(); + await save(); + await revertTemplate(); + await clickButtonInNotice(); + await clickUndoInHeaderToolbar(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the edited content after revert, clicking undo in the header toolbar, save and reload', async () => { + await addDummyText(); + await save(); + const blocksBefore = await siteEditor.getEditedPostContent(); + + await revertTemplate(); + await clickUndoInHeaderToolbar(); + await save(); + await siteEditor.visit(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); + + it( 'should show the edited content after revert, clicking undo in the notice and reload', async () => { + await addDummyText(); + await save(); + const blocksBefore = await siteEditor.getEditedPostContent(); + + await revertTemplate(); + await clickButtonInNotice(); + await siteEditor.visit(); + + const blocksAfter = await siteEditor.getEditedPostContent(); + expect( blocksBefore ).toBe( blocksAfter ); + } ); +} ); From 36049c6daa03cf3ae1a3c2d0d590231ed7571fde Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 14:56:47 +0100 Subject: [PATCH 18/35] Format --- .../class-wp-rest-templates-controller.php | 2 +- ...class-wp-rest-template-controller-test.php | 44 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index 75fd0ff573251..89716be4b9fe5 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -202,7 +202,7 @@ public function update_item( $request ) { // If the request explicitly sets `is_custom` to false, // delete the template post, and return the template file. $request_params = $request->get_json_params(); - if ( is_array($request_params) ) { + if ( is_array( $request_params ) ) { $request_params_keys = array_keys( $request_params ); if ( count( $request_params_keys ) === 2 && isset( $request['is_custom'] ) && false === $request['is_custom'] ) { wp_delete_post( $template->wp_id, true ); diff --git a/phpunit/class-wp-rest-template-controller-test.php b/phpunit/class-wp-rest-template-controller-test.php index 8c0a6c7eb1685..1cb8c3f2e0a86 100644 --- a/phpunit/class-wp-rest-template-controller-test.php +++ b/phpunit/class-wp-rest-template-controller-test.php @@ -141,21 +141,21 @@ public function test_get_item() { unset( $data['_links'] ); $this->assertEquals( array( - 'id' => 'tt1-blocks//header', - 'theme' => 'tt1-blocks', - 'slug' => 'header', - 'title' => array( + 'id' => 'tt1-blocks//header', + 'theme' => 'tt1-blocks', + 'slug' => 'header', + 'title' => array( 'raw' => 'header', 'rendered' => 'header', ), - 'description' => '', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template_part', - 'wp_id' => null, + 'description' => '', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template_part', + 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. - 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, - 'original_file_exists' => true + 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'original_file_exists' => true, ), $data ); @@ -216,22 +216,22 @@ public function test_create_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//my_custom_template_part', - 'theme' => 'tt1-blocks', - 'slug' => 'my_custom_template_part', - 'title' => array( + 'id' => 'tt1-blocks//my_custom_template_part', + 'theme' => 'tt1-blocks', + 'slug' => 'my_custom_template_part', + 'title' => array( 'raw' => 'My Template Part', 'rendered' => 'My Template Part', ), - 'description' => 'Just a description of a template part', - 'status' => 'publish', - 'is_custom' => true, - 'type' => 'wp_template_part', - 'content' => array( + 'description' => 'Just a description of a template part', + 'status' => 'publish', + 'is_custom' => true, + 'type' => 'wp_template_part', + 'content' => array( 'raw' => 'Content', ), - 'area' => 'header', - 'original_file_exists' => false + 'area' => 'header', + 'original_file_exists' => false, ), $data ); From dbce5bac02014e7199b5ad93dd41f8499f1cb172 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 15:03:47 +0100 Subject: [PATCH 19/35] Rename variables --- .../specs/experiments/template-revert.test.js | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js index 46e3cb8953117..d6c4e47473db1 100644 --- a/packages/e2e-tests/specs/experiments/template-revert.test.js +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -65,54 +65,54 @@ describe( 'Template Revert', () => { } ); it( 'should show the original content after revert', async () => { - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await addDummyText(); await save(); await revertTemplate(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert and page reload', async () => { - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await addDummyText(); await save(); await revertTemplate(); await siteEditor.visit(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert and clicking undo in the header toolbar', async () => { await addDummyText(); await save(); - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await revertTemplate(); await clickUndoInHeaderToolbar(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert and clicking undo in the notice', async () => { await addDummyText(); await save(); - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await revertTemplate(); await clickButtonInNotice(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert, clicking undo then redo in the header toolbar', async () => { - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await addDummyText(); await save(); @@ -120,12 +120,12 @@ describe( 'Template Revert', () => { await clickUndoInHeaderToolbar(); await clickRedoInHeaderToolbar(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert, clicking undo in the notice then undo in the header toolbar', async () => { - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await addDummyText(); await save(); @@ -133,34 +133,34 @@ describe( 'Template Revert', () => { await clickButtonInNotice(); await clickUndoInHeaderToolbar(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert, clicking undo in the header toolbar, save and reload', async () => { await addDummyText(); await save(); - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await revertTemplate(); await clickUndoInHeaderToolbar(); await save(); await siteEditor.visit(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert, clicking undo in the notice and reload', async () => { await addDummyText(); await save(); - const blocksBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await siteEditor.getEditedPostContent(); await revertTemplate(); await clickButtonInNotice(); await siteEditor.visit(); - const blocksAfter = await siteEditor.getEditedPostContent(); - expect( blocksBefore ).toBe( blocksAfter ); + const contentAfter = await siteEditor.getEditedPostContent(); + expect( contentBefore ).toBe( contentAfter ); } ); } ); From 5bfd86beac46bc5c0b67f0efbc30ed9845bf87fe Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 15:05:58 +0100 Subject: [PATCH 20/35] Destructure siteEditor --- .../specs/experiments/template-revert.test.js | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js index d6c4e47473db1..586b5de469382 100644 --- a/packages/e2e-tests/specs/experiments/template-revert.test.js +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -12,6 +12,8 @@ import { */ import { siteEditor } from '../../experimental-features'; +const { visit: visitSiteEditor, getEditedPostContent } = siteEditor; + const clickUndoInHeaderToolbar = () => page.click( '.edit-site-header__toolbar button[aria-label="Undo"]' ); @@ -61,58 +63,58 @@ describe( 'Template Revert', () => { } ); beforeEach( async () => { await trashAllPosts( 'wp_template' ); - await siteEditor.visit(); + await visitSiteEditor(); } ); it( 'should show the original content after revert', async () => { - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await addDummyText(); await save(); await revertTemplate(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert and page reload', async () => { - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await addDummyText(); await save(); await revertTemplate(); - await siteEditor.visit(); + await visitSiteEditor(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert and clicking undo in the header toolbar', async () => { await addDummyText(); await save(); - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await revertTemplate(); await clickUndoInHeaderToolbar(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert and clicking undo in the notice', async () => { await addDummyText(); await save(); - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await revertTemplate(); await clickButtonInNotice(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert, clicking undo then redo in the header toolbar', async () => { - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await addDummyText(); await save(); @@ -120,12 +122,12 @@ describe( 'Template Revert', () => { await clickUndoInHeaderToolbar(); await clickRedoInHeaderToolbar(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the original content after revert, clicking undo in the notice then undo in the header toolbar', async () => { - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await addDummyText(); await save(); @@ -133,34 +135,34 @@ describe( 'Template Revert', () => { await clickButtonInNotice(); await clickUndoInHeaderToolbar(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert, clicking undo in the header toolbar, save and reload', async () => { await addDummyText(); await save(); - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await revertTemplate(); await clickUndoInHeaderToolbar(); await save(); - await siteEditor.visit(); + await visitSiteEditor(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); it( 'should show the edited content after revert, clicking undo in the notice and reload', async () => { await addDummyText(); await save(); - const contentBefore = await siteEditor.getEditedPostContent(); + const contentBefore = await getEditedPostContent(); await revertTemplate(); await clickButtonInNotice(); - await siteEditor.visit(); + await visitSiteEditor(); - const contentAfter = await siteEditor.getEditedPostContent(); + const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); } ); } ); From 32aa328014ead1e1ebffd76962761cbb799fd6b3 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 15:27:59 +0100 Subject: [PATCH 21/35] Replace store string literals --- packages/edit-site/src/store/actions.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index d304a06b7f016..6ca0e6427e746 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -6,6 +6,8 @@ import { controls, dispatch } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; import { getPathAndQueryString } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -309,7 +311,7 @@ export function setIsListViewOpened( isOpen ) { export function* revertTemplate( template ) { if ( ! isTemplateRevertable( template ) ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', __( 'This template is not revertable.' ), { type: 'snackbar' } @@ -322,14 +324,14 @@ export function* revertTemplate( template ) { __unstableSerializeAndClean( blocksForSerialization ); const edited = yield controls.select( - 'core', + coreStore, 'getEditedEntityRecord', 'postType', 'wp_template', template.id ); yield controls.dispatch( - 'core', + coreStore, 'editEntityRecord', 'postType', 'wp_template', @@ -346,7 +348,7 @@ export function* revertTemplate( template ) { ); const fileTemplate = yield controls.dispatch( - 'core', + coreStore, 'saveEntityRecord', 'postType', 'wp_template', @@ -355,7 +357,7 @@ export function* revertTemplate( template ) { if ( ! fileTemplate ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', __( 'The editor has encountered an unexpected error. Please reload.' @@ -373,7 +375,7 @@ export function* revertTemplate( template ) { wp_id: null, }; yield controls.dispatch( - 'core', + coreStore, 'editEntityRecord', 'postType', 'wp_template', @@ -381,7 +383,7 @@ export function* revertTemplate( template ) { edits ); yield controls.dispatch( - 'core', + coreStore, 'receiveEntityRecords', 'postType', 'wp_template', @@ -392,7 +394,7 @@ export function* revertTemplate( template ) { ); const undoRevert = async () => { - await dispatch( 'core' ).editEntityRecord( + await dispatch( coreStore ).editEntityRecord( 'postType', 'wp_template', edited.id, @@ -404,14 +406,14 @@ export function* revertTemplate( template ) { } ); - await dispatch( 'core' ).saveEditedEntityRecord( + await dispatch( coreStore ).saveEditedEntityRecord( 'postType', 'wp_template', edited.id ); }; yield controls.dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', __( 'Template reverted.' ), { @@ -430,7 +432,7 @@ export function* revertTemplate( template ) { ? error.message : __( 'Template revert failed. Please reload.' ); yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', errorMessage, { type: 'snackbar' } From a5b32c3c8cccd2c10bc47e4e7f8633d7e3baf749 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 15:45:26 +0100 Subject: [PATCH 22/35] Add save button checks --- .../specs/experiments/template-revert.test.js | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js index 586b5de469382..3637db544f85f 100644 --- a/packages/e2e-tests/specs/experiments/template-revert.test.js +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -14,11 +14,15 @@ import { siteEditor } from '../../experimental-features'; const { visit: visitSiteEditor, getEditedPostContent } = siteEditor; -const clickUndoInHeaderToolbar = () => - page.click( '.edit-site-header__toolbar button[aria-label="Undo"]' ); +const assertSaveButtonIsDisabled = () => + page.waitForSelector( + '.edit-site-save-button__button[aria-disabled="true"]' + ); -const clickRedoInHeaderToolbar = () => - page.click( '.edit-site-header__toolbar button[aria-label="Redo"]' ); +const assertSaveButtonIsEnabled = () => + page.waitForSelector( + '.edit-site-save-button__button[aria-disabled="false"]' + ); const waitForNotice = () => page.waitForSelector( '.components-snackbar', { visible: true } ); @@ -31,6 +35,22 @@ const clickButtonInNotice = async () => { await page.click( selector ); }; +const clickUndoInHeaderToolbar = () => + page.click( '.edit-site-header__toolbar button[aria-label="Undo"]' ); + +const clickRedoInHeaderToolbar = () => + page.click( '.edit-site-header__toolbar button[aria-label="Redo"]' ); + +const undoRevertInHeaderToolbar = async () => { + await clickUndoInHeaderToolbar(); + await assertSaveButtonIsEnabled(); +}; + +const undoRevertInNotice = async () => { + await clickButtonInNotice(); + await assertSaveButtonIsDisabled(); +}; + const addDummyText = async () => { await insertBlock( 'Paragraph' ); await page.keyboard.type( 'Test' ); @@ -48,6 +68,7 @@ const revertTemplate = async () => { await page.click( '.edit-site-document-actions__get-info' ); await page.click( '.edit-site-template-details__revert button' ); await waitForNotice(); + await assertSaveButtonIsDisabled(); }; describe( 'Template Revert', () => { @@ -95,7 +116,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); - await clickUndoInHeaderToolbar(); + await undoRevertInHeaderToolbar(); const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); @@ -107,7 +128,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); - await clickButtonInNotice(); + await undoRevertInNotice(); const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); @@ -119,7 +140,7 @@ describe( 'Template Revert', () => { await addDummyText(); await save(); await revertTemplate(); - await clickUndoInHeaderToolbar(); + await undoRevertInHeaderToolbar(); await clickRedoInHeaderToolbar(); const contentAfter = await getEditedPostContent(); @@ -132,8 +153,8 @@ describe( 'Template Revert', () => { await addDummyText(); await save(); await revertTemplate(); - await clickButtonInNotice(); - await clickUndoInHeaderToolbar(); + await undoRevertInNotice(); + await undoRevertInHeaderToolbar(); const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); @@ -147,6 +168,7 @@ describe( 'Template Revert', () => { await revertTemplate(); await clickUndoInHeaderToolbar(); await save(); + await assertSaveButtonIsDisabled(); await visitSiteEditor(); const contentAfter = await getEditedPostContent(); @@ -159,7 +181,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); - await clickButtonInNotice(); + await undoRevertInNotice(); await visitSiteEditor(); const contentAfter = await getEditedPostContent(); From d581dbc3ed0f4a36e15ebc61da1c8638a4cdafe5 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 16:14:49 +0100 Subject: [PATCH 23/35] Remove visible check --- .../e2e-tests/specs/experiments/template-revert.test.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js index 3637db544f85f..34097705f429b 100644 --- a/packages/e2e-tests/specs/experiments/template-revert.test.js +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -24,14 +24,11 @@ const assertSaveButtonIsEnabled = () => '.edit-site-save-button__button[aria-disabled="false"]' ); -const waitForNotice = () => - page.waitForSelector( '.components-snackbar', { visible: true } ); +const waitForNotice = () => page.waitForSelector( '.components-snackbar' ); const clickButtonInNotice = async () => { const selector = '.components-snackbar button'; - await page.waitForSelector( selector, { - visible: true, - } ); + await page.waitForSelector( selector ); await page.click( selector ); }; From 8cddbc99e286fc99e55614d2b9c964d28bf68692 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Tue, 9 Mar 2021 16:15:11 +0100 Subject: [PATCH 24/35] Add comments and remove unncessary properties --- packages/edit-site/src/store/actions.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 6ca0e6427e746..0aaf698d96a3f 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -330,6 +330,8 @@ export function* revertTemplate( template ) { 'wp_template', template.id ); + // We are fixing up the undo level here to make sure we can undo + // the revert in the header toolbar correctly. yield controls.dispatch( coreStore, 'editEntityRecord', @@ -337,13 +339,12 @@ export function* revertTemplate( template ) { 'wp_template', template.id, { - content: serializeBlocks, - blocks: edited.blocks, - wp_id: null, - is_custom: true, + content: serializeBlocks, // required to make the `undo` behave correctly + blocks: edited.blocks, // required to revert the blocks in the editor + is_custom: true, // required to avoid turning the editor into a dirty state }, { - undoIgnore: true, + undoIgnore: true, // required to merge this edit with the last undo level } ); @@ -354,7 +355,6 @@ export function* revertTemplate( template ) { 'wp_template', { id: template.id, is_custom: false } ); - if ( ! fileTemplate ) { yield controls.dispatch( noticesStore, @@ -368,11 +368,13 @@ export function* revertTemplate( template ) { } const blocks = parse( fileTemplate?.content?.raw ); + // We use the same object for `editEntityRecord` and `receiveEntityRecords` + // to avoid turning the editor into a dirty state. + // `content` property is required to make the undo-redo work. Without `content` + // it wouldn't save the actual blocks to the server. const edits = { content: serializeBlocks, blocks, - is_custom: false, - wp_id: null, }; yield controls.dispatch( coreStore, @@ -382,6 +384,7 @@ export function* revertTemplate( template ) { fileTemplate.id, edits ); + // We override the `edits` to make sure we can undo-redo correctly. yield controls.dispatch( coreStore, 'receiveEntityRecords', @@ -401,8 +404,6 @@ export function* revertTemplate( template ) { { content: serializeBlocks, blocks: edited.blocks, - wp_id: null, - is_custom: true, } ); From feadc711f598120224083ea7433eabfea94bfa6d Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 10 Mar 2021 13:46:24 +0100 Subject: [PATCH 25/35] Revert without autosaving --- lib/full-site-editing/block-templates.php | 19 +++++ .../class-wp-rest-templates-controller.php | 21 ++---- .../specs/experiments/template-revert.test.js | 37 ++++++++- packages/edit-site/src/store/actions.js | 75 +++++++------------ 4 files changed, 90 insertions(+), 62 deletions(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index b21f0331e4bcc..9b2ac18390384 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -371,6 +371,25 @@ function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { } } + return gutenberg_get_block_file_template( $id, $template_type ); +} + +/** + * Retrieves a single unified template object using its id. + * Retrieves the file template. + * + * @param string $id Template unique identifier (example: theme|slug). + * @param array $template_type wp_template or wp_template_part. + * + * @return WP_Block_Template|null File template. + */ +function gutenberg_get_block_file_template( $id, $template_type = 'wp_template' ) { + $parts = explode( '//', $id, 2 ); + if ( count( $parts ) < 2 ) { + return null; + } + list( $theme, $slug ) = $parts; + if ( wp_get_theme()->get_stylesheet() === $theme ) { $template_file = _gutenberg_get_template_file( $template_type, $slug ); if ( null !== $template_file ) { diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index 89716be4b9fe5..a2bf077457376 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -168,7 +168,11 @@ public function get_item_permissions_check( $request ) { * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { - $template = gutenberg_get_block_template( $request['id'], $this->post_type ); + if ( isset( $request['is_custom'] ) && 'false' === $request['is_custom'] ) { + $template = gutenberg_get_block_file_template( $request['id'], $this->post_type ); + } else { + $template = gutenberg_get_block_template( $request['id'], $this->post_type ); + } if ( ! $template ) { return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.', 'gutenberg' ), array( 'status' => 404 ) ); @@ -199,18 +203,9 @@ public function update_item( $request ) { return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.', 'gutenberg' ), array( 'status' => 404 ) ); } - // If the request explicitly sets `is_custom` to false, - // delete the template post, and return the template file. - $request_params = $request->get_json_params(); - if ( is_array( $request_params ) ) { - $request_params_keys = array_keys( $request_params ); - if ( count( $request_params_keys ) === 2 && isset( $request['is_custom'] ) && false === $request['is_custom'] ) { - wp_delete_post( $template->wp_id, true ); - return $this->prepare_item_for_response( - gutenberg_get_block_template( $request['id'], $this->post_type ), - $request - ); - } + if ( isset( $request['reverted_file_content'] ) && isset( $request['content'] ) && $request['reverted_file_content'] === $request['content'] ) { + wp_delete_post( $template->wp_id, true ); + return $this->prepare_item_for_response( gutenberg_get_block_file_template( $request['id'], $this->post_type ), $request ); } $changes = $this->prepare_item_for_database( $request ); diff --git a/packages/e2e-tests/specs/experiments/template-revert.test.js b/packages/e2e-tests/specs/experiments/template-revert.test.js index 34097705f429b..cd0dcaa7ab3d5 100644 --- a/packages/e2e-tests/specs/experiments/template-revert.test.js +++ b/packages/e2e-tests/specs/experiments/template-revert.test.js @@ -5,7 +5,11 @@ import { insertBlock, trashAllPosts, activateTheme, + switchUserToAdmin, + switchUserToTest, + visitAdminPage, } from '@wordpress/e2e-test-utils'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -45,7 +49,7 @@ const undoRevertInHeaderToolbar = async () => { const undoRevertInNotice = async () => { await clickButtonInNotice(); - await assertSaveButtonIsDisabled(); + await assertSaveButtonIsEnabled(); }; const addDummyText = async () => { @@ -65,7 +69,18 @@ const revertTemplate = async () => { await page.click( '.edit-site-document-actions__get-info' ); await page.click( '.edit-site-template-details__revert button' ); await waitForNotice(); - await assertSaveButtonIsDisabled(); + await assertSaveButtonIsEnabled(); +}; + +const assertTemplatesAreDeleted = async () => { + await switchUserToAdmin(); + const query = addQueryArgs( '', { + post_type: 'wp_template', + } ).slice( 1 ); + await visitAdminPage( 'edit.php', query ); + const element = await page.waitForSelector( '#the-list .no-items' ); + expect( element ).toBeTruthy(); + await switchUserToTest(); }; describe( 'Template Revert', () => { @@ -84,12 +99,22 @@ describe( 'Template Revert', () => { await visitSiteEditor(); } ); + it( 'should delete the template after saving the reverted template', async () => { + await addDummyText(); + await save(); + await revertTemplate(); + await save(); + + await assertTemplatesAreDeleted(); + } ); + it( 'should show the original content after revert', async () => { const contentBefore = await getEditedPostContent(); await addDummyText(); await save(); await revertTemplate(); + await save(); const contentAfter = await getEditedPostContent(); expect( contentBefore ).toBe( contentAfter ); @@ -101,6 +126,7 @@ describe( 'Template Revert', () => { await addDummyText(); await save(); await revertTemplate(); + await save(); await visitSiteEditor(); const contentAfter = await getEditedPostContent(); @@ -113,6 +139,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); + await save(); await undoRevertInHeaderToolbar(); const contentAfter = await getEditedPostContent(); @@ -125,6 +152,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); + await save(); await undoRevertInNotice(); const contentAfter = await getEditedPostContent(); @@ -137,6 +165,7 @@ describe( 'Template Revert', () => { await addDummyText(); await save(); await revertTemplate(); + await save(); await undoRevertInHeaderToolbar(); await clickRedoInHeaderToolbar(); @@ -150,6 +179,7 @@ describe( 'Template Revert', () => { await addDummyText(); await save(); await revertTemplate(); + await save(); await undoRevertInNotice(); await undoRevertInHeaderToolbar(); @@ -163,6 +193,7 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); + await save(); await clickUndoInHeaderToolbar(); await save(); await assertSaveButtonIsDisabled(); @@ -178,7 +209,9 @@ describe( 'Template Revert', () => { const contentBefore = await getEditedPostContent(); await revertTemplate(); + await save(); await undoRevertInNotice(); + await save(); await visitSiteEditor(); const contentAfter = await getEditedPostContent(); diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 0aaf698d96a3f..0b3787743111f 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -4,7 +4,7 @@ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; import { controls, dispatch } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; -import { getPathAndQueryString } from '@wordpress/url'; +import { addQueryArgs, getPathAndQueryString } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; @@ -320,9 +320,25 @@ export function* revertTemplate( template ) { } try { + const fileTemplate = yield apiFetch( { + path: addQueryArgs( template._links.self[ 0 ].href, { + is_custom: false, + } ), + } ); + if ( ! fileTemplate ) { + yield controls.dispatch( + noticesStore, + 'createErrorNotice', + __( + 'The editor has encountered an unexpected error. Please reload.' + ), + { type: 'snackbar' } + ); + return; + } + const serializeBlocks = ( { blocks: blocksForSerialization = [] } ) => __unstableSerializeAndClean( blocksForSerialization ); - const edited = yield controls.select( coreStore, 'getEditedEntityRecord', @@ -348,52 +364,22 @@ export function* revertTemplate( template ) { } ); - const fileTemplate = yield controls.dispatch( - coreStore, - 'saveEntityRecord', - 'postType', - 'wp_template', - { id: template.id, is_custom: false } - ); - if ( ! fileTemplate ) { - yield controls.dispatch( - noticesStore, - 'createErrorNotice', - __( - 'The editor has encountered an unexpected error. Please reload.' - ), - { type: 'snackbar' } - ); - return; - } - const blocks = parse( fileTemplate?.content?.raw ); - // We use the same object for `editEntityRecord` and `receiveEntityRecords` - // to avoid turning the editor into a dirty state. - // `content` property is required to make the undo-redo work. Without `content` - // it wouldn't save the actual blocks to the server. - const edits = { - content: serializeBlocks, - blocks, - }; + // `reverted_file_content` is used on the server-side + // it is compared with the template's content + // if they match then the template is deleted (reverted to file) yield controls.dispatch( coreStore, 'editEntityRecord', 'postType', 'wp_template', fileTemplate.id, - edits - ); - // We override the `edits` to make sure we can undo-redo correctly. - yield controls.dispatch( - coreStore, - 'receiveEntityRecords', - 'postType', - 'wp_template', - fileTemplate, - undefined, - true, - edits + { + content: serializeBlocks, + blocks, + is_custom: false, + reverted_file_content: __unstableSerializeAndClean( blocks ), + } ); const undoRevert = async () => { @@ -404,14 +390,9 @@ export function* revertTemplate( template ) { { content: serializeBlocks, blocks: edited.blocks, + is_custom: true, } ); - - await dispatch( coreStore ).saveEditedEntityRecord( - 'postType', - 'wp_template', - edited.id - ); }; yield controls.dispatch( noticesStore, From 743b71d7c7266bea2c471ce4c8d04f0c0d59c7ca Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 10 Mar 2021 14:11:10 +0100 Subject: [PATCH 26/35] Use getEntityRecord instead of apiFetch --- packages/edit-site/src/store/actions.js | 26 +++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 0b3787743111f..3e885e3d18b8c 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -4,7 +4,7 @@ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; import { controls, dispatch } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; -import { addQueryArgs, getPathAndQueryString } from '@wordpress/url'; +import { getPathAndQueryString } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; @@ -320,11 +320,16 @@ export function* revertTemplate( template ) { } try { - const fileTemplate = yield apiFetch( { - path: addQueryArgs( template._links.self[ 0 ].href, { - is_custom: false, - } ), - } ); + // This will override the current template entity + // so we need to refetch after this call. + const fileTemplate = yield controls.resolveSelect( + coreStore, + 'getEntityRecord', + 'postType', + 'wp_template', + template.id, + { is_custom: false } + ); if ( ! fileTemplate ) { yield controls.dispatch( noticesStore, @@ -336,6 +341,15 @@ export function* revertTemplate( template ) { ); return; } + // Refetch the template entity + yield controls.resolveSelect( + coreStore, + 'getEntityRecord', + 'postType', + 'wp_template', + template.id, + { is_custom: true } + ); const serializeBlocks = ( { blocks: blocksForSerialization = [] } ) => __unstableSerializeAndClean( blocksForSerialization ); From e66c425ecbbc64e1eac71198f69d674f2b3796fd Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 24 Mar 2021 12:19:13 +0100 Subject: [PATCH 27/35] Rename original_file_exists to has_theme_file --- lib/full-site-editing/block-templates.php | 48 +++---- .../class-wp-block-template.php | 2 +- .../class-wp-rest-templates-controller.php | 44 +++--- .../src/utils/is-template-revertable.js | 2 +- ...class-wp-rest-template-controller-test.php | 126 +++++++++--------- 5 files changed, 111 insertions(+), 111 deletions(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index 9b2ac18390384..4b7139da7178f 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -184,16 +184,16 @@ function _gutenberg_build_template_result_from_file( $template_file, $template_t $template_content = file_get_contents( $template_file['path'] ); $theme = wp_get_theme()->get_stylesheet(); - $template = new WP_Block_Template(); - $template->id = $theme . '//' . $template_file['slug']; - $template->theme = $theme; - $template->content = _inject_theme_attribute_in_content( $template_content ); - $template->slug = $template_file['slug']; - $template->is_custom = false; - $template->type = $template_type; - $template->title = $template_file['slug']; - $template->status = 'publish'; - $template->original_file_exists = true; + $template = new WP_Block_Template(); + $template->id = $theme . '//' . $template_file['slug']; + $template->theme = $theme; + $template->content = _inject_theme_attribute_in_content( $template_content ); + $template->slug = $template_file['slug']; + $template->is_custom = false; + $template->type = $template_type; + $template->title = $template_file['slug']; + $template->status = 'publish'; + $template->has_theme_file = true; if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { $template->description = $default_template_types[ $template_file['slug'] ]['description']; @@ -225,22 +225,22 @@ function _gutenberg_build_template_result_from_post( $post ) { return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'gutenberg' ) ); } - $theme = $terms[0]->name; - $original_file_exists = wp_get_theme()->get_stylesheet() === $theme && + $theme = $terms[0]->name; + $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && null !== _gutenberg_get_template_file( $post->post_type, $post->post_name ); - $template = new WP_Block_Template(); - $template->wp_id = $post->ID; - $template->id = $theme . '//' . $post->post_name; - $template->theme = $theme; - $template->content = $post->post_content; - $template->slug = $post->post_name; - $template->is_custom = true; - $template->type = $post->post_type; - $template->description = $post->post_excerpt; - $template->title = $post->post_title; - $template->status = $post->post_status; - $template->original_file_exists = $original_file_exists; + $template = new WP_Block_Template(); + $template->wp_id = $post->ID; + $template->id = $theme . '//' . $post->post_name; + $template->theme = $theme; + $template->content = $post->post_content; + $template->slug = $post->post_name; + $template->is_custom = true; + $template->type = $post->post_type; + $template->description = $post->post_excerpt; + $template->title = $post->post_title; + $template->status = $post->post_status; + $template->has_theme_file = $has_theme_file; if ( 'wp_template_part' === $post->post_type ) { $type_terms = get_the_terms( $post, 'wp_template_part_area' ); diff --git a/lib/full-site-editing/class-wp-block-template.php b/lib/full-site-editing/class-wp-block-template.php index 4681aa95d427a..5a5872e05e06f 100644 --- a/lib/full-site-editing/class-wp-block-template.php +++ b/lib/full-site-editing/class-wp-block-template.php @@ -86,5 +86,5 @@ class WP_Block_Template { * * @var boolean */ - public $original_file_exists; + public $has_theme_file; } diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index a2bf077457376..fad2baa1ee3c2 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -393,20 +393,20 @@ protected function prepare_item_for_database( $request ) { */ public function prepare_item_for_response( $template, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $result = array( - 'id' => $template->id, - 'theme' => $template->theme, - 'content' => array( 'raw' => $template->content ), - 'slug' => $template->slug, - 'is_custom' => $template->is_custom, - 'type' => $template->type, - 'description' => $template->description, - 'title' => array( + 'id' => $template->id, + 'theme' => $template->theme, + 'content' => array( 'raw' => $template->content ), + 'slug' => $template->slug, + 'is_custom' => $template->is_custom, + 'type' => $template->type, + 'description' => $template->description, + 'title' => array( 'raw' => $template->title, 'rendered' => $template->title, ), - 'status' => $template->status, - 'wp_id' => $template->wp_id, - 'original_file_exists' => $template->original_file_exists, + 'status' => $template->status, + 'wp_id' => $template->wp_id, + 'has_theme_file' => $template->has_theme_file, ); if ( 'wp_template_part' === $template->type ) { @@ -505,13 +505,13 @@ public function get_item_schema() { 'title' => $this->post_type, 'type' => 'object', 'properties' => array( - 'id' => array( + 'id' => array( 'description' => __( 'ID of template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'slug' => array( + 'slug' => array( 'description' => __( 'Unique slug identifying the template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), @@ -519,49 +519,49 @@ public function get_item_schema() { 'minLength' => 1, 'pattern' => '[a-zA-Z_\-]+', ), - 'theme' => array( + 'theme' => array( 'description' => __( 'Theme identifier for the template.', 'gutenberg' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), ), - 'is_custom' => array( + 'is_custom' => array( 'description' => __( 'Whether the template is customized.', 'gutenberg' ), 'type' => 'bool', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'content' => array( + 'content' => array( 'description' => __( 'Content of template.', 'gutenberg' ), 'type' => array( 'object', 'string' ), 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'title' => array( + 'title' => array( 'description' => __( 'Title of template.', 'gutenberg' ), 'type' => array( 'object', 'string' ), 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'description' => array( + 'description' => array( 'description' => __( 'Description of template.', 'gutenberg' ), 'type' => 'string', 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), ), - 'status' => array( + 'status' => array( 'description' => __( 'Status of template.', 'gutenberg' ), 'type' => 'string', 'default' => 'publish', 'context' => array( 'embed', 'view', 'edit' ), ), - 'wp_id' => array( + 'wp_id' => array( 'description' => __( 'Post ID.', 'gutenberg' ), 'type' => 'integer', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), - 'original_file_exists' => array( - 'description' => __( 'Original file exists.', 'gutenberg' ), + 'has_theme_file' => array( + 'description' => __( 'Theme file exists.', 'gutenberg' ), 'type' => 'bool', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, diff --git a/packages/edit-site/src/utils/is-template-revertable.js b/packages/edit-site/src/utils/is-template-revertable.js index 9fd147d170f4d..cd99d67c37e1b 100644 --- a/packages/edit-site/src/utils/is-template-revertable.js +++ b/packages/edit-site/src/utils/is-template-revertable.js @@ -9,6 +9,6 @@ export default function isTemplateRevertable( template ) { return false; } /* eslint-disable camelcase */ - return template?.is_custom && template?.original_file_exists; + return template?.is_custom && template?.has_theme_file; /* eslint-enable camelcase */ } diff --git a/phpunit/class-wp-rest-template-controller-test.php b/phpunit/class-wp-rest-template-controller-test.php index 1cb8c3f2e0a86..6a5080a2e6246 100644 --- a/phpunit/class-wp-rest-template-controller-test.php +++ b/phpunit/class-wp-rest-template-controller-test.php @@ -62,19 +62,19 @@ function find_and_normalize_template_by_id( $templates, $id ) { $this->assertEquals( array( - 'id' => 'tt1-blocks//index', - 'theme' => 'tt1-blocks', - 'slug' => 'index', - 'title' => array( + 'id' => 'tt1-blocks//index', + 'theme' => 'tt1-blocks', + 'slug' => 'index', + 'title' => array( 'raw' => 'Index', 'rendered' => 'Index', ), - 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template', - 'wp_id' => null, - 'original_file_exists' => true, + 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template', + 'wp_id' => null, + 'has_theme_file' => true, ), find_and_normalize_template_by_id( $data, 'tt1-blocks//index' ) ); @@ -86,21 +86,21 @@ function find_and_normalize_template_by_id( $templates, $id ) { $this->assertEquals( array( - 'id' => 'tt1-blocks//header', - 'theme' => 'tt1-blocks', - 'slug' => 'header', - 'title' => array( + 'id' => 'tt1-blocks//header', + 'theme' => 'tt1-blocks', + 'slug' => 'header', + 'title' => array( 'raw' => 'header', 'rendered' => 'header', ), - 'description' => '', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template_part', - 'wp_id' => null, + 'description' => '', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template_part', + 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. - 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, - 'original_file_exists' => true, + 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'has_theme_file' => true, ), find_and_normalize_template_by_id( $data, 'tt1-blocks//header' ) ); @@ -116,19 +116,19 @@ public function test_get_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//index', - 'theme' => 'tt1-blocks', - 'slug' => 'index', - 'title' => array( + 'id' => 'tt1-blocks//index', + 'theme' => 'tt1-blocks', + 'slug' => 'index', + 'title' => array( 'raw' => 'Index', 'rendered' => 'Index', ), - 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template', - 'wp_id' => null, - 'original_file_exists' => true, + 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template', + 'wp_id' => null, + 'has_theme_file' => true, ), $data ); @@ -141,21 +141,21 @@ public function test_get_item() { unset( $data['_links'] ); $this->assertEquals( array( - 'id' => 'tt1-blocks//header', - 'theme' => 'tt1-blocks', - 'slug' => 'header', - 'title' => array( + 'id' => 'tt1-blocks//header', + 'theme' => 'tt1-blocks', + 'slug' => 'header', + 'title' => array( 'raw' => 'header', 'rendered' => 'header', ), - 'description' => '', - 'status' => 'publish', - 'is_custom' => false, - 'type' => 'wp_template_part', - 'wp_id' => null, + 'description' => '', + 'status' => 'publish', + 'is_custom' => false, + 'type' => 'wp_template_part', + 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. - 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, - 'original_file_exists' => true, + 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, + 'has_theme_file' => true, ), $data ); @@ -179,21 +179,21 @@ public function test_create_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//my_custom_template', - 'theme' => 'tt1-blocks', - 'slug' => 'my_custom_template', - 'title' => array( + 'id' => 'tt1-blocks//my_custom_template', + 'theme' => 'tt1-blocks', + 'slug' => 'my_custom_template', + 'title' => array( 'raw' => 'My Template', 'rendered' => 'My Template', ), - 'description' => 'Just a description', - 'status' => 'publish', - 'is_custom' => true, - 'type' => 'wp_template', - 'content' => array( + 'description' => 'Just a description', + 'status' => 'publish', + 'is_custom' => true, + 'type' => 'wp_template', + 'content' => array( 'raw' => 'Content', ), - 'original_file_exists' => false, + 'has_theme_file' => false, ), $data ); @@ -216,22 +216,22 @@ public function test_create_item() { $this->assertEquals( array( - 'id' => 'tt1-blocks//my_custom_template_part', - 'theme' => 'tt1-blocks', - 'slug' => 'my_custom_template_part', - 'title' => array( + 'id' => 'tt1-blocks//my_custom_template_part', + 'theme' => 'tt1-blocks', + 'slug' => 'my_custom_template_part', + 'title' => array( 'raw' => 'My Template Part', 'rendered' => 'My Template Part', ), - 'description' => 'Just a description of a template part', - 'status' => 'publish', - 'is_custom' => true, - 'type' => 'wp_template_part', - 'content' => array( + 'description' => 'Just a description of a template part', + 'status' => 'publish', + 'is_custom' => true, + 'type' => 'wp_template_part', + 'content' => array( 'raw' => 'Content', ), - 'area' => 'header', - 'original_file_exists' => false, + 'area' => 'header', + 'has_theme_file' => false, ), $data ); From a9ce620d3faadde3230a2a6bc8c3cbfc9ae4919c Mon Sep 17 00:00:00 2001 From: David Szabo Date: Mon, 29 Mar 2021 15:14:31 +0200 Subject: [PATCH 28/35] Use edited entity template --- packages/edit-site/src/components/header/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index eff62335460e4..a3dc28a5858ca 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -14,6 +14,8 @@ import { _x, __ } from '@wordpress/i18n'; import { listView, plus } from '@wordpress/icons'; import { Button } from '@wordpress/components'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; +import { store as editorStore } from '@wordpress/editor'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -46,15 +48,15 @@ export default function Header( { openEntitiesSavedStates } ) { isInserterOpened, isListViewOpened, } = select( editSiteStore ); - const { getEntityRecord } = select( 'core' ); + const { getEditedEntityRecord } = select( coreStore ); const { __experimentalGetTemplateInfo: getTemplateInfo } = select( - 'core/editor' + editorStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); const postType = getEditedPostType(); const postId = getEditedPostId(); - const record = getEntityRecord( 'postType', postType, postId ); + const record = getEditedEntityRecord( 'postType', postType, postId ); const _entityTitle = 'wp_template' === postType ? getTemplateInfo( record ).title From 01bdfe1fe830155264006f5a106c8729e83442d4 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 00:31:50 +0200 Subject: [PATCH 29/35] Update ID format --- lib/full-site-editing/block-templates.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index 4b7139da7178f..a31378d051f03 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -335,7 +335,7 @@ function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_t /** * Retrieves a single unified template object using its id. * - * @param string $id Template unique identifier (example: theme|slug). + * @param string $id Template unique identifier (example: theme_slug//template_slug). * @param array $template_type wp_template or wp_template_part. * * @return WP_Block_Template|null Template. @@ -378,7 +378,7 @@ function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { * Retrieves a single unified template object using its id. * Retrieves the file template. * - * @param string $id Template unique identifier (example: theme|slug). + * @param string $id Template unique identifier (example: theme_slug//template_slug). * @param array $template_type wp_template or wp_template_part. * * @return WP_Block_Template|null File template. From 4a01921541acc2968bacfa6a042df84e5654b065 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 00:44:51 +0200 Subject: [PATCH 30/35] Try apiFetch --- packages/edit-site/src/store/actions.js | 27 ++++++++----------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 3e885e3d18b8c..c9b15b09a6c24 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -4,7 +4,7 @@ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks'; import { controls, dispatch } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; -import { getPathAndQueryString } from '@wordpress/url'; +import { addQueryArgs, getPathAndQueryString } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; @@ -320,16 +320,14 @@ export function* revertTemplate( template ) { } try { - // This will override the current template entity - // so we need to refetch after this call. - const fileTemplate = yield controls.resolveSelect( - coreStore, - 'getEntityRecord', - 'postType', - 'wp_template', - template.id, - { is_custom: false } + const fileTemplatePath = addQueryArgs( + `/wp/v2/templates/${ template.id }`, + { + is_custom: false, + context: 'edit', + } ); + const fileTemplate = yield apiFetch( { path: fileTemplatePath } ); if ( ! fileTemplate ) { yield controls.dispatch( noticesStore, @@ -341,15 +339,6 @@ export function* revertTemplate( template ) { ); return; } - // Refetch the template entity - yield controls.resolveSelect( - coreStore, - 'getEntityRecord', - 'postType', - 'wp_template', - template.id, - { is_custom: true } - ); const serializeBlocks = ( { blocks: blocksForSerialization = [] } ) => __unstableSerializeAndClean( blocksForSerialization ); From 5a6cd9fbe32a069825cb8ad32dfe7f7fbf4324ba Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 15:39:03 +0200 Subject: [PATCH 31/35] Use source instead of is_custom --- lib/full-site-editing/block-templates.php | 4 +-- .../class-wp-block-template.php | 6 ++--- .../class-wp-rest-templates-controller.php | 26 +++++++++---------- packages/edit-site/src/store/actions.js | 12 +++------ .../src/utils/is-template-revertable.js | 2 +- phpunit/class-block-templates-test.php | 16 ++++++------ ...class-wp-rest-template-controller-test.php | 16 ++++++------ 7 files changed, 39 insertions(+), 43 deletions(-) diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index a31378d051f03..598d4a6977633 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -189,7 +189,7 @@ function _gutenberg_build_template_result_from_file( $template_file, $template_t $template->theme = $theme; $template->content = _inject_theme_attribute_in_content( $template_content ); $template->slug = $template_file['slug']; - $template->is_custom = false; + $template->source = 'theme'; $template->type = $template_type; $template->title = $template_file['slug']; $template->status = 'publish'; @@ -235,7 +235,7 @@ function _gutenberg_build_template_result_from_post( $post ) { $template->theme = $theme; $template->content = $post->post_content; $template->slug = $post->post_name; - $template->is_custom = true; + $template->source = 'custom'; $template->type = $post->post_type; $template->description = $post->post_excerpt; $template->title = $post->post_title; diff --git a/lib/full-site-editing/class-wp-block-template.php b/lib/full-site-editing/class-wp-block-template.php index 5a5872e05e06f..d8c9d5c54a2b9 100644 --- a/lib/full-site-editing/class-wp-block-template.php +++ b/lib/full-site-editing/class-wp-block-template.php @@ -61,11 +61,11 @@ class WP_Block_Template { public $description = ''; /** - * Whether it's a theme file template or a custom one. + * Source of the content. `theme` and `custom` is used for now. * - * @var boolean + * @var string */ - public $is_custom = false; + public $source = 'theme'; /** * Post Id. diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index fad2baa1ee3c2..e679736a911be 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -168,7 +168,7 @@ public function get_item_permissions_check( $request ) { * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { - if ( isset( $request['is_custom'] ) && 'false' === $request['is_custom'] ) { + if ( isset( $request['source'] ) && 'theme' === $request['source'] ) { $template = gutenberg_get_block_file_template( $request['id'], $this->post_type ); } else { $template = gutenberg_get_block_template( $request['id'], $this->post_type ); @@ -203,14 +203,14 @@ public function update_item( $request ) { return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.', 'gutenberg' ), array( 'status' => 404 ) ); } - if ( isset( $request['reverted_file_content'] ) && isset( $request['content'] ) && $request['reverted_file_content'] === $request['content'] ) { + if ( isset( $request['source'] ) && 'theme' === $request['source'] ) { wp_delete_post( $template->wp_id, true ); return $this->prepare_item_for_response( gutenberg_get_block_file_template( $request['id'], $this->post_type ), $request ); } $changes = $this->prepare_item_for_database( $request ); - if ( $template->is_custom ) { + if ( 'custom' === $template->source ) { $result = wp_update_post( wp_slash( (array) $changes ), true ); } else { $result = wp_insert_post( wp_slash( (array) $changes ), true ); @@ -292,7 +292,7 @@ public function delete_item( $request ) { if ( ! $template ) { return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.', 'gutenberg' ), array( 'status' => 404 ) ); } - if ( ! $template->is_custom ) { + if ( 'custom' !== $template->source ) { return new WP_Error( 'rest_invalid_template', __( 'Templates based on theme files can\'t be removed.', 'gutenberg' ), array( 'status' => 400 ) ); } @@ -344,7 +344,7 @@ protected function prepare_item_for_database( $request ) { $changes->tax_input = array( 'wp_theme' => isset( $request['theme'] ) ? $request['content'] : wp_get_theme()->get_stylesheet(), ); - } elseif ( ! $template->is_custom ) { + } elseif ( 'custom' !== $template->source ) { $changes->post_type = $this->post_type; $changes->post_status = 'publish'; $changes->tax_input = array( @@ -356,24 +356,24 @@ protected function prepare_item_for_database( $request ) { } if ( isset( $request['content'] ) ) { $changes->post_content = $request['content']; - } elseif ( null !== $template && ! $template->is_custom ) { + } elseif ( null !== $template && 'custom' !== $template->source) { $changes->post_content = $template->content; } if ( isset( $request['title'] ) ) { $changes->post_title = $request['title']; - } elseif ( null !== $template && ! $template->is_custom ) { + } elseif ( null !== $template && 'custom' !== $template->source ) { $changes->post_title = $template->title; } if ( isset( $request['description'] ) ) { $changes->post_excerpt = $request['description']; - } elseif ( null !== $template && ! $template->is_custom ) { + } elseif ( null !== $template && 'custom' !== $template->source ) { $changes->post_excerpt = $template->description; } if ( 'wp_template_part' === $this->post_type ) { if ( isset( $request['area'] ) ) { $changes->tax_input['wp_template_part_area'] = gutenberg_filter_template_part_area( $request['area'] ); - } elseif ( null !== $template && ! $template->is_custom && $template->area ) { + } elseif ( null !== $template && 'custom' !== $template->source && $template->area ) { $changes->tax_input['wp_template_part_area'] = gutenberg_filter_template_part_area( $template->area ); } elseif ( ! $template->area ) { $changes->tax_input['wp_template_part_area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED; @@ -397,7 +397,7 @@ public function prepare_item_for_response( $template, $request ) { // phpcs:igno 'theme' => $template->theme, 'content' => array( 'raw' => $template->content ), 'slug' => $template->slug, - 'is_custom' => $template->is_custom, + 'source' => $template->source, 'type' => $template->type, 'description' => $template->description, 'title' => array( @@ -524,9 +524,9 @@ public function get_item_schema() { 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), ), - 'is_custom' => array( - 'description' => __( 'Whether the template is customized.', 'gutenberg' ), - 'type' => 'bool', + 'source' => array( + 'description' => __( 'Source of template', 'gutenberg' ), + 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index c9b15b09a6c24..5dbe5feac22fa 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -323,7 +323,7 @@ export function* revertTemplate( template ) { const fileTemplatePath = addQueryArgs( `/wp/v2/templates/${ template.id }`, { - is_custom: false, + source: 'theme', context: 'edit', } ); @@ -360,7 +360,7 @@ export function* revertTemplate( template ) { { content: serializeBlocks, // required to make the `undo` behave correctly blocks: edited.blocks, // required to revert the blocks in the editor - is_custom: true, // required to avoid turning the editor into a dirty state + source: 'custom', // required to avoid turning the editor into a dirty state }, { undoIgnore: true, // required to merge this edit with the last undo level @@ -368,9 +368,6 @@ export function* revertTemplate( template ) { ); const blocks = parse( fileTemplate?.content?.raw ); - // `reverted_file_content` is used on the server-side - // it is compared with the template's content - // if they match then the template is deleted (reverted to file) yield controls.dispatch( coreStore, 'editEntityRecord', @@ -380,8 +377,7 @@ export function* revertTemplate( template ) { { content: serializeBlocks, blocks, - is_custom: false, - reverted_file_content: __unstableSerializeAndClean( blocks ), + source: 'theme', } ); @@ -393,7 +389,7 @@ export function* revertTemplate( template ) { { content: serializeBlocks, blocks: edited.blocks, - is_custom: true, + source: 'custom', } ); }; diff --git a/packages/edit-site/src/utils/is-template-revertable.js b/packages/edit-site/src/utils/is-template-revertable.js index cd99d67c37e1b..c2d42f51c9701 100644 --- a/packages/edit-site/src/utils/is-template-revertable.js +++ b/packages/edit-site/src/utils/is-template-revertable.js @@ -9,6 +9,6 @@ export default function isTemplateRevertable( template ) { return false; } /* eslint-disable camelcase */ - return template?.is_custom && template?.has_theme_file; + return template?.source === 'custom' && template?.has_theme_file; /* eslint-enable camelcase */ } diff --git a/phpunit/class-block-templates-test.php b/phpunit/class-block-templates-test.php index 34fc0cc51746c..7024f1911a69b 100644 --- a/phpunit/class-block-templates-test.php +++ b/phpunit/class-block-templates-test.php @@ -74,7 +74,7 @@ function test_gutenberg_build_template_result_from_file() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'single', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( false, $template->is_custom ); + $this->assertEquals( 'theme', $template->source ); $this->assertEquals( 'Single', $template->title ); $this->assertEquals( 'Template used to display a single blog post.', $template->description ); $this->assertEquals( 'wp_template', $template->type ); @@ -92,7 +92,7 @@ function test_gutenberg_build_template_result_from_file() { $this->assertEquals( get_stylesheet(), $template_part->theme ); $this->assertEquals( 'header', $template_part->slug ); $this->assertEquals( 'publish', $template_part->status ); - $this->assertEquals( false, $template_part->is_custom ); + $this->assertEquals( 'theme', $template_part->source ); $this->assertEquals( 'header', $template_part->title ); $this->assertEquals( '', $template_part->description ); $this->assertEquals( 'wp_template_part', $template_part->type ); @@ -110,7 +110,7 @@ function test_gutenberg_build_template_result_from_post() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'my_template', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( true, $template->is_custom ); + $this->assertEquals( 'custom', $template->source ); $this->assertEquals( 'My Template', $template->title ); $this->assertEquals( 'Description of my template', $template->description ); $this->assertEquals( 'wp_template', $template->type ); @@ -125,7 +125,7 @@ function test_gutenberg_build_template_result_from_post() { $this->assertEquals( get_stylesheet(), $template_part->theme ); $this->assertEquals( 'my_template_part', $template_part->slug ); $this->assertEquals( 'publish', $template_part->status ); - $this->assertEquals( true, $template_part->is_custom ); + $this->assertEquals( 'custom', $template_part->source ); $this->assertEquals( 'My Template Part', $template_part->title ); $this->assertEquals( 'Description of my template part', $template_part->description ); $this->assertEquals( 'wp_template_part', $template_part->type ); @@ -172,7 +172,7 @@ function test_gutenberg_get_block_template_from_file() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'index', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( false, $template->is_custom ); + $this->assertEquals( 'theme', $template->source ); $this->assertEquals( 'wp_template', $template->type ); // Test template parts. @@ -182,7 +182,7 @@ function test_gutenberg_get_block_template_from_file() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'header', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( false, $template->is_custom ); + $this->assertEquals( 'theme', $template->source ); $this->assertEquals( 'wp_template_part', $template->type ); // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. $this->assertEquals( WP_TEMPLATE_PART_AREA_UNCATEGORIZED, $template->area ); @@ -198,7 +198,7 @@ function test_gutenberg_get_block_template_from_post() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'my_template', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( true, $template->is_custom ); + $this->assertEquals( 'custom', $template->source ); $this->assertEquals( 'wp_template', $template->type ); // Test template parts. @@ -208,7 +208,7 @@ function test_gutenberg_get_block_template_from_post() { $this->assertEquals( get_stylesheet(), $template->theme ); $this->assertEquals( 'my_template_part', $template->slug ); $this->assertEquals( 'publish', $template->status ); - $this->assertEquals( true, $template->is_custom ); + $this->assertEquals( 'custom', $template->source ); $this->assertEquals( 'wp_template_part', $template->type ); $this->assertEquals( WP_TEMPLATE_PART_AREA_SIDEBAR, $template->area ); } diff --git a/phpunit/class-wp-rest-template-controller-test.php b/phpunit/class-wp-rest-template-controller-test.php index 6a5080a2e6246..2f2b9f4735a0d 100644 --- a/phpunit/class-wp-rest-template-controller-test.php +++ b/phpunit/class-wp-rest-template-controller-test.php @@ -71,7 +71,7 @@ function find_and_normalize_template_by_id( $templates, $id ) { ), 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', 'status' => 'publish', - 'is_custom' => false, + 'source' => 'theme', 'type' => 'wp_template', 'wp_id' => null, 'has_theme_file' => true, @@ -95,7 +95,7 @@ function find_and_normalize_template_by_id( $templates, $id ) { ), 'description' => '', 'status' => 'publish', - 'is_custom' => false, + 'source' => 'theme', 'type' => 'wp_template_part', 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. @@ -125,7 +125,7 @@ public function test_get_item() { ), 'description' => 'The default template used when no other template is available. This is a required template in WordPress.', 'status' => 'publish', - 'is_custom' => false, + 'source' => 'theme', 'type' => 'wp_template', 'wp_id' => null, 'has_theme_file' => true, @@ -150,7 +150,7 @@ public function test_get_item() { ), 'description' => '', 'status' => 'publish', - 'is_custom' => false, + 'source' => 'theme', 'type' => 'wp_template_part', 'wp_id' => null, // TODO - update 'UNCATEGORIZED' to 'HEADER' once tt1-blocks theme.json updated for template part area info. @@ -188,7 +188,7 @@ public function test_create_item() { ), 'description' => 'Just a description', 'status' => 'publish', - 'is_custom' => true, + 'source' => 'custom', 'type' => 'wp_template', 'content' => array( 'raw' => 'Content', @@ -225,7 +225,7 @@ public function test_create_item() { ), 'description' => 'Just a description of a template part', 'status' => 'publish', - 'is_custom' => true, + 'source' => 'custom', 'type' => 'wp_template_part', 'content' => array( 'raw' => 'Content', @@ -248,7 +248,7 @@ public function test_update_item() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( 'My new Index Title', $data['title']['raw'] ); - $this->assertEquals( true, $data['is_custom'] ); + $this->assertEquals( 'custom', $data['source'] ); // Test template parts. $request = new WP_REST_Request( 'PUT', '/wp/v2/template-parts/tt1-blocks//header' ); @@ -260,7 +260,7 @@ public function test_update_item() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( WP_TEMPLATE_PART_AREA_UNCATEGORIZED, $data['area'] ); - $this->assertEquals( true, $data['is_custom'] ); + $this->assertEquals( 'custom', $data['source'] ); } public function test_delete_item() { From 360cb85453cae0f1b8797e91148f276ee76bb061 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 18:01:00 +0200 Subject: [PATCH 32/35] Don't use hardcoded path --- packages/edit-site/src/store/actions.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 5dbe5feac22fa..4bad708c4f913 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -320,12 +320,27 @@ export function* revertTemplate( template ) { } try { + const templateEntity = yield controls.select( + coreStore, + 'getEntity', + 'postType', + template.type + ); + if ( ! templateEntity ) { + yield controls.dispatch( + noticesStore, + 'createErrorNotice', + __( + 'The editor has encountered an unexpected error. Please reload.' + ), + { type: 'snackbar' } + ); + return; + } + const fileTemplatePath = addQueryArgs( - `/wp/v2/templates/${ template.id }`, - { - source: 'theme', - context: 'edit', - } + `${ templateEntity.baseURL }/${ template.id }`, + { context: 'edit', source: 'theme' } ); const fileTemplate = yield apiFetch( { path: fileTemplatePath } ); if ( ! fileTemplate ) { From e64606523f625c99161668869e16b11df4c7bd8e Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 18:35:15 +0200 Subject: [PATCH 33/35] Format --- lib/full-site-editing/class-wp-rest-templates-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php index e679736a911be..e6b3a90efd3d3 100644 --- a/lib/full-site-editing/class-wp-rest-templates-controller.php +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -210,7 +210,7 @@ public function update_item( $request ) { $changes = $this->prepare_item_for_database( $request ); - if ( 'custom' === $template->source ) { + if ( 'custom' === $template->source ) { $result = wp_update_post( wp_slash( (array) $changes ), true ); } else { $result = wp_insert_post( wp_slash( (array) $changes ), true ); @@ -356,7 +356,7 @@ protected function prepare_item_for_database( $request ) { } if ( isset( $request['content'] ) ) { $changes->post_content = $request['content']; - } elseif ( null !== $template && 'custom' !== $template->source) { + } elseif ( null !== $template && 'custom' !== $template->source ) { $changes->post_content = $template->content; } if ( isset( $request['title'] ) ) { From fb73a6b111ef865da025af0c3ba1655dbf8b0cac Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 20:12:40 +0200 Subject: [PATCH 34/35] Use variable for border width --- packages/edit-site/src/components/template-details/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/template-details/style.scss b/packages/edit-site/src/components/template-details/style.scss index d8e63b12865cc..8471ac1d878ce 100644 --- a/packages/edit-site/src/components/template-details/style.scss +++ b/packages/edit-site/src/components/template-details/style.scss @@ -41,6 +41,6 @@ } &:focus:not(:disabled) { - box-shadow: inset 0 0 0 1.5px var(--wp-admin-theme-color), inset 0 0 0 3px #fff; + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px #fff; } } From 2d8aa9e46c8d5a02ba98fbfc001ce20cf32cfc38 Mon Sep 17 00:00:00 2001 From: David Szabo Date: Wed, 31 Mar 2021 20:39:08 +0200 Subject: [PATCH 35/35] Use variables --- packages/edit-site/src/components/template-details/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/template-details/style.scss b/packages/edit-site/src/components/template-details/style.scss index 8471ac1d878ce..b2e0982b0b072 100644 --- a/packages/edit-site/src/components/template-details/style.scss +++ b/packages/edit-site/src/components/template-details/style.scss @@ -19,7 +19,7 @@ text-align: left; &:focus:not(:disabled) { - box-shadow: inset 0 0 0 1.5px var(--wp-admin-theme-color), inset 0 0 0 3px #fff; + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px $white; } } } @@ -41,6 +41,6 @@ } &:focus:not(:disabled) { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px #fff; + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px $white; } }