35 * @type string $wp_template Theme-relative directory name for block templates. |
35 * @type string $wp_template Theme-relative directory name for block templates. |
36 * @type string $wp_template_part Theme-relative directory name for block template parts. |
36 * @type string $wp_template_part Theme-relative directory name for block template parts. |
37 * } |
37 * } |
38 */ |
38 */ |
39 function get_block_theme_folders( $theme_stylesheet = null ) { |
39 function get_block_theme_folders( $theme_stylesheet = null ) { |
40 $theme_name = null === $theme_stylesheet ? get_stylesheet() : $theme_stylesheet; |
40 $theme = wp_get_theme( (string) $theme_stylesheet ); |
41 $root_dir = get_theme_root( $theme_name ); |
41 if ( ! $theme->exists() ) { |
42 $theme_dir = "$root_dir/$theme_name"; |
42 // Return the default folders if the theme doesn't exist. |
43 |
|
44 if ( file_exists( $theme_dir . '/block-templates' ) || file_exists( $theme_dir . '/block-template-parts' ) ) { |
|
45 return array( |
43 return array( |
46 'wp_template' => 'block-templates', |
44 'wp_template' => 'templates', |
47 'wp_template_part' => 'block-template-parts', |
45 'wp_template_part' => 'parts', |
48 ); |
46 ); |
49 } |
47 } |
50 |
48 return $theme->get_block_template_folders(); |
51 return array( |
|
52 'wp_template' => 'templates', |
|
53 'wp_template_part' => 'parts', |
|
54 ); |
|
55 } |
49 } |
56 |
50 |
57 /** |
51 /** |
58 * Returns a filtered list of allowed area values for template parts. |
52 * Returns a filtered list of allowed area values for template parts. |
59 * |
53 * |
60 * @since 5.9.0 |
54 * @since 5.9.0 |
61 * |
55 * |
62 * @return array The supported template part area values. |
56 * @return array[] { |
|
57 * The allowed template part area values. |
|
58 * |
|
59 * @type array ...$0 { |
|
60 * Data for the allowed template part area. |
|
61 * |
|
62 * @type string $area Template part area name. |
|
63 * @type string $label Template part area label. |
|
64 * @type string $description Template part area description. |
|
65 * @type string $icon Template part area icon. |
|
66 * @type string $area_tag Template part area tag. |
|
67 * } |
|
68 * } |
63 */ |
69 */ |
64 function get_allowed_block_template_part_areas() { |
70 function get_allowed_block_template_part_areas() { |
65 $default_area_definitions = array( |
71 $default_area_definitions = array( |
66 array( |
72 array( |
67 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, |
73 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED, |
68 'label' => __( 'General' ), |
74 'label' => _x( 'General', 'template part area' ), |
69 'description' => __( |
75 'description' => __( |
70 'General templates often perform a specific role like displaying post content, and are not tied to any particular area.' |
76 'General templates often perform a specific role like displaying post content, and are not tied to any particular area.' |
71 ), |
77 ), |
72 'icon' => 'layout', |
78 'icon' => 'layout', |
73 'area_tag' => 'div', |
79 'area_tag' => 'div', |
74 ), |
80 ), |
75 array( |
81 array( |
76 'area' => WP_TEMPLATE_PART_AREA_HEADER, |
82 'area' => WP_TEMPLATE_PART_AREA_HEADER, |
77 'label' => __( 'Header' ), |
83 'label' => _x( 'Header', 'template part area' ), |
78 'description' => __( |
84 'description' => __( |
79 'The Header template defines a page area that typically contains a title, logo, and main navigation.' |
85 'The Header template defines a page area that typically contains a title, logo, and main navigation.' |
80 ), |
86 ), |
81 'icon' => 'header', |
87 'icon' => 'header', |
82 'area_tag' => 'header', |
88 'area_tag' => 'header', |
83 ), |
89 ), |
84 array( |
90 array( |
85 'area' => WP_TEMPLATE_PART_AREA_FOOTER, |
91 'area' => WP_TEMPLATE_PART_AREA_FOOTER, |
86 'label' => __( 'Footer' ), |
92 'label' => _x( 'Footer', 'template part area' ), |
87 'description' => __( |
93 'description' => __( |
88 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.' |
94 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.' |
89 ), |
95 ), |
90 'icon' => 'footer', |
96 'icon' => 'footer', |
91 'area_tag' => 'footer', |
97 'area_tag' => 'footer', |
107 * Returns a filtered list of default template types, containing their |
125 * Returns a filtered list of default template types, containing their |
108 * localized titles and descriptions. |
126 * localized titles and descriptions. |
109 * |
127 * |
110 * @since 5.9.0 |
128 * @since 5.9.0 |
111 * |
129 * |
112 * @return array The default template types. |
130 * @return array[] { |
|
131 * The default template types. |
|
132 * |
|
133 * @type array ...$0 { |
|
134 * Data for the template type. |
|
135 * |
|
136 * @type string $title Template type title. |
|
137 * @type string $description Template type description. |
|
138 * } |
|
139 * } |
113 */ |
140 */ |
114 function get_default_block_template_types() { |
141 function get_default_block_template_types() { |
115 $default_template_types = array( |
142 $default_template_types = array( |
116 'index' => array( |
143 'index' => array( |
117 'title' => _x( 'Index', 'Template name' ), |
144 'title' => _x( 'Index', 'Template name' ), |
118 'description' => __( 'Displays posts.' ), |
145 'description' => __( 'Used as a fallback template for all pages when a more specific template is not defined.' ), |
119 ), |
146 ), |
120 'home' => array( |
147 'home' => array( |
121 'title' => _x( 'Home', 'Template name' ), |
148 'title' => _x( 'Blog Home', 'Template name' ), |
122 'description' => __( 'Displays posts on the homepage, or on the Posts page if a static homepage is set.' ), |
149 'description' => __( 'Displays the latest posts as either the site homepage or as the "Posts page" as defined under reading settings. If it exists, the Front Page template overrides this template when posts are shown on the homepage.' ), |
123 ), |
150 ), |
124 'front-page' => array( |
151 'front-page' => array( |
125 'title' => _x( 'Front Page', 'Template name' ), |
152 'title' => _x( 'Front Page', 'Template name' ), |
126 'description' => __( 'Displays the homepage.' ), |
153 'description' => __( 'Displays your site\'s homepage, whether it is set to display latest posts or a static page. The Front Page template takes precedence over all templates.' ), |
127 ), |
154 ), |
128 'singular' => array( |
155 'singular' => array( |
129 'title' => _x( 'Singular', 'Template name' ), |
156 'title' => _x( 'Single Entries', 'Template name' ), |
130 'description' => __( 'Displays a single post or page.' ), |
157 'description' => __( 'Displays any single entry, such as a post or a page. This template will serve as a fallback when a more specific template (e.g. Single Post, Page, or Attachment) cannot be found.' ), |
131 ), |
158 ), |
132 'single' => array( |
159 'single' => array( |
133 'title' => _x( 'Single Post', 'Template name' ), |
160 'title' => _x( 'Single Posts', 'Template name' ), |
134 'description' => __( 'Displays a single post.' ), |
161 'description' => __( 'Displays a single post on your website unless a custom template has been applied to that post or a dedicated template exists.' ), |
135 ), |
162 ), |
136 'page' => array( |
163 'page' => array( |
137 'title' => _x( 'Page', 'Template name' ), |
164 'title' => _x( 'Pages', 'Template name' ), |
138 'description' => __( 'Displays a single page.' ), |
165 'description' => __( 'Displays a static page unless a custom template has been applied to that page or a dedicated template exists.' ), |
139 ), |
166 ), |
140 'archive' => array( |
167 'archive' => array( |
141 'title' => _x( 'Archive', 'Template name' ), |
168 'title' => _x( 'All Archives', 'Template name' ), |
142 'description' => __( 'Displays post categories, tags, and other archives.' ), |
169 'description' => __( 'Displays any archive, including posts by a single author, category, tag, taxonomy, custom post type, and date. This template will serve as a fallback when more specific templates (e.g. Category or Tag) cannot be found.' ), |
143 ), |
170 ), |
144 'author' => array( |
171 'author' => array( |
145 'title' => _x( 'Author', 'Template name' ), |
172 'title' => _x( 'Author Archives', 'Template name' ), |
146 'description' => __( 'Displays latest posts written by a single author.' ), |
173 'description' => __( 'Displays a single author\'s post archive. This template will serve as a fallback when a more specific template (e.g. Author: Admin) cannot be found.' ), |
147 ), |
174 ), |
148 'category' => array( |
175 'category' => array( |
149 'title' => _x( 'Category', 'Template name' ), |
176 'title' => _x( 'Category Archives', 'Template name' ), |
150 'description' => __( 'Displays latest posts in single post category.' ), |
177 'description' => __( 'Displays a post category archive. This template will serve as a fallback when a more specific template (e.g. Category: Recipes) cannot be found.' ), |
151 ), |
178 ), |
152 'taxonomy' => array( |
179 'taxonomy' => array( |
153 'title' => _x( 'Taxonomy', 'Template name' ), |
180 'title' => _x( 'Taxonomy', 'Template name' ), |
154 'description' => __( 'Displays latest posts from a single post taxonomy.' ), |
181 'description' => __( 'Displays a custom taxonomy archive. Like categories and tags, taxonomies have terms which you use to classify things. For example: a taxonomy named "Art" can have multiple terms, such as "Modern" and "18th Century." This template will serve as a fallback when a more specific template (e.g. Taxonomy: Art) cannot be found.' ), |
155 ), |
182 ), |
156 'date' => array( |
183 'date' => array( |
157 'title' => _x( 'Date', 'Template name' ), |
184 'title' => _x( 'Date Archives', 'Template name' ), |
158 'description' => __( 'Displays posts from a specific date.' ), |
185 'description' => __( 'Displays a post archive when a specific date is visited (e.g., example.com/2023/).' ), |
159 ), |
186 ), |
160 'tag' => array( |
187 'tag' => array( |
161 'title' => _x( 'Tag', 'Template name' ), |
188 'title' => _x( 'Tag Archives', 'Template name' ), |
162 'description' => __( 'Displays latest posts with a single post tag.' ), |
189 'description' => __( 'Displays a post tag archive. This template will serve as a fallback when a more specific template (e.g. Tag: Pizza) cannot be found.' ), |
163 ), |
190 ), |
164 'attachment' => array( |
191 'attachment' => array( |
165 'title' => __( 'Media' ), |
192 'title' => __( 'Attachment Pages' ), |
166 'description' => __( 'Displays individual media items or attachments.' ), |
193 'description' => __( 'Displays when a visitor views the dedicated page that exists for any media attachment.' ), |
167 ), |
194 ), |
168 'search' => array( |
195 'search' => array( |
169 'title' => _x( 'Search', 'Template name' ), |
196 'title' => _x( 'Search Results', 'Template name' ), |
170 'description' => __( 'Displays search results.' ), |
197 'description' => __( 'Displays when a visitor performs a search on your website.' ), |
171 ), |
198 ), |
172 'privacy-policy' => array( |
199 'privacy-policy' => array( |
173 'title' => __( 'Privacy Policy' ), |
200 'title' => __( 'Privacy Policy' ), |
174 'description' => __( 'Displays the privacy policy page.' ), |
201 'description' => __( 'Displays your site\'s Privacy Policy page.' ), |
175 ), |
202 ), |
176 '404' => array( |
203 '404' => array( |
177 'title' => _x( '404', 'Template name' ), |
204 'title' => _x( 'Page: 404', 'Template name' ), |
178 'description' => __( 'Displays when no content is found.' ), |
205 'description' => __( 'Displays when a visitor views a non-existent page, such as a dead link or a mistyped URL.' ), |
179 ), |
206 ), |
180 ); |
207 ); |
181 |
208 |
182 /** |
209 /** |
183 * Filters the list of template types. |
210 * Filters the list of default template types. |
184 * |
211 * |
185 * @since 5.9.0 |
212 * @since 5.9.0 |
186 * |
213 * |
187 * @param array $default_template_types An array of template types, formatted as [ slug => [ title, description ] ]. |
214 * @param array[] $default_template_types { |
|
215 * The default template types. |
|
216 * |
|
217 * @type array ...$0 { |
|
218 * Data for the template type. |
|
219 * |
|
220 * @type string $title Template type title. |
|
221 * @type string $description Template type description. |
|
222 * } |
|
223 * } |
188 */ |
224 */ |
189 return apply_filters( 'default_template_types', $default_template_types ); |
225 return apply_filters( 'default_template_types', $default_template_types ); |
190 } |
226 } |
191 |
227 |
192 /** |
228 /** |
215 /* translators: %1$s: Template area type, %2$s: the uncategorized template area value. */ |
250 /* translators: %1$s: Template area type, %2$s: the uncategorized template area value. */ |
216 __( '"%1$s" is not a supported wp_template_part area value and has been added as "%2$s".' ), |
251 __( '"%1$s" is not a supported wp_template_part area value and has been added as "%2$s".' ), |
217 $type, |
252 $type, |
218 WP_TEMPLATE_PART_AREA_UNCATEGORIZED |
253 WP_TEMPLATE_PART_AREA_UNCATEGORIZED |
219 ); |
254 ); |
220 trigger_error( $warning_message, E_USER_NOTICE ); |
255 wp_trigger_error( __FUNCTION__, $warning_message ); |
221 return WP_TEMPLATE_PART_AREA_UNCATEGORIZED; |
256 return WP_TEMPLATE_PART_AREA_UNCATEGORIZED; |
222 } |
257 } |
223 |
258 |
224 /** |
259 /** |
225 * Finds all nested template part file paths in a theme's directory. |
260 * Finds all nested template part file paths in a theme's directory. |
226 * |
261 * |
227 * @since 5.9.0 |
262 * @since 5.9.0 |
228 * @access private |
263 * @access private |
229 * |
264 * |
230 * @param string $base_directory The theme's file path. |
265 * @param string $base_directory The theme's file path. |
231 * @return array A list of paths to all template part files. |
266 * @return string[] A list of paths to all template part files. |
232 */ |
267 */ |
233 function _get_block_templates_paths( $base_directory ) { |
268 function _get_block_templates_paths( $base_directory ) { |
|
269 static $template_path_list = array(); |
|
270 if ( isset( $template_path_list[ $base_directory ] ) ) { |
|
271 return $template_path_list[ $base_directory ]; |
|
272 } |
234 $path_list = array(); |
273 $path_list = array(); |
235 if ( file_exists( $base_directory ) ) { |
274 if ( is_dir( $base_directory ) ) { |
236 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); |
275 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); |
237 $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH ); |
276 $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH ); |
238 foreach ( $nested_html_files as $path => $file ) { |
277 foreach ( $nested_html_files as $path => $file ) { |
239 $path_list[] = $path; |
278 $path_list[] = $path; |
240 } |
279 } |
241 } |
280 } |
|
281 $template_path_list[ $base_directory ] = $path_list; |
242 return $path_list; |
282 return $path_list; |
243 } |
283 } |
244 |
284 |
245 /** |
285 /** |
246 * Retrieves the template file from the theme for a given slug. |
286 * Retrieves the template file from the theme for a given slug. |
247 * |
287 * |
248 * @since 5.9.0 |
288 * @since 5.9.0 |
249 * @access private |
289 * @access private |
250 * |
290 * |
251 * @param string $template_type 'wp_template' or 'wp_template_part'. |
291 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
252 * @param string $slug Template slug. |
292 * @param string $slug Template slug. |
253 * |
293 * @return array|null { |
254 * @return array|null Template. |
294 * Array with template metadata if $template_type is one of 'wp_template' or 'wp_template_part', |
|
295 * null otherwise. |
|
296 * |
|
297 * @type string $slug Template slug. |
|
298 * @type string $path Template file path. |
|
299 * @type string $theme Theme slug. |
|
300 * @type string $type Template type. |
|
301 * @type string $area Template area. Only for 'wp_template_part'. |
|
302 * @type string $title Optional. Template title. |
|
303 * @type string[] $postTypes Optional. List of post types that the template supports. Only for 'wp_template'. |
|
304 * } |
255 */ |
305 */ |
256 function _get_block_template_file( $template_type, $slug ) { |
306 function _get_block_template_file( $template_type, $slug ) { |
257 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { |
307 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) { |
258 return null; |
308 return null; |
259 } |
309 } |
420 |
528 |
421 return $all_blocks; |
529 return $all_blocks; |
422 } |
530 } |
423 |
531 |
424 /** |
532 /** |
425 * Parses wp_template content and injects the active theme's |
533 * Injects the active theme's stylesheet as a `theme` attribute |
426 * stylesheet as a theme attribute into each wp_template_part |
534 * into a given template part block. |
|
535 * |
|
536 * @since 6.4.0 |
|
537 * @access private |
|
538 * |
|
539 * @param array $block a parsed block. |
|
540 */ |
|
541 function _inject_theme_attribute_in_template_part_block( &$block ) { |
|
542 if ( |
|
543 'core/template-part' === $block['blockName'] && |
|
544 ! isset( $block['attrs']['theme'] ) |
|
545 ) { |
|
546 $block['attrs']['theme'] = get_stylesheet(); |
|
547 } |
|
548 } |
|
549 |
|
550 /** |
|
551 * Removes the `theme` attribute from a given template part block. |
|
552 * |
|
553 * @since 6.4.0 |
|
554 * @access private |
|
555 * |
|
556 * @param array $block a parsed block. |
|
557 */ |
|
558 function _remove_theme_attribute_from_template_part_block( &$block ) { |
|
559 if ( |
|
560 'core/template-part' === $block['blockName'] && |
|
561 isset( $block['attrs']['theme'] ) |
|
562 ) { |
|
563 unset( $block['attrs']['theme'] ); |
|
564 } |
|
565 } |
|
566 |
|
567 /** |
|
568 * Builds a unified template object based on a theme file. |
427 * |
569 * |
428 * @since 5.9.0 |
570 * @since 5.9.0 |
|
571 * @since 6.3.0 Added `modified` property to template objects. |
429 * @access private |
572 * @access private |
430 * |
573 * |
431 * @param string $template_content serialized wp_template content. |
|
432 * |
|
433 * @return string Updated 'wp_template' content. |
|
434 */ |
|
435 function _inject_theme_attribute_in_block_template_content( $template_content ) { |
|
436 $has_updated_content = false; |
|
437 $new_content = ''; |
|
438 $template_blocks = parse_blocks( $template_content ); |
|
439 |
|
440 $blocks = _flatten_blocks( $template_blocks ); |
|
441 foreach ( $blocks as &$block ) { |
|
442 if ( |
|
443 'core/template-part' === $block['blockName'] && |
|
444 ! isset( $block['attrs']['theme'] ) |
|
445 ) { |
|
446 $block['attrs']['theme'] = wp_get_theme()->get_stylesheet(); |
|
447 $has_updated_content = true; |
|
448 } |
|
449 } |
|
450 |
|
451 if ( $has_updated_content ) { |
|
452 foreach ( $template_blocks as &$block ) { |
|
453 $new_content .= serialize_block( $block ); |
|
454 } |
|
455 |
|
456 return $new_content; |
|
457 } |
|
458 |
|
459 return $template_content; |
|
460 } |
|
461 |
|
462 /** |
|
463 * Parses a block template and removes the theme attribute from each template part. |
|
464 * |
|
465 * @since 5.9.0 |
|
466 * @access private |
|
467 * |
|
468 * @param string $template_content Serialized block template content. |
|
469 * @return string Updated block template content. |
|
470 */ |
|
471 function _remove_theme_attribute_in_block_template_content( $template_content ) { |
|
472 $has_updated_content = false; |
|
473 $new_content = ''; |
|
474 $template_blocks = parse_blocks( $template_content ); |
|
475 |
|
476 $blocks = _flatten_blocks( $template_blocks ); |
|
477 foreach ( $blocks as $key => $block ) { |
|
478 if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) { |
|
479 unset( $blocks[ $key ]['attrs']['theme'] ); |
|
480 $has_updated_content = true; |
|
481 } |
|
482 } |
|
483 |
|
484 if ( ! $has_updated_content ) { |
|
485 return $template_content; |
|
486 } |
|
487 |
|
488 foreach ( $template_blocks as $block ) { |
|
489 $new_content .= serialize_block( $block ); |
|
490 } |
|
491 |
|
492 return $new_content; |
|
493 } |
|
494 |
|
495 /** |
|
496 * Build a unified template object based on a theme file. |
|
497 * |
|
498 * @since 5.9.0 |
|
499 * @access private |
|
500 * |
|
501 * @param array $template_file Theme file. |
574 * @param array $template_file Theme file. |
502 * @param string $template_type 'wp_template' or 'wp_template_part'. |
575 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
503 * |
|
504 * @return WP_Block_Template Template. |
576 * @return WP_Block_Template Template. |
505 */ |
577 */ |
506 function _build_block_template_result_from_file( $template_file, $template_type ) { |
578 function _build_block_template_result_from_file( $template_file, $template_type ) { |
507 $default_template_types = get_default_block_template_types(); |
579 $default_template_types = get_default_block_template_types(); |
508 $template_content = file_get_contents( $template_file['path'] ); |
580 $theme = get_stylesheet(); |
509 $theme = wp_get_theme()->get_stylesheet(); |
|
510 |
581 |
511 $template = new WP_Block_Template(); |
582 $template = new WP_Block_Template(); |
512 $template->id = $theme . '//' . $template_file['slug']; |
583 $template->id = $theme . '//' . $template_file['slug']; |
513 $template->theme = $theme; |
584 $template->theme = $theme; |
514 $template->content = _inject_theme_attribute_in_block_template_content( $template_content ); |
585 $template->content = file_get_contents( $template_file['path'] ); |
515 $template->slug = $template_file['slug']; |
586 $template->slug = $template_file['slug']; |
516 $template->source = 'theme'; |
587 $template->source = 'theme'; |
517 $template->type = $template_type; |
588 $template->type = $template_type; |
518 $template->title = ! empty( $template_file['title'] ) ? $template_file['title'] : $template_file['slug']; |
589 $template->title = ! empty( $template_file['title'] ) ? $template_file['title'] : $template_file['slug']; |
519 $template->status = 'publish'; |
590 $template->status = 'publish'; |
520 $template->has_theme_file = true; |
591 $template->has_theme_file = true; |
521 $template->is_custom = true; |
592 $template->is_custom = true; |
|
593 $template->modified = null; |
522 |
594 |
523 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { |
595 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { |
524 $template->description = $default_template_types[ $template_file['slug'] ]['description']; |
596 $template->description = $default_template_types[ $template_file['slug'] ]['description']; |
525 $template->title = $default_template_types[ $template_file['slug'] ]['title']; |
597 $template->title = $default_template_types[ $template_file['slug'] ]['title']; |
526 $template->is_custom = false; |
598 $template->is_custom = false; |
532 |
604 |
533 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) { |
605 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) { |
534 $template->area = $template_file['area']; |
606 $template->area = $template_file['area']; |
535 } |
607 } |
536 |
608 |
|
609 $before_block_visitor = '_inject_theme_attribute_in_template_part_block'; |
|
610 $after_block_visitor = null; |
|
611 $hooked_blocks = get_hooked_blocks(); |
|
612 if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { |
|
613 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
|
614 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
|
615 } |
|
616 $blocks = parse_blocks( $template->content ); |
|
617 $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); |
|
618 |
537 return $template; |
619 return $template; |
538 } |
620 } |
539 |
621 |
540 /** |
622 /** |
541 * Build a unified template object based a post Object. |
623 * Builds the title and description of a post-specific template based on the underlying referenced post. |
542 * |
624 * |
543 * @since 5.9.0 |
625 * Mutates the underlying template object. |
|
626 * |
|
627 * @since 6.1.0 |
544 * @access private |
628 * @access private |
545 * |
629 * |
546 * @param WP_Post $post Template post. |
630 * @param string $post_type Post type, e.g. page, post, product. |
547 * |
631 * @param string $slug Slug of the post, e.g. a-story-about-shoes. |
548 * @return WP_Block_Template|WP_Error Template. |
632 * @param WP_Block_Template $template Template to mutate adding the description and title computed. |
549 */ |
633 * @return bool Returns true if the referenced post was found and false otherwise. |
550 function _build_block_template_result_from_post( $post ) { |
634 */ |
|
635 function _wp_build_title_and_description_for_single_post_type_block_template( $post_type, $slug, WP_Block_Template $template ) { |
|
636 $post_type_object = get_post_type_object( $post_type ); |
|
637 |
|
638 $default_args = array( |
|
639 'post_type' => $post_type, |
|
640 'post_status' => 'publish', |
|
641 'posts_per_page' => 1, |
|
642 'update_post_meta_cache' => false, |
|
643 'update_post_term_cache' => false, |
|
644 'ignore_sticky_posts' => true, |
|
645 'no_found_rows' => true, |
|
646 ); |
|
647 |
|
648 $args = array( |
|
649 'name' => $slug, |
|
650 ); |
|
651 $args = wp_parse_args( $args, $default_args ); |
|
652 |
|
653 $posts_query = new WP_Query( $args ); |
|
654 |
|
655 if ( empty( $posts_query->posts ) ) { |
|
656 $template->title = sprintf( |
|
657 /* translators: Custom template title in the Site Editor referencing a post that was not found. 1: Post type singular name, 2: Post type slug. */ |
|
658 __( 'Not found: %1$s (%2$s)' ), |
|
659 $post_type_object->labels->singular_name, |
|
660 $slug |
|
661 ); |
|
662 |
|
663 return false; |
|
664 } |
|
665 |
|
666 $post_title = $posts_query->posts[0]->post_title; |
|
667 |
|
668 $template->title = sprintf( |
|
669 /* translators: Custom template title in the Site Editor. 1: Post type singular name, 2: Post title. */ |
|
670 __( '%1$s: %2$s' ), |
|
671 $post_type_object->labels->singular_name, |
|
672 $post_title |
|
673 ); |
|
674 |
|
675 $template->description = sprintf( |
|
676 /* translators: Custom template description in the Site Editor. %s: Post title. */ |
|
677 __( 'Template for %s' ), |
|
678 $post_title |
|
679 ); |
|
680 |
|
681 $args = array( |
|
682 'title' => $post_title, |
|
683 ); |
|
684 $args = wp_parse_args( $args, $default_args ); |
|
685 |
|
686 $posts_with_same_title_query = new WP_Query( $args ); |
|
687 |
|
688 if ( count( $posts_with_same_title_query->posts ) > 1 ) { |
|
689 $template->title = sprintf( |
|
690 /* translators: Custom template title in the Site Editor. 1: Template title, 2: Post type slug. */ |
|
691 __( '%1$s (%2$s)' ), |
|
692 $template->title, |
|
693 $slug |
|
694 ); |
|
695 } |
|
696 |
|
697 return true; |
|
698 } |
|
699 |
|
700 /** |
|
701 * Builds the title and description of a taxonomy-specific template based on the underlying entity referenced. |
|
702 * |
|
703 * Mutates the underlying template object. |
|
704 * |
|
705 * @since 6.1.0 |
|
706 * @access private |
|
707 * |
|
708 * @param string $taxonomy Identifier of the taxonomy, e.g. category. |
|
709 * @param string $slug Slug of the term, e.g. shoes. |
|
710 * @param WP_Block_Template $template Template to mutate adding the description and title computed. |
|
711 * @return bool True if the term referenced was found and false otherwise. |
|
712 */ |
|
713 function _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, $slug, WP_Block_Template $template ) { |
|
714 $taxonomy_object = get_taxonomy( $taxonomy ); |
|
715 |
|
716 $default_args = array( |
|
717 'taxonomy' => $taxonomy, |
|
718 'hide_empty' => false, |
|
719 'update_term_meta_cache' => false, |
|
720 ); |
|
721 |
|
722 $term_query = new WP_Term_Query(); |
|
723 |
|
724 $args = array( |
|
725 'number' => 1, |
|
726 'slug' => $slug, |
|
727 ); |
|
728 $args = wp_parse_args( $args, $default_args ); |
|
729 |
|
730 $terms_query = $term_query->query( $args ); |
|
731 |
|
732 if ( empty( $terms_query ) ) { |
|
733 $template->title = sprintf( |
|
734 /* translators: Custom template title in the Site Editor, referencing a taxonomy term that was not found. 1: Taxonomy singular name, 2: Term slug. */ |
|
735 __( 'Not found: %1$s (%2$s)' ), |
|
736 $taxonomy_object->labels->singular_name, |
|
737 $slug |
|
738 ); |
|
739 return false; |
|
740 } |
|
741 |
|
742 $term_title = $terms_query[0]->name; |
|
743 |
|
744 $template->title = sprintf( |
|
745 /* translators: Custom template title in the Site Editor. 1: Taxonomy singular name, 2: Term title. */ |
|
746 __( '%1$s: %2$s' ), |
|
747 $taxonomy_object->labels->singular_name, |
|
748 $term_title |
|
749 ); |
|
750 |
|
751 $template->description = sprintf( |
|
752 /* translators: Custom template description in the Site Editor. %s: Term title. */ |
|
753 __( 'Template for %s' ), |
|
754 $term_title |
|
755 ); |
|
756 |
|
757 $term_query = new WP_Term_Query(); |
|
758 |
|
759 $args = array( |
|
760 'number' => 2, |
|
761 'name' => $term_title, |
|
762 ); |
|
763 $args = wp_parse_args( $args, $default_args ); |
|
764 |
|
765 $terms_with_same_title_query = $term_query->query( $args ); |
|
766 |
|
767 if ( count( $terms_with_same_title_query ) > 1 ) { |
|
768 $template->title = sprintf( |
|
769 /* translators: Custom template title in the Site Editor. 1: Template title, 2: Term slug. */ |
|
770 __( '%1$s (%2$s)' ), |
|
771 $template->title, |
|
772 $slug |
|
773 ); |
|
774 } |
|
775 |
|
776 return true; |
|
777 } |
|
778 |
|
779 /** |
|
780 * Builds a block template object from a post object. |
|
781 * |
|
782 * This is a helper function that creates a block template object from a given post object. |
|
783 * It is self-sufficient in that it only uses information passed as arguments; it does not |
|
784 * query the database for additional information. |
|
785 * |
|
786 * @since 6.5.3 |
|
787 * @access private |
|
788 * |
|
789 * @param WP_Post $post Template post. |
|
790 * @param array $terms Additional terms to inform the template object. |
|
791 * @param array $meta Additional meta fields to inform the template object. |
|
792 * @return WP_Block_Template|WP_Error Template or error object. |
|
793 */ |
|
794 function _build_block_template_object_from_post_object( $post, $terms = array(), $meta = array() ) { |
|
795 if ( empty( $terms['wp_theme'] ) ) { |
|
796 return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) ); |
|
797 } |
|
798 $theme = $terms['wp_theme']; |
|
799 |
551 $default_template_types = get_default_block_template_types(); |
800 $default_template_types = get_default_block_template_types(); |
552 $terms = get_the_terms( $post, 'wp_theme' ); |
801 |
553 |
802 $template_file = _get_block_template_file( $post->post_type, $post->post_name ); |
554 if ( is_wp_error( $terms ) ) { |
803 $has_theme_file = get_stylesheet() === $theme && null !== $template_file; |
555 return $terms; |
|
556 } |
|
557 |
|
558 if ( ! $terms ) { |
|
559 return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) ); |
|
560 } |
|
561 |
|
562 $theme = $terms[0]->name; |
|
563 $has_theme_file = wp_get_theme()->get_stylesheet() === $theme && |
|
564 null !== _get_block_template_file( $post->post_type, $post->post_name ); |
|
565 |
|
566 $origin = get_post_meta( $post->ID, 'origin', true ); |
|
567 |
804 |
568 $template = new WP_Block_Template(); |
805 $template = new WP_Block_Template(); |
569 $template->wp_id = $post->ID; |
806 $template->wp_id = $post->ID; |
570 $template->id = $theme . '//' . $post->post_name; |
807 $template->id = $theme . '//' . $post->post_name; |
571 $template->theme = $theme; |
808 $template->theme = $theme; |
572 $template->content = $post->post_content; |
809 $template->content = $post->post_content; |
573 $template->slug = $post->post_name; |
810 $template->slug = $post->post_name; |
574 $template->source = 'custom'; |
811 $template->source = 'custom'; |
575 $template->origin = ! empty( $origin ) ? $origin : null; |
812 $template->origin = ! empty( $meta['origin'] ) ? $meta['origin'] : null; |
576 $template->type = $post->post_type; |
813 $template->type = $post->post_type; |
577 $template->description = $post->post_excerpt; |
814 $template->description = $post->post_excerpt; |
578 $template->title = $post->post_title; |
815 $template->title = $post->post_title; |
579 $template->status = $post->post_status; |
816 $template->status = $post->post_status; |
580 $template->has_theme_file = $has_theme_file; |
817 $template->has_theme_file = $has_theme_file; |
581 $template->is_custom = true; |
818 $template->is_custom = empty( $meta['is_wp_suggestion'] ); |
582 $template->author = $post->post_author; |
819 $template->author = $post->post_author; |
|
820 $template->modified = $post->post_modified; |
|
821 |
|
822 if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) { |
|
823 $template->post_types = $template_file['postTypes']; |
|
824 } |
583 |
825 |
584 if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { |
826 if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) { |
585 $template->is_custom = false; |
827 $template->is_custom = false; |
586 } |
828 } |
587 |
829 |
588 if ( 'wp_template_part' === $post->post_type ) { |
830 if ( 'wp_template_part' === $post->post_type && isset( $terms['wp_template_part_area'] ) ) { |
589 $type_terms = get_the_terms( $post, 'wp_template_part_area' ); |
831 $template->area = $terms['wp_template_part_area']; |
|
832 } |
|
833 |
|
834 return $template; |
|
835 } |
|
836 |
|
837 /** |
|
838 * Builds a unified template object based a post Object. |
|
839 * |
|
840 * @since 5.9.0 |
|
841 * @since 6.3.0 Added `modified` property to template objects. |
|
842 * @since 6.4.0 Added support for a revision post to be passed to this function. |
|
843 * @access private |
|
844 * |
|
845 * @param WP_Post $post Template post. |
|
846 * @return WP_Block_Template|WP_Error Template or error object. |
|
847 */ |
|
848 function _build_block_template_result_from_post( $post ) { |
|
849 $post_id = wp_is_post_revision( $post ); |
|
850 if ( ! $post_id ) { |
|
851 $post_id = $post; |
|
852 } |
|
853 $parent_post = get_post( $post_id ); |
|
854 $post->post_name = $parent_post->post_name; |
|
855 $post->post_type = $parent_post->post_type; |
|
856 |
|
857 $terms = get_the_terms( $parent_post, 'wp_theme' ); |
|
858 |
|
859 if ( is_wp_error( $terms ) ) { |
|
860 return $terms; |
|
861 } |
|
862 |
|
863 if ( ! $terms ) { |
|
864 return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) ); |
|
865 } |
|
866 |
|
867 $terms = array( |
|
868 'wp_theme' => $terms[0]->name, |
|
869 ); |
|
870 |
|
871 if ( 'wp_template_part' === $parent_post->post_type ) { |
|
872 $type_terms = get_the_terms( $parent_post, 'wp_template_part_area' ); |
590 if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { |
873 if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { |
591 $template->area = $type_terms[0]->name; |
874 $terms['wp_template_part_area'] = $type_terms[0]->name; |
592 } |
875 } |
|
876 } |
|
877 |
|
878 $meta = array( |
|
879 'origin' => get_post_meta( $parent_post->ID, 'origin', true ), |
|
880 'is_wp_suggestion' => get_post_meta( $parent_post->ID, 'is_wp_suggestion', true ), |
|
881 ); |
|
882 |
|
883 $template = _build_block_template_object_from_post_object( $post, $terms, $meta ); |
|
884 |
|
885 if ( is_wp_error( $template ) ) { |
|
886 return $template; |
|
887 } |
|
888 |
|
889 // Check for a block template without a description and title or with a title equal to the slug. |
|
890 if ( 'wp_template' === $parent_post->post_type && empty( $template->description ) && ( empty( $template->title ) || $template->title === $template->slug ) ) { |
|
891 $matches = array(); |
|
892 |
|
893 // Check for a block template for a single author, page, post, tag, category, custom post type, or custom taxonomy. |
|
894 if ( preg_match( '/(author|page|single|tag|category|taxonomy)-(.+)/', $template->slug, $matches ) ) { |
|
895 $type = $matches[1]; |
|
896 $slug_remaining = $matches[2]; |
|
897 |
|
898 switch ( $type ) { |
|
899 case 'author': |
|
900 $nice_name = $slug_remaining; |
|
901 $users = get_users( |
|
902 array( |
|
903 'capability' => 'edit_posts', |
|
904 'search' => $nice_name, |
|
905 'search_columns' => array( 'user_nicename' ), |
|
906 'fields' => 'display_name', |
|
907 ) |
|
908 ); |
|
909 |
|
910 if ( empty( $users ) ) { |
|
911 $template->title = sprintf( |
|
912 /* translators: Custom template title in the Site Editor, referencing a deleted author. %s: Author nicename. */ |
|
913 __( 'Deleted author: %s' ), |
|
914 $nice_name |
|
915 ); |
|
916 } else { |
|
917 $author_name = $users[0]; |
|
918 |
|
919 $template->title = sprintf( |
|
920 /* translators: Custom template title in the Site Editor. %s: Author name. */ |
|
921 __( 'Author: %s' ), |
|
922 $author_name |
|
923 ); |
|
924 |
|
925 $template->description = sprintf( |
|
926 /* translators: Custom template description in the Site Editor. %s: Author name. */ |
|
927 __( 'Template for %s' ), |
|
928 $author_name |
|
929 ); |
|
930 |
|
931 $users_with_same_name = get_users( |
|
932 array( |
|
933 'capability' => 'edit_posts', |
|
934 'search' => $author_name, |
|
935 'search_columns' => array( 'display_name' ), |
|
936 'fields' => 'display_name', |
|
937 ) |
|
938 ); |
|
939 |
|
940 if ( count( $users_with_same_name ) > 1 ) { |
|
941 $template->title = sprintf( |
|
942 /* translators: Custom template title in the Site Editor. 1: Template title of an author template, 2: Author nicename. */ |
|
943 __( '%1$s (%2$s)' ), |
|
944 $template->title, |
|
945 $nice_name |
|
946 ); |
|
947 } |
|
948 } |
|
949 break; |
|
950 case 'page': |
|
951 _wp_build_title_and_description_for_single_post_type_block_template( 'page', $slug_remaining, $template ); |
|
952 break; |
|
953 case 'single': |
|
954 $post_types = get_post_types(); |
|
955 |
|
956 foreach ( $post_types as $post_type ) { |
|
957 $post_type_length = strlen( $post_type ) + 1; |
|
958 |
|
959 // If $slug_remaining starts with $post_type followed by a hyphen. |
|
960 if ( 0 === strncmp( $slug_remaining, $post_type . '-', $post_type_length ) ) { |
|
961 $slug = substr( $slug_remaining, $post_type_length, strlen( $slug_remaining ) ); |
|
962 $found = _wp_build_title_and_description_for_single_post_type_block_template( $post_type, $slug, $template ); |
|
963 |
|
964 if ( $found ) { |
|
965 break; |
|
966 } |
|
967 } |
|
968 } |
|
969 break; |
|
970 case 'tag': |
|
971 _wp_build_title_and_description_for_taxonomy_block_template( 'post_tag', $slug_remaining, $template ); |
|
972 break; |
|
973 case 'category': |
|
974 _wp_build_title_and_description_for_taxonomy_block_template( 'category', $slug_remaining, $template ); |
|
975 break; |
|
976 case 'taxonomy': |
|
977 $taxonomies = get_taxonomies(); |
|
978 |
|
979 foreach ( $taxonomies as $taxonomy ) { |
|
980 $taxonomy_length = strlen( $taxonomy ) + 1; |
|
981 |
|
982 // If $slug_remaining starts with $taxonomy followed by a hyphen. |
|
983 if ( 0 === strncmp( $slug_remaining, $taxonomy . '-', $taxonomy_length ) ) { |
|
984 $slug = substr( $slug_remaining, $taxonomy_length, strlen( $slug_remaining ) ); |
|
985 $found = _wp_build_title_and_description_for_taxonomy_block_template( $taxonomy, $slug, $template ); |
|
986 |
|
987 if ( $found ) { |
|
988 break; |
|
989 } |
|
990 } |
|
991 } |
|
992 break; |
|
993 } |
|
994 } |
|
995 } |
|
996 |
|
997 $hooked_blocks = get_hooked_blocks(); |
|
998 if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { |
|
999 $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
|
1000 $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' ); |
|
1001 $blocks = parse_blocks( $template->content ); |
|
1002 $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); |
593 } |
1003 } |
594 |
1004 |
595 return $template; |
1005 return $template; |
596 } |
1006 } |
597 |
1007 |
601 * @since 5.8.0 |
1011 * @since 5.8.0 |
602 * |
1012 * |
603 * @param array $query { |
1013 * @param array $query { |
604 * Optional. Arguments to retrieve templates. |
1014 * Optional. Arguments to retrieve templates. |
605 * |
1015 * |
606 * @type array $slug__in List of slugs to include. |
1016 * @type string[] $slug__in List of slugs to include. |
607 * @type int $wp_id Post ID of customized template. |
1017 * @type int $wp_id Post ID of customized template. |
608 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for wp_template_part template type only). |
1018 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only). |
609 * @type string $post_type Post type to get the templates for. |
1019 * @type string $post_type Post type to get the templates for. |
610 * } |
1020 * } |
611 * @param string $template_type 'wp_template' or 'wp_template_part'. |
1021 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
612 * |
1022 * @return WP_Block_Template[] Array of block templates. |
613 * @return array Templates. |
|
614 */ |
1023 */ |
615 function get_block_templates( $query = array(), $template_type = 'wp_template' ) { |
1024 function get_block_templates( $query = array(), $template_type = 'wp_template' ) { |
616 /** |
1025 /** |
617 * Filters the block templates array before the query takes place. |
1026 * Filters the block templates array before the query takes place. |
618 * |
1027 * |
619 * Return a non-null value to bypass the WordPress queries. |
1028 * Return a non-null value to bypass the WordPress queries. |
620 * |
1029 * |
621 * @since 5.9.0 |
1030 * @since 5.9.0 |
622 * |
1031 * |
623 * @param WP_Block_Template[]|null $block_templates Return an array of block templates to short-circuit the default query, |
1032 * @param WP_Block_Template[]|null $block_templates Return an array of block templates to short-circuit the default query, |
624 * or null to allow WP to run it's normal queries. |
1033 * or null to allow WP to run its normal queries. |
625 * @param array $query { |
1034 * @param array $query { |
626 * Optional. Arguments to retrieve templates. |
1035 * Arguments to retrieve templates. All arguments are optional. |
627 * |
1036 * |
628 * @type array $slug__in List of slugs to include. |
1037 * @type string[] $slug__in List of slugs to include. |
629 * @type int $wp_id Post ID of customized template. |
1038 * @type int $wp_id Post ID of customized template. |
630 * @type string $post_type Post type to get the templates for. |
1039 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only). |
|
1040 * @type string $post_type Post type to get the templates for. |
631 * } |
1041 * } |
632 * @param string $template_type wp_template or wp_template_part. |
1042 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
633 */ |
1043 */ |
634 $templates = apply_filters( 'pre_get_block_templates', null, $query, $template_type ); |
1044 $templates = apply_filters( 'pre_get_block_templates', null, $query, $template_type ); |
635 if ( ! is_null( $templates ) ) { |
1045 if ( ! is_null( $templates ) ) { |
636 return $templates; |
1046 return $templates; |
637 } |
1047 } |
638 |
1048 |
639 $post_type = isset( $query['post_type'] ) ? $query['post_type'] : ''; |
1049 $post_type = isset( $query['post_type'] ) ? $query['post_type'] : ''; |
640 $wp_query_args = array( |
1050 $wp_query_args = array( |
641 'post_status' => array( 'auto-draft', 'draft', 'publish' ), |
1051 'post_status' => array( 'auto-draft', 'draft', 'publish' ), |
642 'post_type' => $template_type, |
1052 'post_type' => $template_type, |
643 'posts_per_page' => -1, |
1053 'posts_per_page' => -1, |
644 'no_found_rows' => true, |
1054 'no_found_rows' => true, |
645 'tax_query' => array( |
1055 'lazy_load_term_meta' => false, |
|
1056 'tax_query' => array( |
646 array( |
1057 array( |
647 'taxonomy' => 'wp_theme', |
1058 'taxonomy' => 'wp_theme', |
648 'field' => 'name', |
1059 'field' => 'name', |
649 'terms' => wp_get_theme()->get_stylesheet(), |
1060 'terms' => get_stylesheet(), |
650 ), |
1061 ), |
651 ), |
1062 ), |
652 ); |
1063 ); |
653 |
1064 |
654 if ( 'wp_template_part' === $template_type && isset( $query['area'] ) ) { |
1065 if ( 'wp_template_part' === $template_type && isset( $query['area'] ) ) { |
682 |
1094 |
683 if ( $post_type && ! $template->is_custom ) { |
1095 if ( $post_type && ! $template->is_custom ) { |
684 continue; |
1096 continue; |
685 } |
1097 } |
686 |
1098 |
|
1099 if ( |
|
1100 $post_type && |
|
1101 isset( $template->post_types ) && |
|
1102 ! in_array( $post_type, $template->post_types, true ) |
|
1103 ) { |
|
1104 continue; |
|
1105 } |
|
1106 |
687 $query_result[] = $template; |
1107 $query_result[] = $template; |
688 } |
1108 } |
689 |
1109 |
690 if ( ! isset( $query['wp_id'] ) ) { |
1110 if ( ! isset( $query['wp_id'] ) ) { |
691 $template_files = _get_block_templates_files( $template_type ); |
1111 /* |
|
1112 * If the query has found some use templates, those have priority |
|
1113 * over the theme-provided ones, so we skip querying and building them. |
|
1114 */ |
|
1115 $query['slug__not_in'] = wp_list_pluck( $query_result, 'slug' ); |
|
1116 $template_files = _get_block_templates_files( $template_type, $query ); |
692 foreach ( $template_files as $template_file ) { |
1117 foreach ( $template_files as $template_file ) { |
693 $template = _build_block_template_result_from_file( $template_file, $template_type ); |
1118 $query_result[] = _build_block_template_result_from_file( $template_file, $template_type ); |
694 |
|
695 if ( $post_type && ! $template->is_custom ) { |
|
696 continue; |
|
697 } |
|
698 |
|
699 if ( $post_type && |
|
700 isset( $template->post_types ) && |
|
701 ! in_array( $post_type, $template->post_types, true ) |
|
702 ) { |
|
703 continue; |
|
704 } |
|
705 |
|
706 $is_not_custom = false === array_search( |
|
707 wp_get_theme()->get_stylesheet() . '//' . $template_file['slug'], |
|
708 wp_list_pluck( $query_result, 'id' ), |
|
709 true |
|
710 ); |
|
711 $fits_slug_query = |
|
712 ! isset( $query['slug__in'] ) || in_array( $template_file['slug'], $query['slug__in'], true ); |
|
713 $fits_area_query = |
|
714 ! isset( $query['area'] ) || $template_file['area'] === $query['area']; |
|
715 $should_include = $is_not_custom && $fits_slug_query && $fits_area_query; |
|
716 if ( $should_include ) { |
|
717 $query_result[] = $template; |
|
718 } |
|
719 } |
1119 } |
720 } |
1120 } |
721 |
1121 |
722 /** |
1122 /** |
723 * Filters the array of queried block templates array after they've been fetched. |
1123 * Filters the array of queried block templates array after they've been fetched. |
724 * |
1124 * |
725 * @since 5.9.0 |
1125 * @since 5.9.0 |
726 * |
1126 * |
727 * @param WP_Block_Template[] $query_result Array of found block templates. |
1127 * @param WP_Block_Template[] $query_result Array of found block templates. |
728 * @param array $query { |
1128 * @param array $query { |
729 * Optional. Arguments to retrieve templates. |
1129 * Arguments to retrieve templates. All arguments are optional. |
730 * |
1130 * |
731 * @type array $slug__in List of slugs to include. |
1131 * @type string[] $slug__in List of slugs to include. |
732 * @type int $wp_id Post ID of customized template. |
1132 * @type int $wp_id Post ID of customized template. |
|
1133 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only). |
|
1134 * @type string $post_type Post type to get the templates for. |
733 * } |
1135 * } |
734 * @param string $template_type wp_template or wp_template_part. |
1136 * @param string $template_type wp_template or wp_template_part. |
735 */ |
1137 */ |
736 return apply_filters( 'get_block_templates', $query_result, $query, $template_type ); |
1138 return apply_filters( 'get_block_templates', $query_result, $query, $template_type ); |
737 } |
1139 } |
738 |
1140 |
739 /** |
1141 /** |
740 * Retrieves a single unified template object using its id. |
1142 * Retrieves a single unified template object using its id. |
741 * |
1143 * |
742 * @since 5.8.0 |
1144 * @since 5.8.0 |
743 * |
1145 * |
744 * @param string $id Template unique identifier (example: theme_slug//template_slug). |
1146 * @param string $id Template unique identifier (example: 'theme_slug//template_slug'). |
745 * @param string $template_type Optional. Template type: `'wp_template'` or '`wp_template_part'`. |
1147 * @param string $template_type Optional. Template type. Either 'wp_template' or 'wp_template_part'. |
746 * Default `'wp_template'`. |
1148 * Default 'wp_template'. |
747 * |
|
748 * @return WP_Block_Template|null Template. |
1149 * @return WP_Block_Template|null Template. |
749 */ |
1150 */ |
750 function get_block_template( $id, $template_type = 'wp_template' ) { |
1151 function get_block_template( $id, $template_type = 'wp_template' ) { |
751 /** |
1152 /** |
752 *Filters the block template object before the query takes place. |
1153 * Filters the block template object before the query takes place. |
753 * |
1154 * |
754 * Return a non-null value to bypass the WordPress queries. |
1155 * Return a non-null value to bypass the WordPress queries. |
755 * |
1156 * |
756 * @since 5.9.0 |
1157 * @since 5.9.0 |
757 * |
1158 * |
758 * @param WP_Block_Template|null $block_template Return block template object to short-circuit the default query, |
1159 * @param WP_Block_Template|null $block_template Return block template object to short-circuit the default query, |
759 * or null to allow WP to run its normal queries. |
1160 * or null to allow WP to run its normal queries. |
760 * @param string $id Template unique identifier (example: theme_slug//template_slug). |
1161 * @param string $id Template unique identifier (example: 'theme_slug//template_slug'). |
761 * @param string $template_type Template type: `'wp_template'` or '`wp_template_part'`. |
1162 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
762 */ |
1163 */ |
763 $block_template = apply_filters( 'pre_get_block_template', null, $id, $template_type ); |
1164 $block_template = apply_filters( 'pre_get_block_template', null, $id, $template_type ); |
764 if ( ! is_null( $block_template ) ) { |
1165 if ( ! is_null( $block_template ) ) { |
765 return $block_template; |
1166 return $block_template; |
766 } |
1167 } |
856 } |
1259 } |
857 |
1260 |
858 $block_template = _build_block_template_result_from_file( $template_file, $template_type ); |
1261 $block_template = _build_block_template_result_from_file( $template_file, $template_type ); |
859 |
1262 |
860 /** |
1263 /** |
861 * Filters the array of queried block templates array after they've been fetched. |
1264 * Filters the block template object after it has been (potentially) fetched from the theme file. |
862 * |
1265 * |
863 * @since 5.9.0 |
1266 * @since 5.9.0 |
864 * |
1267 * |
865 * @param WP_Block_Template|null $block_template The found block template, or null if there is none. |
1268 * @param WP_Block_Template|null $block_template The found block template, or null if there is none. |
866 * @param string $id Template unique identifier (example: theme_slug//template_slug). |
1269 * @param string $id Template unique identifier (example: 'theme_slug//template_slug'). |
867 * @param string $template_type Template type: `'wp_template'` or '`wp_template_part'`. |
1270 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'. |
868 */ |
1271 */ |
869 return apply_filters( 'get_block_file_template', $block_template, $id, $template_type ); |
1272 return apply_filters( 'get_block_file_template', $block_template, $id, $template_type ); |
870 } |
1273 } |
871 |
1274 |
872 /** |
1275 /** |
873 * Print a template-part. |
1276 * Prints a block template part. |
874 * |
1277 * |
875 * @since 5.9.0 |
1278 * @since 5.9.0 |
876 * |
1279 * |
877 * @param string $part The template-part to print. Use "header" or "footer". |
1280 * @param string $part The block template part to print, for example 'header' or 'footer'. |
878 */ |
1281 */ |
879 function block_template_part( $part ) { |
1282 function block_template_part( $part ) { |
880 $template_part = get_block_template( get_stylesheet() . '//' . $part, 'wp_template_part' ); |
1283 $template_part = get_block_template( get_stylesheet() . '//' . $part, 'wp_template_part' ); |
881 if ( ! $template_part || empty( $template_part->content ) ) { |
1284 if ( ! $template_part || empty( $template_part->content ) ) { |
882 return; |
1285 return; |
883 } |
1286 } |
884 echo do_blocks( $template_part->content ); |
1287 echo do_blocks( $template_part->content ); |
885 } |
1288 } |
886 |
1289 |
887 /** |
1290 /** |
888 * Print the header template-part. |
1291 * Prints the header block template part. |
889 * |
1292 * |
890 * @since 5.9.0 |
1293 * @since 5.9.0 |
891 */ |
1294 */ |
892 function block_header_area() { |
1295 function block_header_area() { |
893 block_template_part( 'header' ); |
1296 block_template_part( 'header' ); |
894 } |
1297 } |
895 |
1298 |
896 /** |
1299 /** |
897 * Print the footer template-part. |
1300 * Prints the footer block template part. |
898 * |
1301 * |
899 * @since 5.9.0 |
1302 * @since 5.9.0 |
900 */ |
1303 */ |
901 function block_footer_area() { |
1304 function block_footer_area() { |
902 block_template_part( 'footer' ); |
1305 block_template_part( 'footer' ); |
903 } |
1306 } |
904 |
1307 |
905 /** |
1308 /** |
906 * Filters theme directories that should be ignored during export. |
1309 * Determines whether a theme directory should be ignored during export. |
907 * |
1310 * |
908 * @since 6.0.0 |
1311 * @since 6.0.0 |
909 * |
1312 * |
910 * @param string $path The path of the file in the theme. |
1313 * @param string $path The path of the file in the theme. |
911 * @return Bool Whether this file is in an ignored directory. |
1314 * @return bool Whether this file is in an ignored directory. |
912 */ |
1315 */ |
913 function wp_is_theme_directory_ignored( $path ) { |
1316 function wp_is_theme_directory_ignored( $path ) { |
914 $directories_to_ignore = array( '.svn', '.git', '.hg', '.bzr', 'node_modules', 'vendor' ); |
1317 $directories_to_ignore = array( '.DS_Store', '.svn', '.git', '.hg', '.bzr', 'node_modules', 'vendor' ); |
|
1318 |
915 foreach ( $directories_to_ignore as $directory ) { |
1319 foreach ( $directories_to_ignore as $directory ) { |
916 if ( strpos( $path, $directory ) === 0 ) { |
1320 if ( str_starts_with( $path, $directory ) ) { |
917 return true; |
1321 return true; |
918 } |
1322 } |
919 } |
1323 } |
920 |
1324 |
921 return false; |
1325 return false; |
1020 // Save changes to the zip file. |
1430 // Save changes to the zip file. |
1021 $zip->close(); |
1431 $zip->close(); |
1022 |
1432 |
1023 return $filename; |
1433 return $filename; |
1024 } |
1434 } |
|
1435 |
|
1436 /** |
|
1437 * Gets the template hierarchy for the given template slug to be created. |
|
1438 * |
|
1439 * Note: Always add `index` as the last fallback template. |
|
1440 * |
|
1441 * @since 6.1.0 |
|
1442 * |
|
1443 * @param string $slug The template slug to be created. |
|
1444 * @param bool $is_custom Optional. Indicates if a template is custom or |
|
1445 * part of the template hierarchy. Default false. |
|
1446 * @param string $template_prefix Optional. The template prefix for the created template. |
|
1447 * Used to extract the main template type, e.g. |
|
1448 * in `taxonomy-books` the `taxonomy` is extracted. |
|
1449 * Default empty string. |
|
1450 * @return string[] The template hierarchy. |
|
1451 */ |
|
1452 function get_template_hierarchy( $slug, $is_custom = false, $template_prefix = '' ) { |
|
1453 if ( 'index' === $slug ) { |
|
1454 /** This filter is documented in wp-includes/template.php */ |
|
1455 return apply_filters( 'index_template_hierarchy', array( 'index' ) ); |
|
1456 } |
|
1457 if ( $is_custom ) { |
|
1458 /** This filter is documented in wp-includes/template.php */ |
|
1459 return apply_filters( 'page_template_hierarchy', array( 'page', 'singular', 'index' ) ); |
|
1460 } |
|
1461 if ( 'front-page' === $slug ) { |
|
1462 /** This filter is documented in wp-includes/template.php */ |
|
1463 return apply_filters( 'frontpage_template_hierarchy', array( 'front-page', 'home', 'index' ) ); |
|
1464 } |
|
1465 |
|
1466 $matches = array(); |
|
1467 |
|
1468 $template_hierarchy = array( $slug ); |
|
1469 // Most default templates don't have `$template_prefix` assigned. |
|
1470 if ( ! empty( $template_prefix ) ) { |
|
1471 list( $type ) = explode( '-', $template_prefix ); |
|
1472 // We need these checks because we always add the `$slug` above. |
|
1473 if ( ! in_array( $template_prefix, array( $slug, $type ), true ) ) { |
|
1474 $template_hierarchy[] = $template_prefix; |
|
1475 } |
|
1476 if ( $slug !== $type ) { |
|
1477 $template_hierarchy[] = $type; |
|
1478 } |
|
1479 } elseif ( preg_match( '/^(author|category|archive|tag|page)-.+$/', $slug, $matches ) ) { |
|
1480 $template_hierarchy[] = $matches[1]; |
|
1481 } elseif ( preg_match( '/^(taxonomy|single)-(.+)$/', $slug, $matches ) ) { |
|
1482 $type = $matches[1]; |
|
1483 $slug_remaining = $matches[2]; |
|
1484 |
|
1485 $items = 'single' === $type ? get_post_types() : get_taxonomies(); |
|
1486 foreach ( $items as $item ) { |
|
1487 if ( ! str_starts_with( $slug_remaining, $item ) ) { |
|
1488 continue; |
|
1489 } |
|
1490 |
|
1491 // If $slug_remaining is equal to $post_type or $taxonomy we have |
|
1492 // the single-$post_type template or the taxonomy-$taxonomy template. |
|
1493 if ( $slug_remaining === $item ) { |
|
1494 $template_hierarchy[] = $type; |
|
1495 break; |
|
1496 } |
|
1497 |
|
1498 // If $slug_remaining is single-$post_type-$slug template. |
|
1499 if ( strlen( $slug_remaining ) > strlen( $item ) + 1 ) { |
|
1500 $template_hierarchy[] = "$type-$item"; |
|
1501 $template_hierarchy[] = $type; |
|
1502 break; |
|
1503 } |
|
1504 } |
|
1505 } |
|
1506 // Handle `archive` template. |
|
1507 if ( |
|
1508 str_starts_with( $slug, 'author' ) || |
|
1509 str_starts_with( $slug, 'taxonomy' ) || |
|
1510 str_starts_with( $slug, 'category' ) || |
|
1511 str_starts_with( $slug, 'tag' ) || |
|
1512 'date' === $slug |
|
1513 ) { |
|
1514 $template_hierarchy[] = 'archive'; |
|
1515 } |
|
1516 // Handle `single` template. |
|
1517 if ( 'attachment' === $slug ) { |
|
1518 $template_hierarchy[] = 'single'; |
|
1519 } |
|
1520 // Handle `singular` template. |
|
1521 if ( |
|
1522 str_starts_with( $slug, 'single' ) || |
|
1523 str_starts_with( $slug, 'page' ) || |
|
1524 'attachment' === $slug |
|
1525 ) { |
|
1526 $template_hierarchy[] = 'singular'; |
|
1527 } |
|
1528 $template_hierarchy[] = 'index'; |
|
1529 |
|
1530 $template_type = ''; |
|
1531 if ( ! empty( $template_prefix ) ) { |
|
1532 list( $template_type ) = explode( '-', $template_prefix ); |
|
1533 } else { |
|
1534 list( $template_type ) = explode( '-', $slug ); |
|
1535 } |
|
1536 $valid_template_types = array( '404', 'archive', 'attachment', 'author', 'category', 'date', 'embed', 'frontpage', 'home', 'index', 'page', 'paged', 'privacypolicy', 'search', 'single', 'singular', 'tag', 'taxonomy' ); |
|
1537 if ( in_array( $template_type, $valid_template_types, true ) ) { |
|
1538 /** This filter is documented in wp-includes/template.php */ |
|
1539 return apply_filters( "{$template_type}_template_hierarchy", $template_hierarchy ); |
|
1540 } |
|
1541 return $template_hierarchy; |
|
1542 } |
|
1543 |
|
1544 /** |
|
1545 * Inject ignoredHookedBlocks metadata attributes into a template or template part. |
|
1546 * |
|
1547 * Given an object that represents a `wp_template` or `wp_template_part` post object |
|
1548 * prepared for inserting or updating the database, locate all blocks that have |
|
1549 * hooked blocks, and inject a `metadata.ignoredHookedBlocks` attribute into the anchor |
|
1550 * blocks to reflect the latter. |
|
1551 * |
|
1552 * @since 6.5.0 |
|
1553 * @access private |
|
1554 * |
|
1555 * @param stdClass $changes An object representing a template or template part |
|
1556 * prepared for inserting or updating the database. |
|
1557 * @param WP_REST_Request $deprecated Deprecated. Not used. |
|
1558 * @return stdClass|WP_Error The updated object representing a template or template part. |
|
1559 */ |
|
1560 function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated = null ) { |
|
1561 if ( null !== $deprecated ) { |
|
1562 _deprecated_argument( __FUNCTION__, '6.5.3' ); |
|
1563 } |
|
1564 |
|
1565 if ( ! isset( $changes->post_content ) ) { |
|
1566 return $changes; |
|
1567 } |
|
1568 |
|
1569 $hooked_blocks = get_hooked_blocks(); |
|
1570 if ( empty( $hooked_blocks ) && ! has_filter( 'hooked_block_types' ) ) { |
|
1571 return $changes; |
|
1572 } |
|
1573 |
|
1574 $meta = isset( $changes->meta_input ) ? $changes->meta_input : array(); |
|
1575 $terms = isset( $changes->tax_input ) ? $changes->tax_input : array(); |
|
1576 |
|
1577 if ( empty( $changes->ID ) ) { |
|
1578 // There's no post object for this template in the database for this template yet. |
|
1579 $post = $changes; |
|
1580 } else { |
|
1581 // Find the existing post object. |
|
1582 $post = get_post( $changes->ID ); |
|
1583 |
|
1584 // If the post is a revision, use the parent post's post_name and post_type. |
|
1585 $post_id = wp_is_post_revision( $post ); |
|
1586 if ( $post_id ) { |
|
1587 $parent_post = get_post( $post_id ); |
|
1588 $post->post_name = $parent_post->post_name; |
|
1589 $post->post_type = $parent_post->post_type; |
|
1590 } |
|
1591 |
|
1592 // Apply the changes to the existing post object. |
|
1593 $post = (object) array_merge( (array) $post, (array) $changes ); |
|
1594 |
|
1595 $type_terms = get_the_terms( $changes->ID, 'wp_theme' ); |
|
1596 $terms['wp_theme'] = ! is_wp_error( $type_terms ) && ! empty( $type_terms ) ? $type_terms[0]->name : null; |
|
1597 } |
|
1598 |
|
1599 // Required for the WP_Block_Template. Update the post object with the current time. |
|
1600 $post->post_modified = current_time( 'mysql' ); |
|
1601 |
|
1602 // If the post_author is empty, set it to the current user. |
|
1603 if ( empty( $post->post_author ) ) { |
|
1604 $post->post_author = get_current_user_id(); |
|
1605 } |
|
1606 |
|
1607 if ( 'wp_template_part' === $post->post_type && ! isset( $terms['wp_template_part_area'] ) ) { |
|
1608 $area_terms = get_the_terms( $changes->ID, 'wp_template_part_area' ); |
|
1609 $terms['wp_template_part_area'] = ! is_wp_error( $area_terms ) && ! empty( $area_terms ) ? $area_terms[0]->name : null; |
|
1610 } |
|
1611 |
|
1612 $template = _build_block_template_object_from_post_object( new WP_Post( $post ), $terms, $meta ); |
|
1613 |
|
1614 if ( is_wp_error( $template ) ) { |
|
1615 return $template; |
|
1616 } |
|
1617 |
|
1618 $changes->post_content = apply_block_hooks_to_content( $changes->post_content, $template, 'set_ignored_hooked_blocks_metadata' ); |
|
1619 |
|
1620 return $changes; |
|
1621 } |