Fix reverting customisations by refactoring pre_get_block_file_template (https://github.com/woocommerce/woocommerce-blocks/pull/5746)

* Refactor maybe_return_blocks_template to get_woocommerce_block_file_template

* Remove unused method and rename get_woocommerce_block_file_template to get_block_file_template

* Account for product-archive template fallbacks

* Fix bug with clearing customizations of templates using the deprecated plugin ID
This commit is contained in:
Tom Cafferkey 2022-02-10 14:15:09 +00:00 committed by GitHub
parent 0a8fe0a0c6
commit d178761252
2 changed files with 59 additions and 91 deletions

View File

@ -37,8 +37,7 @@ class BlockTemplatesController {
public function __construct() {
// This feature is gated for WooCommerce versions 6.0.0 and above.
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '6.0.0', '>=' ) ) {
$root_path = plugin_dir_path( __DIR__ ) . self::TEMPLATES_ROOT_DIR . DIRECTORY_SEPARATOR;
$root_path = plugin_dir_path( __DIR__ ) . self::TEMPLATES_ROOT_DIR . DIRECTORY_SEPARATOR;
$this->templates_directory = $root_path . BlockTemplateUtils::DIRECTORY_NAMES['TEMPLATES'];
$this->template_parts_directory = $root_path . BlockTemplateUtils::DIRECTORY_NAMES['TEMPLATE_PARTS'];
$this->init();
@ -50,110 +49,74 @@ class BlockTemplatesController {
*/
protected function init() {
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
add_filter( 'pre_get_block_file_template', array( $this, 'maybe_return_blocks_template' ), 10, 3 );
add_filter( 'pre_get_block_file_template', array( $this, 'get_block_file_template' ), 10, 3 );
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
}
/**
* This function checks if there's a blocks template (ultimately it resolves either a saved blocks template from the
* database or a template file in `woo-gutenberg-products-block/templates/templates/`)
* This function checks if there's a block template file in `woo-gutenberg-products-block/templates/templates/`
* to return to pre_get_posts short-circuiting the query in Gutenberg.
*
* @param \WP_Block_Template|null $template Return a block template object to short-circuit the default query,
* or null to allow WP to run its normal queries.
* @param string $id Template unique identifier (example: theme_slug//template_slug).
* @param array $template_type wp_template or wp_template_part.
* @param string $template_type wp_template or wp_template_part.
*
* @return mixed|\WP_Block_Template|\WP_Error
*/
public function maybe_return_blocks_template( $template, $id, $template_type ) {
// 'get_block_template' was introduced in WP 5.9. We need to support
// 'gutenberg_get_block_template' for previous versions of WP with
// Gutenberg enabled.
if (
! function_exists( 'gutenberg_get_block_template' ) &&
! function_exists( 'get_block_template' )
) {
return $template;
}
public function get_block_file_template( $template, $id, $template_type ) {
$template_name_parts = explode( '//', $id );
if ( count( $template_name_parts ) < 2 ) {
return $template;
}
list( , $slug ) = $template_name_parts;
// Remove the filter at this point because if we don't then this function will infinite loop.
remove_filter( 'pre_get_block_file_template', array( $this, 'maybe_return_blocks_template' ), 10, 3 );
list( $template_id, $template_slug ) = $template_name_parts;
// Check if the theme has a saved version of this template before falling back to the woo one. Please note how
// the slug has not been modified at this point, we're still using the default one passed to this hook.
$maybe_template = function_exists( 'gutenberg_get_block_template' ) ?
gutenberg_get_block_template( $id, $template_type ) :
get_block_template( $id, $template_type );
if ( null !== $maybe_template ) {
add_filter( 'pre_get_block_file_template', array( $this, 'maybe_return_blocks_template' ), 10, 3 );
return $maybe_template;
// If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag.html template let's use the themes archive-product.html template.
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) {
$template_path = BlockTemplateUtils::get_theme_template_path( 'archive-product' );
$template_object = BlockTemplateUtils::create_new_block_template_object( $template_path, $template_type, $template_slug, true );
return BlockTemplateUtils::build_template_result_from_file( $template_object, $template_type );
}
// Theme-based template didn't exist, try switching the theme to woocommerce and try again. This function has
// been unhooked so won't run again.
add_filter( 'get_block_file_template', array( $this, 'get_single_block_template' ), 10, 3 );
$maybe_template = function_exists( 'gutenberg_get_block_template' ) ?
gutenberg_get_block_template( BlockTemplateUtils::PLUGIN_SLUG . '//' . $slug, $template_type ) :
get_block_template( BlockTemplateUtils::PLUGIN_SLUG . '//' . $slug, $template_type );
// This is a real edge-case, we are supporting users who have saved templates under the deprecated slug. See its definition for more information.
// You can likely ignore this code unless you're supporting/debugging early customised templates.
if ( BlockTemplateUtils::DEPRECATED_PLUGIN_SLUG === strtolower( $template_id ) ) {
// Because we are using get_block_templates we have to unhook this method to prevent a recursive loop where this filter is applied.
remove_filter( 'pre_get_block_file_template', array( $this, 'get_block_file_template' ), 10, 3 );
$template_with_deprecated_id = BlockTemplateUtils::get_block_template( $id, $template_type );
// Let's hook this method back now that we have used the function.
add_filter( 'pre_get_block_file_template', array( $this, 'get_block_file_template' ), 10, 3 );
// Re-hook this function, it was only unhooked to stop recursion.
add_filter( 'pre_get_block_file_template', array( $this, 'maybe_return_blocks_template' ), 10, 3 );
remove_filter( 'get_block_file_template', array( $this, 'get_single_block_template' ), 10, 3 );
if ( null !== $maybe_template ) {
return $maybe_template;
if ( null !== $template_with_deprecated_id ) {
return $template_with_deprecated_id;
}
}
// At this point we haven't had any luck finding a template. Give up and let Gutenberg take control again.
// If we are not dealing with a WooCommerce template let's return early and let it continue through the process.
if ( BlockTemplateUtils::PLUGIN_SLUG !== $template_id ) {
return $template;
}
// If we don't have a template let Gutenberg do its thing.
if ( ! $this->block_template_is_available( $template_slug, $template_type ) ) {
return $template;
}
$directory = $this->get_templates_directory( $template_type );
$template_file_path = $directory . '/' . $template_slug . '.html';
$template_object = BlockTemplateUtils::create_new_block_template_object( $template_file_path, $template_type, $template_slug );
$template_built = BlockTemplateUtils::build_template_result_from_file( $template_object, $template_type );
if ( null !== $template_built ) {
return $template_built;
}
// Hand back over to Gutenberg if we can't find a template.
return $template;
}
/**
* Runs on the get_block_template hook. If a template is already found and passed to this function, then return it
* and don't run.
* If a template is *not* passed, try to look for one that matches the ID in the database, if that's not found defer
* to Blocks templates files. Priority goes: DB-Theme, DB-Blocks, Filesystem-Theme, Filesystem-Blocks.
*
* @param \WP_Block_Template $template The found block template.
* @param string $id Template unique identifier (example: theme_slug//template_slug).
* @param array $template_type wp_template or wp_template_part.
*
* @return mixed|null
*/
public function get_single_block_template( $template, $id, $template_type ) {
// The template was already found before the filter runs, just return it immediately.
if ( null !== $template ) {
return $template;
}
$template_name_parts = explode( '//', $id );
if ( count( $template_name_parts ) < 2 ) {
return $template;
}
list( , $slug ) = $template_name_parts;
// If this blocks template doesn't exist then we should just skip the function and let Gutenberg handle it.
if ( ! $this->block_template_is_available( $slug, $template_type ) ) {
return $template;
}
$available_templates = $this->get_block_templates_from_woocommerce(
array( $slug ),
$this->get_block_templates_from_db( array( $slug ), $template_type ),
$template_type
);
return ( is_array( $available_templates ) && count( $available_templates ) > 0 )
? BlockTemplateUtils::gutenberg_build_template_result_from_file( $available_templates[0], $available_templates[0]->type )
: $template;
}
/**
* Add the block template objects to be used.
*
@ -194,7 +157,7 @@ class BlockTemplatesController {
// It would be custom if the template was modified in the editor, so if it's not custom we can load it from
// the filesystem.
if ( 'custom' !== $template_file->source ) {
$template = BlockTemplateUtils::gutenberg_build_template_result_from_file( $template_file, $template_type );
$template = BlockTemplateUtils::build_template_result_from_file( $template_file, $template_type );
} else {
$template_file->title = BlockTemplateUtils::convert_slug_to_title( $template_file->slug );
$query_result[] = $template_file;
@ -269,11 +232,6 @@ class BlockTemplatesController {
* @return int[]|\WP_Post[] An array of found templates.
*/
public function get_block_templates_from_db( $slugs = array(), $template_type = 'wp_template' ) {
// This was the previously incorrect slug used to save DB templates against.
// To maintain compatibility with users sites who have already customised WooCommerce block templates using this slug we have to still use it to query those.
// More context found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423.
$invalid_plugin_slug = 'woocommerce';
$check_query_args = array(
'post_type' => $template_type,
'posts_per_page' => -1,
@ -282,7 +240,7 @@ class BlockTemplatesController {
array(
'taxonomy' => 'wp_theme',
'field' => 'name',
'terms' => array( $invalid_plugin_slug, BlockTemplateUtils::PLUGIN_SLUG, get_stylesheet() ),
'terms' => array( BlockTemplateUtils::DEPRECATED_PLUGIN_SLUG, BlockTemplateUtils::PLUGIN_SLUG, get_stylesheet() ),
),
),
);

View File

@ -39,6 +39,16 @@ class BlockTemplateUtils {
*/
const PLUGIN_SLUG = 'woocommerce/woocommerce';
/**
* Deprecated WooCommerce plugin slug
*
* For supporting users who have customized templates under the incorrect plugin slug during the first release.
* More context found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423.
*
* @var string
*/
const DEPRECATED_PLUGIN_SLUG = 'woocommerce';
/**
* Returns an array containing the references of
* the passed blocks and their inner blocks.
@ -153,7 +163,7 @@ class BlockTemplateUtils {
// We are checking 'woocommerce' to maintain legacy templates which are saved to the DB,
// prior to updating to use the correct slug.
// More information found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423.
if ( self::PLUGIN_SLUG === $theme || 'woocommerce' === strtolower( $theme ) ) {
if ( self::PLUGIN_SLUG === $theme || self::DEPRECATED_PLUGIN_SLUG === strtolower( $theme ) ) {
$template->origin = 'plugin';
}
@ -163,12 +173,12 @@ class BlockTemplateUtils {
/**
* Build a unified template object based on a theme file.
*
* @param array $template_file Theme file.
* @param array $template_type wp_template or wp_template_part.
* @param array|object $template_file Theme file.
* @param string $template_type wp_template or wp_template_part.
*
* @return \WP_Block_Template Template.
*/
public static function gutenberg_build_template_result_from_file( $template_file, $template_type ) {
public static function build_template_result_from_file( $template_file, $template_type ) {
$template_file = (object) $template_file;
// If the theme has an archive-products.html template but does not have product taxonomy templates