Skip to content

Commit b343603

Browse files
Editor: Adds template types, is_wp_suggestion, and fallback template content.
This commit improves site editor templates by: * Adds a post meta `is_wp_suggestion` to templates created from the site editor. Why? To differentiate the templates created from the post editor in the Template panel in inspector controls and the templates suggested in site editor. See [WordPress/gutenberg#41387 Gutenberg PR 41387] for more details. * Expands the template types that can be added to the site editor to include single custom post type and specific posts templates. See [WordPress/gutenberg#41189 Gutenberg PR 41189] for more details. * Adds fallback template content on creation in site editor: * Introduces `get_template_hierarchy()` to get the template hierarchy for a given template slug to be created. * Adds a `lookup` route to `WP_REST_Templates_Controller` to get the fallback template content. See [WordPress/gutenberg#42520 Gutenberg PR 42520] for more details. * Fixes a typo in default category template's description within `get_default_block_template_types()`. See [WordPress/gutenberg#42586 Gutenberg PR 42586] for more details. * Changes field checks from `in_array()` to `rest_is_field_included()` in `WP_REST_Post_Types_Controller`. * Adds an `icon` field to `WP_REST_Post_Types_Controller` Follow-up to [53129], [52331], [52275], [52062], [51962], [43087]. Props ntsekouras, spacedmonkey, mamaduka, mburridge, jameskoster, bernhard-reiter, mcsf, hellofromTonya. See #56467. git-svn-id: https://develop.svn.wordpress.org/trunk@54269 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 072d9da commit b343603

File tree

10 files changed

+583
-31
lines changed

10 files changed

+583
-31
lines changed

phpcs.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237

238238
<!-- Test case parent classes outside of the "includes" folder. -->
239239
<element value="Tests_Query_Conditionals"/>
240+
<element value="WP_Block_Templates_UnitTestCase"/>
240241
<element value="WP_Filesystem_UnitTestCase"/>
241242
<element value="WP_HTTP_UnitTestCase"/>
242243
<element value="WP_Image_UnitTestCase"/>

src/wp-includes/block-template-utils.php

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function get_default_block_template_types() {
147147
),
148148
'category' => array(
149149
'title' => _x( 'Category', 'Template name' ),
150-
'description' => __( 'Displays latest posts in single post category.' ),
150+
'description' => __( 'Displays latest posts from a single post category.' ),
151151
),
152152
'taxonomy' => array(
153153
'title' => _x( 'Taxonomy', 'Template name' ),
@@ -555,7 +555,8 @@ function _build_block_template_result_from_post( $post ) {
555555
$template_file = _get_block_template_file( $post->post_type, $post->post_name );
556556
$has_theme_file = wp_get_theme()->get_stylesheet() === $theme && null !== $template_file;
557557

558-
$origin = get_post_meta( $post->ID, 'origin', true );
558+
$origin = get_post_meta( $post->ID, 'origin', true );
559+
$is_wp_suggestion = get_post_meta( $post->ID, 'is_wp_suggestion', true );
559560

560561
$template = new WP_Block_Template();
561562
$template->wp_id = $post->ID;
@@ -570,7 +571,7 @@ function _build_block_template_result_from_post( $post ) {
570571
$template->title = $post->post_title;
571572
$template->status = $post->post_status;
572573
$template->has_theme_file = $has_theme_file;
573-
$template->is_custom = true;
574+
$template->is_custom = empty( $is_wp_suggestion );
574575
$template->author = $post->post_author;
575576

576577
if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
@@ -679,7 +680,8 @@ function get_block_templates( $query = array(), $template_type = 'wp_template' )
679680
continue;
680681
}
681682

682-
if ( $post_type &&
683+
if (
684+
$post_type &&
683685
isset( $template->post_types ) &&
684686
! in_array( $post_type, $template->post_types, true )
685687
) {
@@ -912,9 +914,10 @@ function block_footer_area() {
912914
* @return Bool Whether this file is in an ignored directory.
913915
*/
914916
function wp_is_theme_directory_ignored( $path ) {
915-
$directories_to_ignore = array( '.svn', '.git', '.hg', '.bzr', 'node_modules', 'vendor' );
917+
$directories_to_ignore = array( '.DS_Store', '.svn', '.git', '.hg', '.bzr', 'node_modules', 'vendor' );
918+
916919
foreach ( $directories_to_ignore as $directory ) {
917-
if ( strpos( $path, $directory ) === 0 ) {
920+
if ( str_starts_with( $path, $directory ) ) {
918921
return true;
919922
}
920923
}
@@ -1023,3 +1026,74 @@ function wp_generate_block_templates_export_file() {
10231026

10241027
return $filename;
10251028
}
1029+
1030+
/**
1031+
* Gets the template hierarchy for the given template slug to be created.
1032+
*
1033+
*
1034+
* Note: Always add `index` as the last fallback template.
1035+
*
1036+
* @since 6.1.0
1037+
*
1038+
* @param string $slug The template slug to be created.
1039+
* @param boolean $is_custom Optional. Indicates if a template is custom or
1040+
* part of the template hierarchy. Default false.
1041+
* @param string $template_prefix Optional. The template prefix for the created template.
1042+
* Used to extract the main template type, e.g.
1043+
* in `taxonomy-books` the `taxonomy` is extracted.
1044+
* Default empty string.
1045+
* @return string[] The template hierarchy.
1046+
*/
1047+
function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) {
1048+
if ( 'index' === $slug ) {
1049+
return array( 'index' );
1050+
}
1051+
if ( $is_custom ) {
1052+
return array( 'page', 'singular', 'index' );
1053+
}
1054+
if ( 'front-page' === $slug ) {
1055+
return array( 'front-page', 'home', 'index' );
1056+
}
1057+
1058+
$template_hierarchy = array( $slug );
1059+
1060+
// Most default templates don't have `$template_prefix` assigned.
1061+
if ( $template_prefix ) {
1062+
list( $type ) = explode( '-', $template_prefix );
1063+
// These checks are needed because the `$slug` above is always added.
1064+
if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) {
1065+
$template_hierarchy[] = $template_prefix;
1066+
}
1067+
if ( $slug !== $type ) {
1068+
$template_hierarchy[] = $type;
1069+
}
1070+
}
1071+
1072+
// Handle `archive` template.
1073+
if (
1074+
str_starts_with( $slug, 'author' ) ||
1075+
str_starts_with( $slug, 'taxonomy' ) ||
1076+
str_starts_with( $slug, 'category' ) ||
1077+
str_starts_with( $slug, 'tag' ) ||
1078+
'date' === $slug
1079+
) {
1080+
$template_hierarchy[] = 'archive';
1081+
}
1082+
// Handle `single` template.
1083+
if ( 'attachment' === $slug ) {
1084+
$template_hierarchy[] = 'single';
1085+
}
1086+
1087+
// Handle `singular` template.
1088+
if (
1089+
str_starts_with( $slug, 'single' ) ||
1090+
str_starts_with( $slug, 'page' ) ||
1091+
'attachment' === $slug
1092+
) {
1093+
$template_hierarchy[] = 'singular';
1094+
}
1095+
1096+
$template_hierarchy[] = 'index';
1097+
1098+
return $template_hierarchy;
1099+
};

src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -186,54 +186,58 @@ public function prepare_item_for_response( $item, $request ) {
186186
$fields = $this->get_fields_for_response( $request );
187187
$data = array();
188188

189-
if ( in_array( 'capabilities', $fields, true ) ) {
189+
if ( rest_is_field_included( 'capabilities', $fields ) ) {
190190
$data['capabilities'] = $post_type->cap;
191191
}
192192

193-
if ( in_array( 'description', $fields, true ) ) {
193+
if ( rest_is_field_included( 'description', $fields ) ) {
194194
$data['description'] = $post_type->description;
195195
}
196196

197-
if ( in_array( 'hierarchical', $fields, true ) ) {
197+
if ( rest_is_field_included( 'hierarchical', $fields ) ) {
198198
$data['hierarchical'] = $post_type->hierarchical;
199199
}
200200

201-
if ( in_array( 'visibility', $fields, true ) ) {
201+
if ( rest_is_field_included( 'visibility', $fields ) ) {
202202
$data['visibility'] = array(
203203
'show_in_nav_menus' => (bool) $post_type->show_in_nav_menus,
204204
'show_ui' => (bool) $post_type->show_ui,
205205
);
206206
}
207207

208-
if ( in_array( 'viewable', $fields, true ) ) {
208+
if ( rest_is_field_included( 'viewable', $fields ) ) {
209209
$data['viewable'] = is_post_type_viewable( $post_type );
210210
}
211211

212-
if ( in_array( 'labels', $fields, true ) ) {
212+
if ( rest_is_field_included( 'labels', $fields ) ) {
213213
$data['labels'] = $post_type->labels;
214214
}
215215

216-
if ( in_array( 'name', $fields, true ) ) {
216+
if ( rest_is_field_included( 'name', $fields ) ) {
217217
$data['name'] = $post_type->label;
218218
}
219219

220-
if ( in_array( 'slug', $fields, true ) ) {
220+
if ( rest_is_field_included( 'slug', $fields ) ) {
221221
$data['slug'] = $post_type->name;
222222
}
223223

224-
if ( in_array( 'supports', $fields, true ) ) {
224+
if ( rest_is_field_included( 'icon', $fields ) ) {
225+
$data['icon'] = $post_type->menu_icon;
226+
}
227+
228+
if ( rest_is_field_included( 'supports', $fields ) ) {
225229
$data['supports'] = $supports;
226230
}
227231

228-
if ( in_array( 'taxonomies', $fields, true ) ) {
232+
if ( rest_is_field_included( 'taxonomies', $fields ) ) {
229233
$data['taxonomies'] = array_values( $taxonomies );
230234
}
231235

232-
if ( in_array( 'rest_base', $fields, true ) ) {
236+
if ( rest_is_field_included( 'rest_base', $fields ) ) {
233237
$data['rest_base'] = $base;
234238
}
235239

236-
if ( in_array( 'rest_namespace', $fields, true ) ) {
240+
if ( rest_is_field_included( 'rest_namespace', $fields ) ) {
237241
$data['rest_namespace'] = $namespace;
238242
}
239243

@@ -287,6 +291,7 @@ protected function prepare_links( $post_type ) {
287291
* @since 4.7.0
288292
* @since 4.8.0 The `supports` property was added.
289293
* @since 5.9.0 The `visibility` and `rest_namespace` properties were added.
294+
* @since 6.1.0 The `icon` property was added.
290295
*
291296
* @return array Item schema data.
292297
*/
@@ -385,6 +390,12 @@ public function get_item_schema() {
385390
),
386391
),
387392
),
393+
'icon' => array(
394+
'description' => __( 'The icon for the post type.' ),
395+
'type' => array( 'string', 'null' ),
396+
'context' => array( 'view', 'edit', 'embed' ),
397+
'readonly' => true,
398+
),
388399
),
389400
);
390401

src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function __construct( $post_type ) {
4242
* Registers the controllers routes.
4343
*
4444
* @since 5.8.0
45+
* @since 6.1.0 Endpoint for fallback template content.
4546
*/
4647
public function register_routes() {
4748
// Lists all templates.
@@ -65,6 +66,34 @@ public function register_routes() {
6566
)
6667
);
6768

69+
// Get fallback template content.
70+
register_rest_route(
71+
$this->namespace,
72+
'/' . $this->rest_base . '/lookup',
73+
array(
74+
array(
75+
'methods' => WP_REST_Server::READABLE,
76+
'callback' => array( $this, 'get_template_fallback' ),
77+
'permission_callback' => array( $this, 'get_item_permissions_check' ),
78+
'args' => array(
79+
'slug' => array(
80+
'description' => __( 'The slug of the template to get the fallback for' ),
81+
'type' => 'string',
82+
'required' => true,
83+
),
84+
'is_custom' => array(
85+
'description' => __( ' Indicates if a template is custom or part of the template hierarchy' ),
86+
'type' => 'boolean',
87+
),
88+
'template_prefix' => array(
89+
'description' => __( 'The template prefix for the created template. This is used to extract the main template type, e.g. in `taxonomy-books` extracts the `taxonomy`' ),
90+
'type' => 'string',
91+
),
92+
),
93+
),
94+
)
95+
);
96+
6897
// Lists/updates a single template based on the given id.
6998
register_rest_route(
7099
$this->namespace,
@@ -117,6 +146,21 @@ public function register_routes() {
117146
);
118147
}
119148

149+
/**
150+
* Returns the fallback template for the given slug.
151+
*
152+
* @since 6.1.0
153+
*
154+
* @param WP_REST_Request $request The request instance.
155+
* @return WP_REST_Response|WP_Error
156+
*/
157+
public function get_template_fallback( $request ) {
158+
$hierarchy = get_template_hierarchy( $request['slug'], $request['is_custom'], $request['template_prefix'] );
159+
$fallback_template = resolve_block_template( $request['slug'], $hierarchy, '' );
160+
$response = $this->prepare_item_for_response( $fallback_template, $request );
161+
return rest_ensure_response( $response );
162+
}
163+
120164
/**
121165
* Checks if the user has permissions to make the request.
122166
*
@@ -525,6 +569,15 @@ protected function prepare_item_for_database( $request ) {
525569
$changes->post_excerpt = $template->description;
526570
}
527571

572+
if ( 'wp_template' === $this->post_type && isset( $request['is_wp_suggestion'] ) ) {
573+
$changes->meta_input = wp_parse_args(
574+
array(
575+
'is_wp_suggestion' => $request['is_wp_suggestion'],
576+
),
577+
$changes->meta_input = array()
578+
);
579+
}
580+
528581
if ( 'wp_template_part' === $this->post_type ) {
529582
if ( isset( $request['area'] ) ) {
530583
$changes->tax_input['wp_template_part_area'] = _filter_block_template_part_area( $request['area'] );
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/**
4+
* @group block-templates
5+
* @covers ::get_template_hierarchy
6+
*/
7+
abstract class WP_Block_Templates_UnitTestCase extends WP_UnitTestCase {
8+
const TEST_THEME = 'block-theme';
9+
10+
protected static $template_post;
11+
protected static $template_part_post;
12+
13+
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
14+
/*
15+
* Set up a template post corresponding to a different theme.
16+
* Do this to ensure resolution and slug creation works as expected,
17+
* even with another post of that same name present for another theme.
18+
*/
19+
self::$template_post = $factory->post->create_and_get(
20+
array(
21+
'post_type' => 'wp_template',
22+
'post_name' => 'my_template',
23+
'post_title' => 'My Template',
24+
'post_content' => 'Content',
25+
'post_excerpt' => 'Description of my template',
26+
'tax_input' => array(
27+
'wp_theme' => array(
28+
'this-theme-should-not-resolve',
29+
),
30+
),
31+
)
32+
);
33+
34+
wp_set_post_terms( self::$template_post->ID, 'this-theme-should-not-resolve', 'wp_theme' );
35+
36+
// Set up template post.
37+
self::$template_post = $factory->post->create_and_get(
38+
array(
39+
'post_type' => 'wp_template',
40+
'post_name' => 'my_template',
41+
'post_title' => 'My Template',
42+
'post_content' => 'Content',
43+
'post_excerpt' => 'Description of my template',
44+
'tax_input' => array(
45+
'wp_theme' => array(
46+
self::TEST_THEME,
47+
),
48+
),
49+
)
50+
);
51+
52+
wp_set_post_terms( self::$template_post->ID, self::TEST_THEME, 'wp_theme' );
53+
54+
// Set up template part post.
55+
self::$template_part_post = $factory->post->create_and_get(
56+
array(
57+
'post_type' => 'wp_template_part',
58+
'post_name' => 'my_template_part',
59+
'post_title' => 'My Template Part',
60+
'post_content' => 'Content',
61+
'post_excerpt' => 'Description of my template part',
62+
'tax_input' => array(
63+
'wp_theme' => array(
64+
self::TEST_THEME,
65+
),
66+
'wp_template_part_area' => array(
67+
WP_TEMPLATE_PART_AREA_HEADER,
68+
),
69+
),
70+
)
71+
);
72+
73+
wp_set_post_terms( self::$template_part_post->ID, WP_TEMPLATE_PART_AREA_HEADER, 'wp_template_part_area' );
74+
wp_set_post_terms( self::$template_part_post->ID, self::TEST_THEME, 'wp_theme' );
75+
}
76+
77+
public static function wpTearDownAfterClass() {
78+
wp_delete_post( self::$template_post->ID );
79+
wp_delete_post( self::$template_part_post->ID );
80+
}
81+
82+
public function set_up() {
83+
parent::set_up();
84+
switch_theme( self::TEST_THEME );
85+
}
86+
}

0 commit comments

Comments
 (0)