PluginProbe
Gutenberg / 23.7.2
Gutenberg v23.7.2
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-7.0 / template-activate.php

template-activate.php in Gutenberg 23.7.2, at lib/compat/wordpress-7.0/template-activate.php

843 lines 30.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Migrate existing "edited" templates. By existing, it means that the template
4 // is active.
5 function gutenberg_get_migrated_active_templates() {
6 // Query all templates in the database. See `get_block_templates`.
7 $wp_query_args = array(
8 'post_status' => 'publish',
9 'post_type' => 'wp_template',
10 'posts_per_page' => -1,
11 'no_found_rows' => true,
12 'lazy_load_term_meta' => false,
13 'tax_query' => array(
14 array(
15 'taxonomy' => 'wp_theme',
16 'field' => 'name',
17 'terms' => get_stylesheet(),
18 ),
19 ),
20 // Only get templates that are not inactive by default.
21 'meta_query' => array(
22 'relation' => 'OR',
23 array(
24 'key' => 'is_inactive_by_default',
25 'compare' => 'NOT EXISTS',
26 ),
27 array(
28 'key' => 'is_inactive_by_default',
29 'value' => false,
30 'compare' => '=',
31 ),
32 ),
33 );
34
35 $template_query = new WP_Query( $wp_query_args );
36 $active_templates = array();
37
38 foreach ( $template_query->posts as $post ) {
39 $active_templates[ $post->post_name ] = $post->ID;
40 }
41
42 return $active_templates;
43 }
44
45 require_once __DIR__ . '/class-gutenberg-rest-old-templates-controller.php';
46
47 // How does this work?
48 // 1. For wp_template, we remove the custom templates controller, so it becomes
49 // a normal posts endpoint, modified slightly to allow auto-drafts.
50 add_filter( 'register_post_type_args', 'gutenberg_modify_wp_template_post_type_args', 10, 2 );
51 function gutenberg_modify_wp_template_post_type_args( $args, $post_type ) {
52 // Check active_templates experiment status.
53 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
54 return $args;
55 }
56 if ( 'wp_template' === $post_type ) {
57 $args['rest_base'] = 'created-templates';
58 $args['rest_controller_class'] = 'WP_REST_Posts_Controller';
59 $args['autosave_rest_controller_class'] = null;
60 $args['revisions_rest_controller_class'] = null;
61 $args['supports'] = array_merge( $args['supports'], array( 'custom-fields' ) );
62 }
63 return $args;
64 }
65
66 // 2. We maintain the routes for /templates and /templates/lookup. I think we'll
67 // need to deprecate /templates eventually, but we'll still want to be able
68 // to lookup the active template for a specific slug, and probably get a list
69 // of all _active_ templates. For that we can keep /lookup.
70 // Priority 100, after `create_initial_rest_routes`.
71 add_action( 'rest_api_init', 'gutenberg_maintain_templates_routes', 100 );
72
73 /**
74 * @global array $wp_post_types List of post types.
75 */
76 function gutenberg_maintain_templates_routes() {
77 // Check active_templates experiment status.
78 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
79 return;
80 }
81 // This should later be changed in core so we don't need initialize
82 // WP_REST_Templates_Controller with a post type.
83 global $wp_post_types;
84 // Register the old templates endpoints. The WP_REST_Templates_Controller
85 // and sub-controllers used to be linked to the wp_template post type, but
86 // are no longer. They still require a post type object when constructing the
87 // class. To maintain backward and changes to these controller classes, we
88 // make use that the wp_template post type has the right information it
89 // needs.
90 $wp_post_types['wp_template']->rest_base = 'templates';
91 // Store the classes so they can be restored.
92 $original_rest_controller_class = $wp_post_types['wp_template']->rest_controller_class;
93 $original_autosave_rest_controller_class = $wp_post_types['wp_template']->autosave_rest_controller_class;
94 $original_revisions_rest_controller_class = $wp_post_types['wp_template']->revisions_rest_controller_class;
95 // Temporarily set the old classes.
96 $wp_post_types['wp_template']->rest_controller_class = 'WP_REST_Templates_Controller';
97 $wp_post_types['wp_template']->autosave_rest_controller_class = 'WP_REST_Template_Autosaves_Controller';
98 $wp_post_types['wp_template']->revisions_rest_controller_class = 'WP_REST_Template_Revisions_Controller';
99 // Initialize the controllers. The order is important: the autosave
100 // controller needs both the templates and revisions controllers.
101 $controller = new Gutenberg_REST_Old_Templates_Controller( 'wp_template' );
102 $wp_post_types['wp_template']->rest_controller = $controller;
103 $revisions_controller = new WP_REST_Template_Revisions_Controller( 'wp_template' );
104 $wp_post_types['wp_template']->revisions_rest_controller = $revisions_controller;
105 $autosaves_controller = new WP_REST_Template_Autosaves_Controller( 'wp_template' );
106 // Unset the controller cache, it will be re-initialized when
107 // get_rest_controller is called.
108 $wp_post_types['wp_template']->rest_controller = null;
109 $wp_post_types['wp_template']->revisions_rest_controller = null;
110 // Restore the original classes.
111 $wp_post_types['wp_template']->rest_controller_class = $original_rest_controller_class;
112 $wp_post_types['wp_template']->autosave_rest_controller_class = $original_autosave_rest_controller_class;
113 $wp_post_types['wp_template']->revisions_rest_controller_class = $original_revisions_rest_controller_class;
114 // Restore the original base.
115 $wp_post_types['wp_template']->rest_base = 'created-templates';
116
117 // Register the old routes.
118 $autosaves_controller->register_routes();
119 $revisions_controller->register_routes();
120 $controller->register_routes();
121
122 $registered_template_controller = new Gutenberg_REST_Static_Templates_Controller();
123 $registered_template_controller->register_routes();
124
125 // Add the same field as wp_registered_template.
126 register_rest_field(
127 'wp_template',
128 'theme',
129 array(
130 'get_callback' => function ( $post_arr ) {
131 // add_additional_fields_to_object is also called for the old
132 // templates controller, so we need to check if the id is an
133 // integer to make sure it's the proper post type endpoint.
134 if ( ! is_int( $post_arr['id'] ) ) {
135 $template = get_block_template( $post_arr['id'], 'wp_template' );
136 return $template ? $template->theme : null;
137 }
138 $terms = get_the_terms( $post_arr['id'], 'wp_theme' );
139 if ( is_wp_error( $terms ) || empty( $terms ) ) {
140 return null;
141 }
142 return $terms[0]->slug;
143 },
144 )
145 );
146
147 // Allow setting the is_wp_suggestion meta field, which partly determines if
148 // a template is a custom template.
149 register_post_meta(
150 'wp_template',
151 'is_wp_suggestion',
152 array(
153 'type' => 'boolean',
154 'show_in_rest' => true,
155 'single' => true,
156 )
157 );
158 }
159
160 // 3. We need a route to get that raw static templates from themes and plugins.
161 // I registered this as a post type route because right now the
162 // EditorProvider assumes templates are posts.
163 add_action( 'init', 'gutenberg_setup_static_template' );
164
165 function gutenberg_setup_static_template() {
166 register_setting(
167 'reading',
168 'active_templates',
169 array(
170 'type' => 'object',
171 // Do not set the default value to an empty array! For some reason
172 // that will prevent the option from being set to an empty array.
173 'show_in_rest' => array(
174 'schema' => array(
175 'type' => 'object',
176 // Properties can be integers, strings, or false
177 // (deactivated).
178 'additionalProperties' => true,
179 ),
180 ),
181 'label' => 'Active Templates',
182 )
183 );
184 }
185
186 add_filter( 'pre_wp_unique_post_slug', 'gutenberg_allow_template_slugs_to_be_duplicated', 10, 5 );
187 function gutenberg_allow_template_slugs_to_be_duplicated( $override, $slug, $post_id, $post_status, $post_type ) {
188 // Check active_templates experiment status.
189 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
190 return $override;
191 }
192 return 'wp_template' === $post_type ? $slug : $override;
193 }
194
195 function gutenberg_get_registered_block_templates( $query ) {
196 $template_files = _get_block_templates_files( 'wp_template', $query );
197 $query_result = array();
198
199 // _get_block_templates_files seems broken in core, it does not object the
200 // query.
201 if ( isset( $query['slug__in'] ) && is_array( $query['slug__in'] ) ) {
202 $template_files = array_filter(
203 $template_files,
204 function ( $template_file ) use ( $query ) {
205 return in_array( $template_file['slug'], $query['slug__in'], true );
206 }
207 );
208 }
209
210 foreach ( $template_files as $template_file ) {
211 $query_result[] = _build_block_template_result_from_file( $template_file, 'wp_template' );
212 }
213
214 // Add templates registered in the template registry. Filtering out the ones which have a theme file.
215 $registered_templates = WP_Block_Templates_Registry::get_instance()->get_by_query( $query );
216 $matching_registered_templates = array_filter(
217 $registered_templates,
218 function ( $registered_template ) use ( $template_files ) {
219 foreach ( $template_files as $template_file ) {
220 if ( $template_file['slug'] === $registered_template->slug ) {
221 return false;
222 }
223 }
224 return true;
225 }
226 );
227
228 $query_result = array_merge( $query_result, $matching_registered_templates );
229
230 /**
231 * Filters the array of queried block templates array after they've been fetched.
232 *
233 * @since 5.9.0
234 *
235 * @param WP_Block_Template[] $query_result Array of found block templates.
236 * @param array $query {
237 * Arguments to retrieve templates. All arguments are optional.
238 *
239 * @type string[] $slug__in List of slugs to include.
240 * @type int $wp_id Post ID of customized template.
241 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only).
242 * @type string $post_type Post type to get the templates for.
243 * }
244 * @param string $template_type wp_template or wp_template_part.
245 */
246 return apply_filters( 'get_block_templates', $query_result, $query, 'wp_template' );
247 }
248
249 $template_types = array(
250 'index',
251 '404',
252 'search',
253 'frontpage',
254 'home',
255 'privacypolicy',
256 'taxonomy',
257 'attachment',
258 'single',
259 'page',
260 'singular',
261 'category',
262 'tag',
263 'author',
264 'date',
265 'archive',
266 );
267
268 // Unfortunately, there is no general filter.
269 foreach ( $template_types as $template_type ) {
270 add_filter( "{$template_type}_template", 'gutenberg_get_template', 0, 3 );
271 }
272
273 function gutenberg_get_template( $template, $type, $templates ) {
274 // Check active_templates experiment status.
275 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
276 return $template;
277 }
278 $template = locate_template( $templates );
279 return gutenberg_locate_block_template( $template, $type, $templates );
280 }
281
282 ///////////////////////////////////////////////////////////////////////
283 // This function is a copy of core's, except for the marked section. //
284 ///////////////////////////////////////////////////////////////////////
285 /**
286 * Returns the correct 'wp_template' to render for the request template type.
287 *
288 * @access private
289 * @since 5.8.0
290 * @since 5.9.0 Added the `$fallback_template` parameter.
291 *
292 * @param string $template_type The current template type.
293 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority.
294 * @param string $fallback_template A PHP fallback template to use if no matching block template is found.
295 * @return WP_Block_Template|null template A template object, or null if none could be found.
296 */
297 function gutenberg_resolve_block_template( $template_type, $template_hierarchy, $fallback_template ) {
298 if ( ! $template_type ) {
299 return null;
300 }
301
302 if ( empty( $template_hierarchy ) ) {
303 $template_hierarchy = array( $template_type );
304 }
305
306 $slugs = array_map(
307 '_strip_template_file_suffix',
308 $template_hierarchy
309 );
310
311 //////////////////////////////
312 // START CORE MODIFICATIONS //
313 //////////////////////////////
314
315 $object_id = get_queried_object_id();
316 $specific_template = $object_id ? get_page_template_slug( $object_id ) : null;
317 $active_templates = (array) get_option( 'active_templates', array() );
318
319 // We expect one template for each slug. Use the active template if it is
320 // set and exists. Otherwise use the static template.
321 $templates = array();
322 $remaining_slugs = array();
323
324 foreach ( $slugs as $slug ) {
325 if ( $slug === $specific_template || empty( $active_templates[ $slug ] ) ) {
326 $remaining_slugs[] = $slug;
327 continue;
328 }
329
330 // TODO: it need to be possible to set a static template as active.
331 $post = get_post( $active_templates[ $slug ] );
332 if ( ! $post || 'publish' !== $post->post_status ) {
333 $remaining_slugs[] = $slug;
334 continue;
335 }
336
337 $template = _build_block_template_result_from_post( $post );
338
339 // Ensure the active templates are associated with the active theme.
340 // See _build_block_template_object_from_post_object.
341 if ( get_stylesheet() !== $template->theme ) {
342 $remaining_slugs[] = $slug;
343 continue;
344 }
345
346 $templates[] = $template;
347 }
348
349 // Apply the filter to the active templates for backward compatibility.
350 if ( ! empty( $templates ) ) {
351 $templates = apply_filters(
352 'get_block_templates',
353 $templates,
354 array(
355 'slug__in' => array_map(
356 function ( $template ) {
357 return $template->slug;
358 },
359 $templates
360 ),
361 ),
362 'wp_template'
363 );
364 }
365
366 // For any remaining slugs, use the static template.
367 if ( ! empty( $remaining_slugs ) ) {
368 $query = array(
369 'slug__in' => $remaining_slugs,
370 );
371 $templates = array_merge( $templates, gutenberg_get_registered_block_templates( $query ) );
372 }
373
374 if ( $specific_template && in_array( $specific_template, $remaining_slugs, true ) ) {
375 $templates = array_merge( $templates, get_block_templates( array( 'slug__in' => array( $specific_template ) ) ) );
376 }
377
378 ////////////////////////////
379 // END CORE MODIFICATIONS //
380 ////////////////////////////
381
382 // Order these templates per slug priority.
383 // Build map of template slugs to their priority in the current hierarchy.
384 $slug_priorities = array_flip( $slugs );
385
386 usort(
387 $templates,
388 static function ( $template_a, $template_b ) use ( $slug_priorities ) {
389 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ];
390 }
391 );
392
393 $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
394 $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
395
396 // Is the active theme a child theme, and is the PHP fallback template part of it?
397 if (
398 str_starts_with( $fallback_template, $theme_base_path ) &&
399 ! str_contains( $fallback_template, $parent_theme_base_path )
400 ) {
401 $fallback_template_slug = substr(
402 $fallback_template,
403 // Starting position of slug.
404 strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ),
405 // Remove '.php' suffix.
406 -4
407 );
408
409 // Is our candidate block template's slug identical to our PHP fallback template's?
410 if (
411 count( $templates ) &&
412 $fallback_template_slug === $templates[0]->slug &&
413 'theme' === $templates[0]->source
414 ) {
415 // Unfortunately, we cannot trust $templates[0]->theme, since it will always
416 // be set to the active theme's slug by _build_block_template_result_from_file(),
417 // even if the block template is really coming from the active theme's parent.
418 // (The reason for this is that we want it to be associated with the active theme
419 // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
420 // Instead, we use _get_block_template_file() to locate the block template file.
421 $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug );
422 if ( $template_file && get_template() === $template_file['theme'] ) {
423 // The block template is part of the parent theme, so we
424 // have to give precedence to the child theme's PHP template.
425 array_shift( $templates );
426 }
427 }
428 }
429
430 return count( $templates ) ? $templates[0] : null;
431 }
432
433 ///////////////////////////////////////////////////////////////////////
434 // This function is a copy of core's, except for the marked section. //
435 ///////////////////////////////////////////////////////////////////////
436 /**
437 * Finds a block template with equal or higher specificity than a given PHP template file.
438 *
439 * Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
440 *
441 * @since 5.8.0
442 * @since 6.3.0 Added `$_wp_current_template_id` global for editing of current template directly from the admin bar.
443 *
444 * @global string $_wp_current_template_content
445 * @global string $_wp_current_template_id
446 *
447 * @param string $template Path to the template. See locate_template().
448 * @param string $type Sanitized filename without extension.
449 * @param string[] $templates A list of template candidates, in descending order of priority.
450 * @return string The path to the Site Editor template canvas file, or the fallback PHP template.
451 */
452 function gutenberg_locate_block_template( $template, $type, array $templates ) {
453 global $_wp_current_template_content, $_wp_current_template_id;
454
455 // Check active_templates experiment status.
456 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
457 return $template;
458 }
459
460 if ( ! current_theme_supports( 'block-templates' ) ) {
461 return $template;
462 }
463
464 if ( $template ) {
465 /*
466 * locate_template() has found a PHP template at the path specified by $template.
467 * That means that we have a fallback candidate if we cannot find a block template
468 * with higher specificity.
469 *
470 * Thus, before looking for matching block themes, we shorten our list of candidate
471 * templates accordingly.
472 */
473
474 // Locate the index of $template (without the theme directory path) in $templates.
475 $relative_template_path = str_replace(
476 array( get_stylesheet_directory() . '/', get_template_directory() . '/' ),
477 '',
478 $template
479 );
480 $index = array_search( $relative_template_path, $templates, true );
481
482 // If the template hierarchy algorithm has successfully located a PHP template file,
483 // we will only consider block templates with higher or equal specificity.
484 $templates = array_slice( $templates, 0, $index + 1 );
485 }
486
487 //////////////////////////////
488 // START CORE MODIFICATIONS //
489 //////////////////////////////
490 $block_template = gutenberg_resolve_block_template( $type, $templates, $template );
491 ////////////////////////////
492 // END CORE MODIFICATIONS //
493 ////////////////////////////
494
495 if ( $block_template ) {
496 $_wp_current_template_id = $block_template->id;
497
498 if ( empty( $block_template->content ) ) {
499 if ( is_user_logged_in() ) {
500 $_wp_current_template_content = wp_render_empty_block_template_warning( $block_template );
501 } else {
502 if ( $block_template->has_theme_file ) {
503 // Show contents from theme template if user is not logged in.
504 $theme_template = _get_block_template_file( 'wp_template', $block_template->slug );
505 $_wp_current_template_content = file_get_contents( $theme_template['path'] );
506 } else {
507 $_wp_current_template_content = $block_template->content;
508 }
509 }
510 } elseif ( ! empty( $block_template->content ) ) {
511 $_wp_current_template_content = $block_template->content;
512 }
513 if ( isset( $_GET['_wp-find-template'] ) ) {
514 wp_send_json_success( $block_template );
515 }
516 } else {
517 if ( $template ) {
518 return $template;
519 }
520
521 if ( 'index' === $type ) {
522 if ( isset( $_GET['_wp-find-template'] ) ) {
523 wp_send_json_error( array( 'message' => __( 'No matching template found.' ) ) );
524 }
525 } else {
526 return ''; // So that the template loader keeps looking for templates.
527 }
528 }
529
530 // Add hooks for template canvas.
531 // Add viewport meta tag.
532 add_action( 'wp_head', '_block_template_viewport_meta_tag', 0 );
533
534 // Render title tag with content, regardless of whether theme has title-tag support.
535 remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering...
536 add_action( 'wp_head', '_block_template_render_title_tag', 1 ); // ...and make it unconditional.
537
538 // This file will be included instead of the theme's template file.
539 return ABSPATH . WPINC . '/template-canvas.php';
540 }
541
542 // We need to set the theme for the template when it's created. See:
543 // https://github.com/WordPress/wordpress-develop/blob/b2c8d8d2c8754cab5286b06efb4c11e2b6aa92d5/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php#L571-L578
544 // Priority 9 so it runs before default hooks like
545 // `inject_ignored_hooked_blocks_metadata_attributes`.
546 remove_action( 'rest_pre_insert_wp_template', 'wp_assign_new_template_to_theme', 9 );
547 add_action( 'rest_pre_insert_wp_template', 'gutenberg_set_active_template_theme', 9, 2 );
548 function gutenberg_set_active_template_theme( $changes, $request ) {
549 // Check active_templates experiment status.
550 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
551 return $changes;
552 }
553 if ( str_starts_with( $request->get_route(), '/wp/v2/templates' ) ) {
554 return $changes;
555 }
556 $changes->tax_input = array(
557 'wp_theme' => $request['theme'] ?? get_stylesheet(),
558 );
559 // All new templates saved will receive meta so we can distinguish between
560 // templates created the old way as edits and templates created the new way.
561 $changes->meta_input = array(
562 'is_inactive_by_default' => true,
563 );
564 return $changes;
565 }
566
567 add_action( 'save_post_wp_template', 'gutenberg_maybe_update_active_templates' );
568 function gutenberg_maybe_update_active_templates( $post_id ) {
569 // Check active_templates experiment status.
570 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
571 return;
572 }
573 $post = get_post( $post_id );
574 $is_inactive_by_default = get_post_meta( $post_id, 'is_inactive_by_default', true );
575 if ( $is_inactive_by_default ) {
576 return;
577 }
578 $active_templates = get_option( 'active_templates', array() );
579 $active_templates[ $post->post_name ] = $post->ID;
580 update_option( 'active_templates', $active_templates );
581 }
582
583 add_action( 'pre_get_block_template', 'gutenberg_filter_pre_get_block_template', 10, 3 );
584
585 ///////////////////////////////////////////////////////////////////////
586 // This function is a copy of core's, except for the marked section. //
587 ///////////////////////////////////////////////////////////////////////
588 function gutenberg_filter_pre_get_block_template( $output, $id, $template_type ) {
589 // Check active_templates experiment status.
590 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
591 return $output;
592 }
593 if ( 'wp_template' !== $template_type ) {
594 return $output;
595 }
596 $parts = explode( '//', $id, 2 );
597 if ( count( $parts ) < 2 ) {
598 return null;
599 }
600 list( $theme, $slug ) = $parts;
601
602 //////////////////////////////
603 // START CORE MODIFICATIONS //
604 //////////////////////////////
605 $active_templates = get_option( 'active_templates', array() );
606
607 if ( ! empty( $active_templates[ $slug ] ) ) {
608 if ( is_int( $active_templates[ $slug ] ) ) {
609 $post = get_post( $active_templates[ $slug ] );
610 if ( $post && 'publish' === $post->post_status ) {
611 $template = _build_block_template_result_from_post( $post );
612
613 if ( ! is_wp_error( $template ) && $theme === $template->theme ) {
614 return $template;
615 }
616 }
617 }
618 }
619 ////////////////////////////
620 // END CORE MODIFICATIONS //
621 ////////////////////////////
622
623 $wp_query_args = array(
624 'post_name__in' => array( $slug ),
625 'post_type' => $template_type,
626 'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
627 'posts_per_page' => 1,
628 'no_found_rows' => true,
629 'tax_query' => array(
630 array(
631 'taxonomy' => 'wp_theme',
632 'field' => 'name',
633 'terms' => $theme,
634 ),
635 ),
636 );
637 $template_query = new WP_Query( $wp_query_args );
638 $posts = $template_query->posts;
639
640 if ( count( $posts ) > 0 ) {
641 $template = _build_block_template_result_from_post( $posts[0] );
642
643 //////////////////////////////
644 // START CORE MODIFICATIONS //
645 //////////////////////////////
646 // Custom templates don't need to be activated, so if it's a custom
647 // template, return it.
648 if ( ! is_wp_error( $template ) && $template->is_custom ) {
649 return $template;
650 }
651 ////////////////////////////
652 // END CORE MODIFICATIONS //
653 ////////////////////////////
654 }
655
656 $block_template = get_block_file_template( $id, $template_type );
657
658 /**
659 * Filters the queried block template object after it's been fetched.
660 *
661 * @since 5.9.0
662 *
663 * @param WP_Block_Template|null $block_template The found block template, or null if there isn't one.
664 * @param string $id Template unique identifier (example: 'theme_slug//template_slug').
665 * @param string $template_type Template type. Either 'wp_template' or 'wp_template_part'.
666 */
667 return apply_filters( 'get_block_template', $block_template, $id, $template_type );
668 }
669
670 add_action( 'pre_get_block_templates', 'gutenberg_get_block_templates', 10, 3 );
671
672 ///////////////////////////////////////////////////////////////////////
673 // This function is a copy of core's, except for the marked section. //
674 ///////////////////////////////////////////////////////////////////////
675 function gutenberg_get_block_templates( $output, $query = array(), $template_type = 'wp_template' ) {
676 // Check active_templates experiment status.
677 if ( ! gutenberg_is_experiment_enabled( 'active_templates' ) ) {
678 return $output;
679 }
680 if ( 'wp_template' !== $template_type ) {
681 return $output;
682 }
683
684 $post_type = $query['post_type'] ?? '';
685 $wp_query_args = array(
686 'post_status' => array( 'auto-draft', 'draft', 'publish' ),
687 'post_type' => $template_type,
688 'posts_per_page' => -1,
689 'no_found_rows' => true,
690 'lazy_load_term_meta' => false,
691 'tax_query' => array(
692 array(
693 'taxonomy' => 'wp_theme',
694 'field' => 'name',
695 'terms' => get_stylesheet(),
696 ),
697 ),
698 );
699
700 if ( ! empty( $query['slug__in'] ) ) {
701 $wp_query_args['post_name__in'] = $query['slug__in'];
702 $wp_query_args['posts_per_page'] = count( array_unique( $query['slug__in'] ) );
703 }
704
705 // This is only needed for the regular templates/template parts post type listing and editor.
706 if ( isset( $query['wp_id'] ) ) {
707 $wp_query_args['p'] = $query['wp_id'];
708 } else {
709 $wp_query_args['post_status'] = 'publish';
710 }
711
712 //////////////////////////////
713 // START CORE MODIFICATIONS //
714 //////////////////////////////
715 $active_templates = get_option( 'active_templates', array() );
716 ////////////////////////////
717 // END CORE MODIFICATIONS //
718 ////////////////////////////
719
720 $template_query = new WP_Query( $wp_query_args );
721 $query_result = array();
722 foreach ( $template_query->posts as $post ) {
723 $template = _build_block_template_result_from_post( $post );
724
725 if ( is_wp_error( $template ) ) {
726 continue;
727 }
728
729 if ( $post_type && ! $template->is_custom ) {
730 continue;
731 }
732
733 if (
734 $post_type &&
735 isset( $template->post_types ) &&
736 ! in_array( $post_type, $template->post_types, true )
737 ) {
738 continue;
739 }
740
741 //////////////////////////////
742 // START CORE MODIFICATIONS //
743 //////////////////////////////
744 if ( $template->is_custom || isset( $query['wp_id'] ) ) {
745 // Custom templates don't need to be activated, leave them be.
746 // Also don't filter out templates when querying by wp_id.
747 $query_result[] = $template;
748 } elseif ( isset( $active_templates[ $template->slug ] ) && $active_templates[ $template->slug ] === $post->ID ) {
749 // Only include active templates.
750 $query_result[] = $template;
751 }
752 ////////////////////////////
753 // END CORE MODIFICATIONS //
754 ////////////////////////////
755 }
756
757 if ( ! isset( $query['wp_id'] ) ) {
758 /*
759 * If the query has found some user templates, those have priority
760 * over the theme-provided ones, so we skip querying and building them.
761 */
762 $query['slug__not_in'] = wp_list_pluck( $query_result, 'slug' );
763 /*
764 * We need to unset the post_type query param because some templates
765 * would be excluded otherwise, like `page.html` when looking for
766 * `page` templates. We need all templates so we can exclude duplicates
767 * from plugin-registered templates.
768 * See: https://github.com/WordPress/gutenberg/issues/65584
769 */
770 $template_files_query = $query;
771 unset( $template_files_query['post_type'] );
772 $template_files = _get_block_templates_files( $template_type, $template_files_query );
773 foreach ( $template_files as $template_file ) {
774 // If the query doesn't specify a post type, or it does and the template matches the post type, add it.
775 if (
776 ! isset( $query['post_type'] ) ||
777 (
778 isset( $template_file['postTypes'] ) &&
779 in_array( $query['post_type'], $template_file['postTypes'], true )
780 )
781 ) {
782 $query_result[] = _build_block_template_result_from_file( $template_file, $template_type );
783 } elseif ( ! isset( $template_file['postTypes'] ) ) {
784 // The custom templates with no associated post types are available for all post types as long
785 // as they are not default templates.
786 $candidate = _build_block_template_result_from_file( $template_file, $template_type );
787 $default_template_types = get_default_block_template_types();
788 if ( ! isset( $default_template_types[ $candidate->slug ] ) ) {
789 $query_result[] = $candidate;
790 }
791 }
792 }
793
794 if ( 'wp_template' === $template_type ) {
795 // Add templates registered in the template registry. Filtering out the ones which have a theme file.
796 $registered_templates = WP_Block_Templates_Registry::get_instance()->get_by_query( $query );
797 $matching_registered_templates = array_filter(
798 $registered_templates,
799 function ( $registered_template ) use ( $template_files ) {
800 foreach ( $template_files as $template_file ) {
801 if ( $template_file['slug'] === $registered_template->slug ) {
802 return false;
803 }
804 }
805 return true;
806 }
807 );
808
809 $matching_registered_templates = array_map(
810 function ( $template ) {
811 $template->content = apply_block_hooks_to_content(
812 $template->content,
813 $template,
814 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata'
815 );
816 return $template;
817 },
818 $matching_registered_templates
819 );
820
821 $query_result = array_merge( $query_result, $matching_registered_templates );
822 }
823 }
824
825 /**
826 * Filters the array of queried block templates array after they've been fetched.
827 *
828 * @since 5.9.0
829 *
830 * @param WP_Block_Template[] $query_result Array of found block templates.
831 * @param array $query {
832 * Arguments to retrieve templates. All arguments are optional.
833 *
834 * @type string[] $slug__in List of slugs to include.
835 * @type int $wp_id Post ID of customized template.
836 * @type string $area A 'wp_template_part_area' taxonomy value to filter by (for 'wp_template_part' template type only).
837 * @type string $post_type Post type to get the templates for.
838 * }
839 * @param string $template_type wp_template or wp_template_part.
840 */
841 return apply_filters( 'get_block_templates', $query_result, $query, $template_type );
842 }
843