From 087058460f57f5d756d56def7ce3ddb26bd22297 Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Thu, 23 Dec 2021 13:27:31 +0100 Subject: [PATCH 1/5] Fix custom templates with fallback being incorrectly attributed Category and tags templates can fallback to the generic archive if, e.g., the theme provides one for the latter but not for the former. However, since Gutenberg is not aware of this fallback mechanism, it would incorrectly attribute the custom template to the user instead of the theme. Here we are explicitly setting the `has_theme_file` to make sure Gutenberg knows we do, in fact, have a theme fail (if not what it expects). Fixes #5441 --- src/BlockTemplatesController.php | 23 +++--------- src/Utils/BlockTemplateUtils.php | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 48fae793464..2103b628561 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -175,16 +175,10 @@ public function add_block_templates( $query_result, $query, $template_type ) { // @todo: Add apply_filters to _gutenberg_get_template_files() in Gutenberg to prevent duplication of logic. foreach ( $template_files as $template_file ) { - // Avoid adding the same template if it's already in the array of $query_result. - if ( - array_filter( - $query_result, - function( $query_result_template ) use ( $template_file ) { - return $query_result_template->slug === $template_file->slug && - $query_result_template->theme === $template_file->theme; - } - ) - ) { + // If we have a template which is eligible for a fallback, we need to explicitly tell Gutenberg that + // it has a theme file (because it is using the fallback template file). And then `continue` to avoid + // adding duplicates. + if ( BlockTemplateUtils::confirm_theme_file_when_fallback_is_available( $query_result, $template_file ) ) { continue; } @@ -348,14 +342,7 @@ function ( $template ) use ( $template_slug ) { } // If the theme has an archive-product.html template, but not a taxonomy-product_cat.html template let's use the themes archive-product.html template. - if ( 'taxonomy-product_cat' === $template_slug && ! BlockTemplateUtils::theme_has_template( 'taxonomy-product_cat' ) && BlockTemplateUtils::theme_has_template( 'archive-product' ) ) { - $template_file = get_stylesheet_directory() . '/' . self::TEMPLATES_DIR_NAME . '/archive-product.html'; - $templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true ); - continue; - } - - // If the theme has an archive-product.html template, but not a taxonomy-product_tag.html template let's use the themes archive-product.html template. - if ( 'taxonomy-product_tag' === $template_slug && ! BlockTemplateUtils::theme_has_template( 'taxonomy-product_tag' ) && BlockTemplateUtils::theme_has_template( 'archive-product' ) ) { + if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) { $template_file = get_stylesheet_directory() . '/' . self::TEMPLATES_DIR_NAME . '/archive-product.html'; $templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true ); continue; diff --git a/src/Utils/BlockTemplateUtils.php b/src/Utils/BlockTemplateUtils.php index 3ef2e3aa318..b2ca6ce990e 100644 --- a/src/Utils/BlockTemplateUtils.php +++ b/src/Utils/BlockTemplateUtils.php @@ -281,4 +281,68 @@ public static function supports_block_templates() { return true; } + + /** + * Checks if we can fallback to the `archive-product` template for a given slug + * + * `taxonomy-product_cat` and `taxonomy-product_tag` templates can generally use the + * `archive-product` as a fallback if there are no specific overrides. + * + * @param string $template_slug Slug to check for fallbacks. + * @return boolean + */ + public static function template_is_eligible_for_product_archive_fallback( $template_slug ) { + $eligible_for_fallbacks = array( 'taxonomy-product_cat', 'taxonomy-product_tag' ); + + if ( + in_array( $template_slug, $eligible_for_fallbacks, true ) + && ! self::theme_has_template( $template_slug ) + && self::theme_has_template( 'archive-product' ) + ) { + return true; + } + + return false; + } + + /** + * Sets the `has_theme_file` to `true` for templates with fallbacks + * + * There are cases (such as tags and categories) in which fallback templates + * can be used; so, while *technically* the theme doesn't have a specific file + * for them, it is important that we tell Gutenberg that we do, in fact, + * have a theme file (i.e. the fallback one). + * + * **Note:** this function changes the array that has been passed. + * + * It returns `true` if anything was changed, `false` otherwise. + * + * @param array $query_result Array of template objects. + * @param array $template A specific template object which could have a fallback. + * + * @return boolean + */ + public static function confirm_theme_file_when_fallback_is_available( $query_result, $template ) { + $template_with_fallback_idx = null; + + array_walk( + $query_result, + function( $query_result_template, $idx ) use ( $template, &$template_with_fallback_idx ) { + if ( + $query_result_template->slug === $template->slug + && $query_result_template->theme === $template->theme + && self::template_is_eligible_for_product_archive_fallback( $template->slug ) + ) { + $template_with_fallback_idx = $idx; + } + } + ); + + if ( is_int( $template_with_fallback_idx ) ) { + $query_result[ $template_with_fallback_idx ]->has_theme_file = true; + return true; + } + + return false; + } } From a03d9364533d72b6253dd527091905100a474487 Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Thu, 23 Dec 2021 14:41:58 +0100 Subject: [PATCH 2/5] Also skip the loop if template is duplicate but has no fallback --- src/Utils/BlockTemplateUtils.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Utils/BlockTemplateUtils.php b/src/Utils/BlockTemplateUtils.php index b2ca6ce990e..a802338895e 100644 --- a/src/Utils/BlockTemplateUtils.php +++ b/src/Utils/BlockTemplateUtils.php @@ -324,22 +324,29 @@ public static function template_is_eligible_for_product_archive_fallback( $templ */ public static function confirm_theme_file_when_fallback_is_available( $query_result, $template ) { $template_with_fallback_idx = null; + $is_duplicate = false; array_walk( $query_result, - function( $query_result_template, $idx ) use ( $template, &$template_with_fallback_idx ) { + function( $query_result_template, $idx ) use ( $template, &$template_with_fallback_idx, &$is_duplicate ) { if ( $query_result_template->slug === $template->slug && $query_result_template->theme === $template->theme - && self::template_is_eligible_for_product_archive_fallback( $template->slug ) ) { + $is_duplicate = true; + + if ( self::template_is_eligible_for_product_archive_fallback( $template->slug ) ) { $template_with_fallback_idx = $idx; + } } } ); - if ( is_int( $template_with_fallback_idx ) ) { - $query_result[ $template_with_fallback_idx ]->has_theme_file = true; + if ( $is_duplicate ) { + if ( is_int( $template_with_fallback_idx ) ) { + $query_result[ $template_with_fallback_idx ]->has_theme_file = true; + } + return true; } From 9d6fb1414137113626b9f4c5055af7e0ef56163e Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Thu, 23 Dec 2021 17:10:00 +0100 Subject: [PATCH 3/5] Address code review feedback --- src/BlockTemplatesController.php | 2 +- src/Utils/BlockTemplateUtils.php | 42 +++++++++----------------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/BlockTemplatesController.php b/src/BlockTemplatesController.php index 2103b628561..c8abf43ce5c 100644 --- a/src/BlockTemplatesController.php +++ b/src/BlockTemplatesController.php @@ -178,7 +178,7 @@ public function add_block_templates( $query_result, $query, $template_type ) { // If we have a template which is eligible for a fallback, we need to explicitly tell Gutenberg that // it has a theme file (because it is using the fallback template file). And then `continue` to avoid // adding duplicates. - if ( BlockTemplateUtils::confirm_theme_file_when_fallback_is_available( $query_result, $template_file ) ) { + if ( BlockTemplateUtils::set_has_theme_file_if_fallback_is_available( $query_result, $template_file ) ) { continue; } diff --git a/src/Utils/BlockTemplateUtils.php b/src/Utils/BlockTemplateUtils.php index a802338895e..6860124181f 100644 --- a/src/Utils/BlockTemplateUtils.php +++ b/src/Utils/BlockTemplateUtils.php @@ -294,15 +294,9 @@ public static function supports_block_templates() { public static function template_is_eligible_for_product_archive_fallback( $template_slug ) { $eligible_for_fallbacks = array( 'taxonomy-product_cat', 'taxonomy-product_tag' ); - if ( - in_array( $template_slug, $eligible_for_fallbacks, true ) + return in_array( $template_slug, $eligible_for_fallbacks, true ) && ! self::theme_has_template( $template_slug ) - && self::theme_has_template( 'archive-product' ) - ) { - return true; - } - - return false; + && self::theme_has_template( 'archive-product' ); } /** @@ -322,32 +316,18 @@ public static function template_is_eligible_for_product_archive_fallback( $templ * * @return boolean */ - public static function confirm_theme_file_when_fallback_is_available( $query_result, $template ) { - $template_with_fallback_idx = null; - $is_duplicate = false; - - array_walk( - $query_result, - function( $query_result_template, $idx ) use ( $template, &$template_with_fallback_idx, &$is_duplicate ) { - if ( - $query_result_template->slug === $template->slug - && $query_result_template->theme === $template->theme - ) { - $is_duplicate = true; - - if ( self::template_is_eligible_for_product_archive_fallback( $template->slug ) ) { - $template_with_fallback_idx = $idx; - } + public static function set_has_theme_file_if_fallback_is_available( $query_result, $template ) { + foreach ( $query_result as $i => &$query_result_template ) { + if ( + $query_result_template->slug === $template->slug + && $query_result_template->theme === $template->theme + ) { + if ( self::template_is_eligible_for_product_archive_fallback( $template->slug ) ) { + $query_result_template->has_theme_file = true; } - } - ); - if ( $is_duplicate ) { - if ( is_int( $template_with_fallback_idx ) ) { - $query_result[ $template_with_fallback_idx ]->has_theme_file = true; + return true; } - - return true; } return false; From a0a71f01432bdde611c60f6f67af5a5ffb68c8ee Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Thu, 23 Dec 2021 17:27:24 +0100 Subject: [PATCH 4/5] Remove unused variable --- src/Utils/BlockTemplateUtils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/BlockTemplateUtils.php b/src/Utils/BlockTemplateUtils.php index 6860124181f..92a617d44e0 100644 --- a/src/Utils/BlockTemplateUtils.php +++ b/src/Utils/BlockTemplateUtils.php @@ -317,7 +317,7 @@ public static function template_is_eligible_for_product_archive_fallback( $templ * @return boolean */ public static function set_has_theme_file_if_fallback_is_available( $query_result, $template ) { - foreach ( $query_result as $i => &$query_result_template ) { + foreach ( $query_result as $query_result_template ) { if ( $query_result_template->slug === $template->slug && $query_result_template->theme === $template->theme From ead8b241c665c708fd4a8ae7605c42afdfe5ef93 Mon Sep 17 00:00:00 2001 From: Lucio Giannotta Date: Thu, 23 Dec 2021 17:28:23 +0100 Subject: [PATCH 5/5] Pass by reference --- src/Utils/BlockTemplateUtils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/BlockTemplateUtils.php b/src/Utils/BlockTemplateUtils.php index 92a617d44e0..52ca21ffa48 100644 --- a/src/Utils/BlockTemplateUtils.php +++ b/src/Utils/BlockTemplateUtils.php @@ -317,7 +317,7 @@ public static function template_is_eligible_for_product_archive_fallback( $templ * @return boolean */ public static function set_has_theme_file_if_fallback_is_available( $query_result, $template ) { - foreach ( $query_result as $query_result_template ) { + foreach ( $query_result as &$query_result_template ) { if ( $query_result_template->slug === $template->slug && $query_result_template->theme === $template->theme