PluginProbe
Gutenberg / 12.6.0
Gutenberg v12.6.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / compat / wordpress-5.9 / block-template-utils.php

block-template-utils.php in Gutenberg 12.6.0, at lib/compat/wordpress-5.9/block-template-utils.php

951 lines 30.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Temporary compatibility shims for features present in Gutenberg.
4 * This file should be removed when WordPress 5.9.0 becomes the lowest
5 * supported version by this plugin.
6 *
7 * @package gutenberg
8 */
9
10 // Define constants for supported wp_template_part_area taxonomy.
11 if ( ! defined( 'WP_TEMPLATE_PART_AREA_HEADER' ) ) {
12 define( 'WP_TEMPLATE_PART_AREA_HEADER', 'header' );
13 }
14 if ( ! defined( 'WP_TEMPLATE_PART_AREA_FOOTER' ) ) {
15 define( 'WP_TEMPLATE_PART_AREA_FOOTER', 'footer' );
16 }
17 if ( ! defined( 'WP_TEMPLATE_PART_AREA_SIDEBAR' ) ) {
18 define( 'WP_TEMPLATE_PART_AREA_SIDEBAR', 'sidebar' );
19 }
20 if ( ! defined( 'WP_TEMPLATE_PART_AREA_UNCATEGORIZED' ) ) {
21 define( 'WP_TEMPLATE_PART_AREA_UNCATEGORIZED', 'uncategorized' );
22 }
23
24 if ( ! function_exists( 'get_block_theme_folders' ) ) {
25 /**
26 * For backward compatibility reasons,
27 * block themes might be using block-templates or block-template-parts,
28 * this function ensures we fallback to these folders properly.
29 *
30 * @param string $theme_stylesheet The stylesheet. Default is to leverage the main theme root.
31 *
32 * @return array Folder names used by block themes.
33 */
34 function get_block_theme_folders( $theme_stylesheet = null ) {
35 $theme_name = null === $theme_stylesheet ? get_stylesheet() : $theme_stylesheet;
36 $root_dir = get_theme_root( $theme_name );
37 $theme_dir = "$root_dir/$theme_name";
38
39 if ( file_exists( $theme_dir . '/block-templates' ) || file_exists( $theme_dir . '/block-template-parts' ) ) {
40 return array(
41 'wp_template' => 'block-templates',
42 'wp_template_part' => 'block-template-parts',
43 );
44 }
45
46 return array(
47 'wp_template' => 'templates',
48 'wp_template_part' => 'parts',
49 );
50 }
51 }
52
53 if ( ! function_exists( 'get_allowed_block_template_part_areas' ) ) {
54 /**
55 * Returns a filtered list of allowed area values for template parts.
56 *
57 * @return array The supported template part area values.
58 */
59 function get_allowed_block_template_part_areas() {
60 $default_area_definitions = array(
61 array(
62 'area' => WP_TEMPLATE_PART_AREA_UNCATEGORIZED,
63 'label' => __( 'General', 'gutenberg' ),
64 'description' => __(
65 'General templates often perform a specific role like displaying post content, and are not tied to any particular area.',
66 'gutenberg'
67 ),
68 'icon' => 'layout',
69 'area_tag' => 'div',
70 ),
71 array(
72 'area' => WP_TEMPLATE_PART_AREA_HEADER,
73 'label' => __( 'Header', 'gutenberg' ),
74 'description' => __(
75 'The Header template defines a page area that typically contains a title, logo, and main navigation.',
76 'gutenberg'
77 ),
78 'icon' => 'header',
79 'area_tag' => 'header',
80 ),
81 array(
82 'area' => WP_TEMPLATE_PART_AREA_FOOTER,
83 'label' => __( 'Footer', 'gutenberg' ),
84 'description' => __(
85 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.',
86 'gutenberg'
87 ),
88 'icon' => 'footer',
89 'area_tag' => 'footer',
90 ),
91 );
92
93 /**
94 * Filters the list of allowed template part area values.
95 *
96 * @param array $default_areas An array of supported area objects.
97 */
98 return apply_filters( 'default_wp_template_part_areas', $default_area_definitions );
99 }
100 }
101
102 if ( ! function_exists( 'get_default_block_template_types' ) ) {
103 /**
104 * Returns a filtered list of default template types, containing their
105 * localized titles and descriptions.
106 *
107 * @return array The default template types.
108 */
109 function get_default_block_template_types() {
110 $default_template_types = array(
111 'index' => array(
112 'title' => _x( 'Index', 'Template name', 'gutenberg' ),
113 'description' => __( 'Displays posts.', 'gutenberg' ),
114 ),
115 'home' => array(
116 'title' => _x( 'Home', 'Template name', 'gutenberg' ),
117 'description' => __( 'Displays posts on the homepage, or on the Posts page if a static homepage is set.', 'gutenberg' ),
118 ),
119 'front-page' => array(
120 'title' => _x( 'Front Page', 'Template name', 'gutenberg' ),
121 'description' => __( 'Displays the homepage.', 'gutenberg' ),
122 ),
123 'singular' => array(
124 'title' => _x( 'Singular', 'Template name', 'gutenberg' ),
125 'description' => __( 'Displays a single post or page.', 'gutenberg' ),
126 ),
127 'single' => array(
128 'title' => _x( 'Single Post', 'Template name', 'gutenberg' ),
129 'description' => __( 'Displays a single post.', 'gutenberg' ),
130 ),
131 'page' => array(
132 'title' => _x( 'Page', 'Template name', 'gutenberg' ),
133 'description' => __( 'Displays a single page.', 'gutenberg' ),
134 ),
135 'archive' => array(
136 'title' => _x( 'Archive', 'Template name', 'gutenberg' ),
137 'description' => __( 'Displays post categories, tags, and other archives.', 'gutenberg' ),
138 ),
139 'author' => array(
140 'title' => _x( 'Author', 'Template name', 'gutenberg' ),
141 'description' => __( 'Displays latest posts written by a single author.', 'gutenberg' ),
142 ),
143 'category' => array(
144 'title' => _x( 'Category', 'Template name', 'gutenberg' ),
145 'description' => __( 'Displays latest posts in single post category.', 'gutenberg' ),
146 ),
147 'taxonomy' => array(
148 'title' => _x( 'Taxonomy', 'Template name', 'gutenberg' ),
149 'description' => __( 'Displays latest posts from a single post taxonomy.', 'gutenberg' ),
150 ),
151 'date' => array(
152 'title' => _x( 'Date', 'Template name', 'gutenberg' ),
153 'description' => __( 'Displays posts from a specific date.', 'gutenberg' ),
154 ),
155 'tag' => array(
156 'title' => _x( 'Tag', 'Template name', 'gutenberg' ),
157 'description' => __( 'Displays latest posts with single post tag.', 'gutenberg' ),
158 ),
159 'attachment' => array(
160 'title' => __( 'Media', 'gutenberg' ),
161 'description' => __( 'Displays individual media items or attachments.', 'gutenberg' ),
162 ),
163 'search' => array(
164 'title' => _x( 'Search', 'Template name', 'gutenberg' ),
165 'description' => __( 'Displays search results.', 'gutenberg' ),
166 ),
167 'privacy-policy' => array(
168 'title' => __( 'Privacy Policy', 'gutenberg' ),
169 'description' => __( 'Displays the privacy policy page.', 'gutenberg' ),
170 ),
171 '404' => array(
172 'title' => _x( '404', 'Template name', 'gutenberg' ),
173 'description' => __( 'Displays when no content is found.', 'gutenberg' ),
174 ),
175 );
176
177 /**
178 * Filters the list of template types.
179 *
180 * @param array $default_template_types An array of template types, formatted as [ slug => [ title, description ] ].
181 *
182 * @since 5.x.x
183 */
184 return apply_filters( 'default_template_types', $default_template_types );
185 }
186 }
187
188 if ( ! function_exists( '_filter_block_template_part_area' ) ) {
189 /**
190 * Checks whether the input 'area' is a supported value.
191 * Returns the input if supported, otherwise returns the 'uncategorized' value.
192 *
193 * @param string $type Template part area name.
194 *
195 * @return string Input if supported, else the uncategorized value.
196 */
197 function _filter_block_template_part_area( $type ) {
198 $allowed_areas = array_map(
199 static function ( $item ) {
200 return $item['area'];
201 },
202 get_allowed_block_template_part_areas()
203 );
204 if ( in_array( $type, $allowed_areas, true ) ) {
205 return $type;
206 }
207
208 /* translators: %1$s: Template area type, %2$s: the uncategorized template area value. */
209 $warning_message = sprintf( __( '"%1$s" is not a supported wp_template_part area value and has been added as "%2$s".', 'gutenberg' ), $type, WP_TEMPLATE_PART_AREA_UNCATEGORIZED );
210 trigger_error( $warning_message, E_USER_NOTICE );
211 return WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
212 }
213 }
214
215 if ( ! function_exists( '_get_block_templates_paths' ) ) {
216 /**
217 * Finds all nested template part file paths in a theme's directory.
218 *
219 * @access private
220 *
221 * @param string $base_directory The theme's file path.
222 * @return array $path_list A list of paths to all template part files.
223 */
224 function _get_block_templates_paths( $base_directory ) {
225 $path_list = array();
226 if ( file_exists( $base_directory ) ) {
227 $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) );
228 $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH );
229 foreach ( $nested_html_files as $path => $file ) {
230 $path_list[] = $path;
231 }
232 }
233 return $path_list;
234 }
235 }
236
237 if ( ! function_exists( '_get_block_template_file' ) ) {
238 /**
239 * Retrieves the template file from the theme for a given slug.
240 *
241 * @access private
242 * @internal
243 *
244 * @param string $template_type wp_template or wp_template_part.
245 * @param string $slug template slug.
246 *
247 * @return array|null Template.
248 */
249 function _get_block_template_file( $template_type, $slug ) {
250 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) {
251 return null;
252 }
253
254 $themes = array(
255 get_stylesheet() => get_stylesheet_directory(),
256 get_template() => get_template_directory(),
257 );
258 foreach ( $themes as $theme_slug => $theme_dir ) {
259 $template_base_paths = get_block_theme_folders( $theme_slug );
260 $file_path = $theme_dir . '/' . $template_base_paths[ $template_type ] . '/' . $slug . '.html';
261 if ( file_exists( $file_path ) ) {
262 $new_template_item = array(
263 'slug' => $slug,
264 'path' => $file_path,
265 'theme' => $theme_slug,
266 'type' => $template_type,
267 );
268
269 if ( 'wp_template_part' === $template_type ) {
270 return _add_block_template_part_area_info( $new_template_item );
271 }
272
273 if ( 'wp_template' === $template_type ) {
274 return _add_block_template_info( $new_template_item );
275 }
276
277 return $new_template_item;
278 }
279 }
280
281 return null;
282 }
283 }
284
285 if ( ! function_exists( '_get_block_templates_files' ) ) {
286 /**
287 * Retrieves the template files from the theme.
288 *
289 * @access private
290 * @internal
291 *
292 * @param string $template_type wp_template or wp_template_part.
293 *
294 * @return array Template.
295 */
296 function _get_block_templates_files( $template_type ) {
297 if ( 'wp_template' !== $template_type && 'wp_template_part' !== $template_type ) {
298 return null;
299 }
300
301 $themes = array(
302 get_stylesheet() => get_stylesheet_directory(),
303 get_template() => get_template_directory(),
304 );
305 $template_files = array();
306 foreach ( $themes as $theme_slug => $theme_dir ) {
307 $template_base_paths = get_block_theme_folders( $theme_slug );
308 $theme_template_files = _get_block_templates_paths( $theme_dir . '/' . $template_base_paths[ $template_type ] );
309 foreach ( $theme_template_files as $template_file ) {
310 $template_base_path = $template_base_paths[ $template_type ];
311 $template_slug = substr(
312 $template_file,
313 // Starting position of slug.
314 strpos( $template_file, $template_base_path . DIRECTORY_SEPARATOR ) + 1 + strlen( $template_base_path ),
315 // Subtract ending '.html'.
316 -5
317 );
318 $new_template_item = array(
319 'slug' => $template_slug,
320 'path' => $template_file,
321 'theme' => $theme_slug,
322 'type' => $template_type,
323 );
324
325 if ( 'wp_template_part' === $template_type ) {
326 $template_files[] = _add_block_template_part_area_info( $new_template_item );
327 }
328
329 if ( 'wp_template' === $template_type ) {
330 $template_files[] = _add_block_template_info( $new_template_item );
331 }
332 }
333 }
334
335 return $template_files;
336 }
337 }
338
339 if ( ! function_exists( '_add_block_template_info' ) ) {
340 /**
341 * Attempts to add custom template information to the template item.
342 *
343 * @param array $template_item Template to add information to (requires 'slug' field).
344 * @return array Template
345 */
346 function _add_block_template_info( $template_item ) {
347 if ( ! WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
348 return $template_item;
349 }
350
351 $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_custom_templates();
352 if ( isset( $theme_data[ $template_item['slug'] ] ) ) {
353 $template_item['title'] = $theme_data[ $template_item['slug'] ]['title'];
354 $template_item['postTypes'] = $theme_data[ $template_item['slug'] ]['postTypes'];
355 }
356
357 return $template_item;
358 }
359 }
360
361 if ( ! function_exists( '_add_block_template_part_area_info' ) ) {
362 /**
363 * Attempts to add the template part's area information to the input template.
364 *
365 * @param array $template_info Template to add information to (requires 'type' and 'slug' fields).
366 *
367 * @return array Template.
368 */
369 function _add_block_template_part_area_info( $template_info ) {
370 if ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) {
371 $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_template_parts();
372 }
373
374 if ( isset( $theme_data[ $template_info['slug'] ]['area'] ) ) {
375 $template_info['title'] = $theme_data[ $template_info['slug'] ]['title'];
376 $template_info['area'] = _filter_block_template_part_area( $theme_data[ $template_info['slug'] ]['area'] );
377 } else {
378 $template_info['area'] = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
379 }
380
381 return $template_info;
382 }
383 }
384
385 if ( ! function_exists( '_flatten_blocks' ) ) {
386 /**
387 * Returns an array containing the references of
388 * the passed blocks and their inner blocks.
389 *
390 * @param array $blocks array of blocks.
391 *
392 * @return array block references to the passed blocks and their inner blocks.
393 */
394 function _flatten_blocks( &$blocks ) {
395 $all_blocks = array();
396 $queue = array();
397 foreach ( $blocks as &$block ) {
398 $queue[] = &$block;
399 }
400
401 while ( count( $queue ) > 0 ) {
402 $block = &$queue[0];
403 array_shift( $queue );
404 $all_blocks[] = &$block;
405
406 if ( ! empty( $block['innerBlocks'] ) ) {
407 foreach ( $block['innerBlocks'] as &$inner_block ) {
408 $queue[] = &$inner_block;
409 }
410 }
411 }
412
413 return $all_blocks;
414 }
415 }
416
417 if ( ! function_exists( '_inject_theme_attribute_in_block_template_content' ) ) {
418 /**
419 * Parses wp_template content and injects the current theme's
420 * stylesheet as a theme attribute into each wp_template_part
421 *
422 * @param string $template_content serialized wp_template content.
423 *
424 * @return string Updated wp_template content.
425 */
426 function _inject_theme_attribute_in_block_template_content( $template_content ) {
427 $has_updated_content = false;
428 $new_content = '';
429 $template_blocks = parse_blocks( $template_content );
430
431 $blocks = _flatten_blocks( $template_blocks );
432 foreach ( $blocks as &$block ) {
433 if (
434 'core/template-part' === $block['blockName'] &&
435 ! isset( $block['attrs']['theme'] )
436 ) {
437 $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
438 $has_updated_content = true;
439 }
440 }
441
442 if ( $has_updated_content ) {
443 foreach ( $template_blocks as &$block ) {
444 $new_content .= serialize_block( $block );
445 }
446
447 return $new_content;
448 }
449
450 return $template_content;
451 }
452 }
453
454 if ( ! function_exists( '_remove_theme_attribute_in_block_template_content' ) ) {
455 /**
456 * Parses wp_template content and removes the theme attribute from
457 * each wp_template_part
458 *
459 * @param string $template_content serialized wp_template content.
460 *
461 * @return string Updated wp_template content.
462 */
463 function _remove_theme_attribute_in_block_template_content( $template_content ) {
464 $has_updated_content = false;
465 $new_content = '';
466 $template_blocks = parse_blocks( $template_content );
467
468 $blocks = _flatten_blocks( $template_blocks );
469 foreach ( $blocks as $key => $block ) {
470 if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) {
471 unset( $blocks[ $key ]['attrs']['theme'] );
472 $has_updated_content = true;
473 }
474 }
475
476 if ( ! $has_updated_content ) {
477 return $template_content;
478 }
479
480 foreach ( $template_blocks as $block ) {
481 $new_content .= serialize_block( $block );
482 }
483
484 return $new_content;
485 }
486 }
487
488 if ( ! function_exists( '_build_block_template_result_from_file' ) ) {
489 /**
490 * Build a unified template object based on a theme file.
491 *
492 * @param array $template_file Theme file.
493 * @param array $template_type wp_template or wp_template_part.
494 *
495 * @return Gutenberg_Block_Template Template.
496 */
497 function _build_block_template_result_from_file( $template_file, $template_type ) {
498 $default_template_types = get_default_block_template_types();
499 $template_content = file_get_contents( $template_file['path'] );
500 $theme = wp_get_theme()->get_stylesheet();
501
502 $template = new Gutenberg_Block_Template();
503 $template->id = $theme . '//' . $template_file['slug'];
504 $template->theme = $theme;
505 $template->content = _inject_theme_attribute_in_block_template_content( $template_content );
506 $template->slug = $template_file['slug'];
507 $template->source = 'theme';
508 $template->type = $template_type;
509 $template->title = ! empty( $template_file['title'] ) ? $template_file['title'] : $template_file['slug'];
510 $template->status = 'publish';
511 $template->has_theme_file = true;
512 $template->is_custom = true;
513
514 if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) {
515 $template->description = $default_template_types[ $template_file['slug'] ]['description'];
516 $template->title = $default_template_types[ $template_file['slug'] ]['title'];
517 $template->is_custom = false;
518 }
519
520 if ( 'wp_template' === $template_type && isset( $template_file['postTypes'] ) ) {
521 $template->post_types = $template_file['postTypes'];
522 }
523
524 if ( 'wp_template_part' === $template_type && isset( $template_file['area'] ) ) {
525 $template->area = $template_file['area'];
526 }
527
528 return $template;
529 }
530 }
531
532 if ( ! function_exists( '_build_block_template_result_from_post' ) ) {
533 /**
534 * Build a unified template object based a post Object.
535 *
536 * @param WP_Post $post Template post.
537 *
538 * @return Gutenberg_Block_Template|WP_Error Template.
539 */
540 function _build_block_template_result_from_post( $post ) {
541 $default_template_types = get_default_block_template_types();
542 $terms = get_the_terms( $post, 'wp_theme' );
543
544 if ( is_wp_error( $terms ) ) {
545 return $terms;
546 }
547
548 if ( ! $terms ) {
549 return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'gutenberg' ) );
550 }
551
552 $origin = get_post_meta( $post->ID, 'origin', true );
553
554 $theme = $terms[0]->name;
555 $has_theme_file = wp_get_theme()->get_stylesheet() === $theme &&
556 null !== _get_block_template_file( $post->post_type, $post->post_name );
557
558 $template = new Gutenberg_Block_Template();
559 $template->wp_id = $post->ID;
560 $template->id = $theme . '//' . $post->post_name;
561 $template->theme = $theme;
562 $template->content = $post->post_content;
563 $template->slug = $post->post_name;
564 $template->source = 'custom';
565 $template->origin = ! empty( $origin ) ? $origin : null;
566 $template->type = $post->post_type;
567 $template->description = $post->post_excerpt;
568 $template->title = $post->post_title;
569 $template->status = $post->post_status;
570 $template->has_theme_file = $has_theme_file;
571 $template->is_custom = true;
572 $template->author = $post->post_author;
573
574 if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) {
575 $template->is_custom = false;
576 }
577
578 if ( 'wp_template_part' === $post->post_type ) {
579 $type_terms = get_the_terms( $post, 'wp_template_part_area' );
580 if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
581 $template->area = $type_terms[0]->name;
582 }
583 }
584
585 return $template;
586 }
587 }
588
589
590 /**
591 * Retrieves a list of unified template objects based on a query.
592 *
593 * @param array $query {
594 * Optional. Arguments to retrieve templates.
595 *
596 * @type array $slug__in List of slugs to include.
597 * @type int $wp_id Post ID of customized template.
598 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for wp_template_part template type only).
599 * @type string $post_type Post type to get the templates for.
600 * }
601 * @param array $template_type wp_template or wp_template_part.
602 *
603 * @return array Templates.
604 */
605 function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_template' ) {
606 /**
607 * Filters the block templates array before the query takes place.
608 *
609 * Return a non-null value to bypass the WordPress queries.
610 *
611 * @since 10.8
612 *
613 * @param Gutenberg_Block_Template[]|null $block_templates Return an array of block templates to short-circuit the default query,
614 * or null to allow WP to run it's normal queries.
615 * @param array $query {
616 * Optional. Arguments to retrieve templates.
617 *
618 * @type array $slug__in List of slugs to include.
619 * @type int $wp_id Post ID of customized template.
620 * @type string $post_type Post type to get the templates for.
621 * }
622 * @param array $template_type wp_template or wp_template_part.
623 */
624 $templates = apply_filters( 'pre_get_block_templates', null, $query, $template_type );
625 if ( ! is_null( $templates ) ) {
626 return $templates;
627 }
628
629 $post_type = isset( $query['post_type'] ) ? $query['post_type'] : '';
630 $wp_query_args = array(
631 'post_status' => array( 'auto-draft', 'draft', 'publish' ),
632 'post_type' => $template_type,
633 'posts_per_page' => -1,
634 'no_found_rows' => true,
635 'tax_query' => array(
636 array(
637 'taxonomy' => 'wp_theme',
638 'field' => 'name',
639 'terms' => wp_get_theme()->get_stylesheet(),
640 ),
641 ),
642 );
643
644 if ( 'wp_template_part' === $template_type && isset( $query['area'] ) ) {
645 $wp_query_args['tax_query'][] = array(
646 'taxonomy' => 'wp_template_part_area',
647 'field' => 'name',
648 'terms' => $query['area'],
649 );
650 $wp_query_args['tax_query']['relation'] = 'AND';
651 }
652
653 if ( isset( $query['slug__in'] ) ) {
654 $wp_query_args['post_name__in'] = $query['slug__in'];
655 }
656
657 // This is only needed for the regular templates/template parts CPT listing and editor.
658 if ( isset( $query['wp_id'] ) ) {
659 $wp_query_args['p'] = $query['wp_id'];
660 } else {
661 $wp_query_args['post_status'] = 'publish';
662 }
663
664 $template_query = new WP_Query( $wp_query_args );
665 $query_result = array();
666 foreach ( $template_query->posts as $post ) {
667 $template = _build_block_template_result_from_post( $post );
668
669 if ( is_wp_error( $template ) ) {
670 continue;
671 }
672
673 if ( $post_type && ! $template->is_custom ) {
674 continue;
675 }
676
677 $query_result[] = $template;
678 }
679
680 if ( ! isset( $query['wp_id'] ) ) {
681 $template_files = _get_block_templates_files( $template_type );
682 foreach ( $template_files as $template_file ) {
683 $template = _build_block_template_result_from_file( $template_file, $template_type );
684
685 if ( $post_type && ! $template->is_custom ) {
686 continue;
687 }
688
689 if ( $post_type &&
690 isset( $template->post_types ) &&
691 ! in_array( $post_type, $template->post_types, true )
692 ) {
693 continue;
694 }
695
696 $is_not_custom = false === array_search(
697 wp_get_theme()->get_stylesheet() . '//' . $template_file['slug'],
698 array_column( $query_result, 'id' ),
699 true
700 );
701 $fits_slug_query =
702 ! isset( $query['slug__in'] ) || in_array( $template_file['slug'], $query['slug__in'], true );
703 $fits_area_query =
704 ! isset( $query['area'] ) || $template_file['area'] === $query['area'];
705 $should_include = $is_not_custom && $fits_slug_query && $fits_area_query;
706 if ( $should_include ) {
707 $query_result[] = $template;
708 }
709 }
710 }
711 /**
712 * Filters the array of queried block templates array after they've been fetched.
713 *
714 * @since 10.8
715 *
716 * @param Gutenberg_Block_Template[] $query_result Array of found block templates.
717 * @param array $query {
718 * Optional. Arguments to retrieve templates.
719 *
720 * @type array $slug__in List of slugs to include.
721 * @type int $wp_id Post ID of customized template.
722 * }
723 * @param array $template_type wp_template or wp_template_part.
724 */
725 return apply_filters( 'get_block_templates', $query_result, $query, $template_type );
726 }
727
728 /**
729 * Retrieves a single unified template object using its id.
730 *
731 * @param string $id Template unique identifier (example: theme_slug//template_slug).
732 * @param array $template_type wp_template or wp_template_part.
733 *
734 * @return Gutenberg_Block_Template|null Template.
735 */
736 function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) {
737 /**
738 * Filters the block template object before the query takes place.
739 *
740 * Return a non-null value to bypass the WordPress queries.
741 *
742 * @since 10.8
743 *
744 * @param Gutenberg_Block_Template|null $block_template Return block template object to short-circuit the default query,
745 * or null to allow WP to run it's normal queries.
746 * @param string $id Template unique identifier (example: theme_slug//template_slug).
747 * @param array $template_type wp_template or wp_template_part.
748 */
749 $block_template = apply_filters( 'pre_get_block_template', null, $id, $template_type );
750 if ( ! is_null( $block_template ) ) {
751 return $block_template;
752 }
753
754 $parts = explode( '//', $id, 2 );
755 if ( count( $parts ) < 2 ) {
756 return null;
757 }
758 list( $theme, $slug ) = $parts;
759 $wp_query_args = array(
760 'post_name__in' => array( $slug ),
761 'post_type' => $template_type,
762 'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
763 'posts_per_page' => 1,
764 'no_found_rows' => true,
765 'tax_query' => array(
766 array(
767 'taxonomy' => 'wp_theme',
768 'field' => 'name',
769 'terms' => $theme,
770 ),
771 ),
772 );
773 $template_query = new WP_Query( $wp_query_args );
774 $posts = $template_query->posts;
775
776 if ( count( $posts ) > 0 ) {
777 $template = _build_block_template_result_from_post( $posts[0] );
778
779 if ( ! is_wp_error( $template ) ) {
780 return $template;
781 }
782 }
783
784 $block_template = get_block_file_template( $id, $template_type );
785
786 /**
787 * Filters the queried block template object after it's been fetched.
788 *
789 * @since 10.8
790 *
791 * @param Gutenberg_Block_Template|null $block_template The found block template, or null if there isn't one.
792 * @param string $id Template unique identifier (example: theme_slug//template_slug).
793 * @param array $template_type wp_template or wp_template_part.
794 */
795 return apply_filters( 'get_block_template', $block_template, $id, $template_type );
796 }
797
798 if ( ! function_exists( 'get_block_file_template' ) ) {
799 /**
800 * Retrieves a single unified template object using its id.
801 * Retrieves the file template.
802 *
803 * @param string $id Template unique identifier (example: theme_slug//template_slug).
804 * @param array $template_type wp_template or wp_template_part.
805 *
806 * @return Gutenberg_Block_Template|null File template.
807 */
808 function get_block_file_template( $id, $template_type = 'wp_template' ) {
809 /**
810 * Filters the block templates array before the query takes place.
811 *
812 * Return a non-null value to bypass the WordPress queries.
813 *
814 * @since 10.8
815 *
816 * @param Gutenberg_Block_Template|null $block_template Return block template object to short-circuit the default query,
817 * or null to allow WP to run it's normal queries.
818 * @param string $id Template unique identifier (example: theme_slug//template_slug).
819 * @param array $template_type wp_template or wp_template_part.
820 */
821 $block_template = apply_filters( 'pre_get_block_file_template', null, $id, $template_type );
822 if ( ! is_null( $block_template ) ) {
823 return $block_template;
824 }
825
826 $parts = explode( '//', $id, 2 );
827 if ( count( $parts ) < 2 ) {
828 /** This filter is documented at the end of this function */
829 return apply_filters( 'get_block_file_template', null, $id, $template_type );
830 }
831 list( $theme, $slug ) = $parts;
832
833 if ( wp_get_theme()->get_stylesheet() !== $theme ) {
834 /** This filter is documented at the end of this function */
835 return apply_filters( 'get_block_file_template', null, $id, $template_type );
836 }
837
838 $template_file = _get_block_template_file( $template_type, $slug );
839 if ( null === $template_file ) {
840 /** This filter is documented at the end of this function */
841 return apply_filters( 'get_block_file_template', null, $id, $template_type );
842 }
843
844 $block_template = _build_block_template_result_from_file( $template_file, $template_type );
845
846 /**
847 * Filters the array of queried block templates array after they've been fetched.
848 *
849 * @since 10.8
850 *
851 * @param null|Gutenberg_Block_Template $block_template The found block template.
852 * @param string $id Template unique identifier (example: theme_slug//template_slug).
853 * @param array $template_type wp_template or wp_template_part.
854 */
855 return apply_filters( 'get_block_file_template', $block_template, $id, $template_type );
856 }
857 }
858
859 if ( ! function_exists( 'block_template_part' ) ) {
860 /**
861 * Print a template-part.
862 *
863 * @param string $part The template-part to print. Use "header" or "footer".
864 *
865 * @return void
866 */
867 function block_template_part( $part ) {
868 $template_part = gutenberg_get_block_template( get_stylesheet() . '//' . $part, 'wp_template_part' );
869 if ( ! $template_part || empty( $template_part->content ) ) {
870 return;
871 }
872 echo do_blocks( $template_part->content );
873 }
874 }
875
876 if ( ! function_exists( 'block_header_area' ) ) {
877 /**
878 * Print the header template-part.
879 *
880 * @return void
881 */
882 function block_header_area() {
883 block_template_part( 'header' );
884 }
885 }
886
887 if ( ! function_exists( 'block_footer_area' ) ) {
888 /**
889 * Print the footer template-part.
890 *
891 * @return void
892 */
893 function block_footer_area() {
894 block_template_part( 'footer' );
895 }
896 }
897
898 if ( ! function_exists( 'wp_generate_block_templates_export_file' ) ) {
899 /**
900 * Creates an export of the current templates and
901 * template parts from the site editor at the
902 * specified path in a ZIP file.
903 *
904 * @since 5.9.0
905 *
906 * @return WP_Error|string Path of the ZIP file or error on failure.
907 */
908 function wp_generate_block_templates_export_file() {
909 if ( ! class_exists( 'ZipArchive' ) ) {
910 return new WP_Error( 'missing_zip_package', __( 'Zip Export not supported.', 'gutenberg' ) );
911 }
912
913 $obscura = wp_generate_password( 12, false, false );
914 $filename = get_temp_dir() . 'edit-site-export-' . $obscura . '.zip';
915
916 $zip = new ZipArchive();
917 if ( true !== $zip->open( $filename, ZipArchive::CREATE ) ) {
918 return new WP_Error( 'unable_to_create_zip', __( 'Unable to open export file (archive) for writing.', 'gutenberg' ) );
919 }
920
921 $zip->addEmptyDir( 'theme' );
922 $zip->addEmptyDir( 'theme/templates' );
923 $zip->addEmptyDir( 'theme/parts' );
924
925 // Load templates into the zip file.
926 $templates = gutenberg_get_block_templates();
927 foreach ( $templates as $template ) {
928 $template->content = _remove_theme_attribute_in_block_template_content( $template->content );
929
930 $zip->addFromString(
931 'theme/templates/' . $template->slug . '.html',
932 $template->content
933 );
934 }
935
936 // Load template parts into the zip file.
937 $template_parts = gutenberg_get_block_templates( array(), 'wp_template_part' );
938 foreach ( $template_parts as $template_part ) {
939 $zip->addFromString(
940 'theme/parts/' . $template_part->slug . '.html',
941 $template_part->content
942 );
943 }
944
945 // Save changes to the zip file.
946 $zip->close();
947
948 return $filename;
949 }
950 }
951