From 71bc8d081c096b5666ff2ca236866687f6936e16 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Thu, 2 Dec 2021 19:57:53 +0100 Subject: [PATCH] FSE: Fix template resolution to give precedence to child theme PHP templates over parent theme block templates with equal specificity --- lib/full-site-editing/template-loader.php | 44 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/full-site-editing/template-loader.php b/lib/full-site-editing/template-loader.php index 40953108f37ec5..9d6e1b2475418d 100644 --- a/lib/full-site-editing/template-loader.php +++ b/lib/full-site-editing/template-loader.php @@ -61,7 +61,7 @@ function gutenberg_override_query_template( $template, $type, array $templates ) $templates = array_slice( $templates, 0, $index + 1 ); } - $block_template = gutenberg_resolve_template( $type, $templates ); + $block_template = gutenberg_resolve_template( $type, $templates, $template ); if ( $block_template ) { if ( empty( $block_template->content ) && is_user_logged_in() ) { @@ -109,11 +109,14 @@ function gutenberg_override_query_template( $template, $type, array $templates ) * * Accepts an optional $template_hierarchy argument as a hint. * + * @since 5.9.0 Added the `$fallback_template` parameter. + * * @param string $template_type The current template type. * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority. + * @param string $fallback_template A PHP fallback template to use if no matching block template is found. * @return null|Gutenberg_Block_Template A block template if found. Null if not. */ -function gutenberg_resolve_template( $template_type, $template_hierarchy ) { +function gutenberg_resolve_template( $template_type, $template_hierarchy, $fallback_template ) { if ( ! $template_type ) { return null; } @@ -145,6 +148,43 @@ function ( $template_a, $template_b ) use ( $slug_priorities ) { } ); + $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR; + $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR; + + // Is the current theme a child theme, and is the PHP fallback template part of it? + if ( + strpos( $fallback_template, $theme_base_path ) === 0 && + strpos( $fallback_template, $parent_theme_base_path ) === false + ) { + $fallback_template_slug = substr( + $fallback_template, + // Starting position of slug. + strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ), + // Remove '.php' suffix. + -4 + ); + + // Is our candidate block template's slug identical to our PHP fallback template's? + if ( + count( $templates ) && + $fallback_template_slug === $templates[0]->slug && + 'theme' === $templates[0]->source + ) { + // Unfortunately, we cannot trust $templates[0]->theme, since it will always + // be set to the current theme's slug by _build_block_template_result_from_file(), + // even if the block template is really coming from the current theme's parent. + // (The reason for this is that we want it to be associated with the current theme + // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.) + // Instead, we use _get_block_template_file() to locate the block template file. + $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug ); + if ( $template_file && get_template() === $template_file['theme'] ) { + // The block template is part of the parent theme, so we + // have to give precedence to the child theme's PHP template. + array_shift( $templates ); + } + } + } + return count( $templates ) ? $templates[0] : null; }