BlockTemplateUtils.php
<?php
namespace Automattic\WooCommerce\Blocks\Utils;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Blocks\Options;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\BlockTemplatesRegistry;
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
class BlockTemplateUtils {
const DIRECTORY_NAMES = array(
'DEPRECATED_TEMPLATES' => 'block-templates',
'DEPRECATED_TEMPLATE_PARTS' => 'block-template-parts',
'TEMPLATES' => 'templates',
'TEMPLATE_PARTS' => 'parts',
);
const TEMPLATES_ROOT_DIR = 'templates';
const PLUGIN_SLUG = 'woocommerce/woocommerce';
const DEPRECATED_PLUGIN_SLUG = 'woocommerce';
public static function get_template( $template_slug ) {
$block_templates_registry = Package::container()->get( BlockTemplatesRegistry::class );
return $block_templates_registry->get_template( $template_slug );
}
public static function flatten_blocks( &$blocks ) {
$all_blocks = array();
$queue = array();
foreach ( $blocks as &$block ) {
$queue[] = &$block;
}
$queue_count = count( $queue );
while ( $queue_count > 0 ) {
$block = &$queue[0];
array_shift( $queue );
$all_blocks[] = &$block;
if ( ! empty( $block['innerBlocks'] ) ) {
foreach ( $block['innerBlocks'] as &$inner_block ) {
$queue[] = &$inner_block;
}
}
$queue_count = count( $queue );
}
return $all_blocks;
}
public static function inject_theme_attribute_in_content( $template_content ) {
$has_updated_content = false;
$new_content = '';
$template_blocks = parse_blocks( $template_content );
$blocks = self::flatten_blocks( $template_blocks );
foreach ( $blocks as &$block ) {
if (
'core/template-part' === $block['blockName'] &&
! isset( $block['attrs']['theme'] )
) {
$block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
$has_updated_content = true;
}
}
if ( $has_updated_content ) {
foreach ( $template_blocks as &$block ) {
$new_content .= serialize_block( $block );
}
return $new_content;
}
return $template_content;
}
public static function build_template_result_from_post( $post ) {
$terms = get_the_terms( $post, 'wp_theme' );
if ( is_wp_error( $terms ) ) {
return $terms;
}
if ( ! $terms ) {
return new \WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'woocommerce' ) );
}
$theme = $terms[0]->name;
$has_theme_file = true;
$template = new \WP_Block_Template();
$template->wp_id = $post->ID;
$template->id = $theme . '//' . $post->post_name;
$template->theme = $theme;
$template->content = $post->post_content;
$template->slug = $post->post_name;
$template->source = 'custom';
$template->type = $post->post_type;
$template->description = $post->post_excerpt;
$template->title = $post->post_title;
$template->status = $post->post_status;
$template->has_theme_file = $has_theme_file;
$template->is_custom = false;
$template->post_types = array();
if ( 'wp_template_part' === $post->post_type ) {
$type_terms = get_the_terms( $post, 'wp_template_part_area' );
if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
$template->area = $type_terms[0]->name;
}
}
if ( self::PLUGIN_SLUG === $theme || self::DEPRECATED_PLUGIN_SLUG === strtolower( $theme ) ) {
$template->origin = 'plugin';
}
if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
$hooked_blocks = get_hooked_blocks();
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
$blocks = parse_blocks( $template->content );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
}
}
return $template;
}
public static function build_template_result_from_file( $template_file, $template_type ) {
$template_file = (object) $template_file;
$template_is_from_theme = 'theme' === $template_file->source;
$theme_name = wp_get_theme()->get( 'TextDomain' );
$template_content = file_get_contents( $template_file->path );
$template = new \WP_Block_Template();
$template->id = $template_is_from_theme ? $theme_name . '//' . $template_file->slug : self::PLUGIN_SLUG . '//' . $template_file->slug;
$template->theme = $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG;
$template->content = self::inject_theme_attribute_in_content( $template_content );
if ( ProductCatalogTemplate::SLUG === $template_file->slug ) {
$template->content = str_replace( '<!-- wp:term-description {"align":"wide"} /-->', '', $template->content );
}
$template->source = $template_file->source ? $template_file->source : 'plugin';
$template->slug = $template_file->slug;
$template->type = $template_type;
$template->title = ! empty( $template_file->title ) ? $template_file->title : self::get_block_template_title( $template_file->slug );
$template->description = ! empty( $template_file->description ) ? $template_file->description : self::get_block_template_description( $template_file->slug );
$template->status = 'publish';
$template->has_theme_file = true;
$template->origin = $template_file->source;
$template->is_custom = false;
$template->post_types = array();
$template->area = self::get_block_template_area( $template->slug, $template_type );
if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$after_block_visitor = null;
$hooked_blocks = get_hooked_blocks();
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
}
$blocks = parse_blocks( $template->content );
$template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
}
return $template;
}
public static function create_new_block_template_object( $template_file, $template_type, $template_slug, $template_is_from_theme = false ) {
$theme_name = wp_get_theme()->get( 'TextDomain' );
$new_template_item = array(
'slug' => $template_slug,
'id' => $template_is_from_theme ? $theme_name . '//' . $template_slug : self::PLUGIN_SLUG . '//' . $template_slug,
'path' => $template_file,
'type' => $template_type,
'theme' => $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG,
'source' => $template_is_from_theme ? 'theme' : 'plugin',
'title' => self::get_block_template_title( $template_slug ),
'description' => self::get_block_template_description( $template_slug ),
'post_types' => array(),
);
return (object) $new_template_item;
}
public static function get_template_paths( $template_type ) {
$wp_template_filenames = array(
'archive-product.html',
'order-confirmation.html',
'page-cart.html',
'page-checkout.html',
'product-search-results.html',
'single-product.html',
'taxonomy-product_attribute.html',
'taxonomy-product_cat.html',
'taxonomy-product_tag.html',
);
if ( Features::is_enabled( 'launch-your-store' ) ) {
$wp_template_filenames[] = 'coming-soon.html';
}
$wp_template_part_filenames = array(
'checkout-header.html',
'mini-cart.html',
'product-filters-overlay.html',
);
$directory = self::get_templates_directory( $template_type );
$path_list = array_map(
function ( $filename ) use ( $directory ) {
return $directory . DIRECTORY_SEPARATOR . $filename;
},
'wp_template' === $template_type ? $wp_template_filenames : $wp_template_part_filenames
);
return $path_list;
}
public static function get_templates_directory( $template_type = 'wp_template' ) {
$root_path = dirname( __DIR__, 3 ) . '/' . self::TEMPLATES_ROOT_DIR . DIRECTORY_SEPARATOR;
$templates_directory = $root_path . self::DIRECTORY_NAMES['TEMPLATES'];
$template_parts_directory = $root_path . self::DIRECTORY_NAMES['TEMPLATE_PARTS'];
if ( 'wp_template_part' === $template_type ) {
return $template_parts_directory;
}
if ( self::should_use_blockified_product_grid_templates() ) {
return $templates_directory . '/blockified';
}
return $templates_directory;
}
public static function get_block_template_title( $template_slug ) {
$registered_template = self::get_template( $template_slug );
if ( isset( $registered_template ) ) {
return $registered_template->get_template_title();
} else {
return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
}
}
public static function get_block_template_description( $template_slug ) {
$registered_template = self::get_template( $template_slug );
if ( isset( $registered_template ) ) {
return $registered_template->get_template_description();
}
return '';
}
public static function get_block_template_area( $template_slug, $template_type ) {
if ( 'wp_template_part' === $template_type ) {
$registered_template = self::get_template( $template_slug );
if ( $registered_template && property_exists( $registered_template, 'template_area' ) ) {
return $registered_template->template_area;
}
}
return 'uncategorized';
}
public static function generate_template_slug_from_path( $path ) {
$template_extension = '.html';
return basename( $path, $template_extension );
}
public static function get_theme_template_path( $template_slug, $template_type = 'wp_template' ) {
$template_filename = $template_slug . '.html';
$possible_templates_dir = 'wp_template' === $template_type ? array(
self::DIRECTORY_NAMES['TEMPLATES'],
self::DIRECTORY_NAMES['DEPRECATED_TEMPLATES'],
) : array(
self::DIRECTORY_NAMES['TEMPLATE_PARTS'],
self::DIRECTORY_NAMES['DEPRECATED_TEMPLATE_PARTS'],
);
$possible_paths = array_reduce(
$possible_templates_dir,
function ( $carry, $item ) use ( $template_filename ) {
$filepath = DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $template_filename;
$carry[] = get_stylesheet_directory() . $filepath;
$carry[] = get_template_directory() . $filepath;
return $carry;
},
array()
);
foreach ( $possible_paths as $path ) {
if ( is_readable( $path ) ) {
return $path;
}
}
return null;
}
public static function theme_has_template( $template_name ) {
return ! ! self::get_theme_template_path( $template_name, 'wp_template' );
}
public static function theme_has_template_part( $template_name ) {
return ! ! self::get_theme_template_path( $template_name, 'wp_template_part' );
}
public static function supports_block_templates( $template_type = 'wp_template' ) {
if ( 'wp_template_part' === $template_type && ( wc_current_theme_is_fse_theme() || current_theme_supports( 'block-template-parts' ) ) ) {
return true;
} elseif ( 'wp_template' === $template_type && wc_current_theme_is_fse_theme() ) {
return true;
}
return false;
}
public static function template_is_eligible_for_product_archive_fallback( $template_slug ) {
$registered_template = self::get_template( $template_slug );
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
return ProductCatalogTemplate::SLUG === $registered_template->fallback_template;
}
return false;
}
public static function template_is_eligible_for_product_archive_fallback_from_db( $template_slug, $db_templates ) {
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
if ( ! $eligible_for_fallback ) {
return false;
}
$array_filter = array_filter(
$db_templates,
function ( $template ) use ( $template_slug ) {
return ProductCatalogTemplate::SLUG === $template->slug;
}
);
return count( $array_filter ) > 0;
}
public static function get_fallback_template_from_db( $template_slug, $db_templates ) {
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
if ( ! $eligible_for_fallback ) {
return false;
}
foreach ( $db_templates as $template ) {
if ( ProductCatalogTemplate::SLUG === $template->slug ) {
return $template;
}
}
return false;
}
public static function template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) {
return self::template_is_eligible_for_product_archive_fallback( $template_slug )
&& ! self::theme_has_template( $template_slug )
&& self::theme_has_template( ProductCatalogTemplate::SLUG );
}
public static function set_has_theme_file_if_fallback_is_available( $query_result, $template ) {
foreach ( $query_result as &$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_from_theme( $template->slug ) ) {
$query_result_template->has_theme_file = true;
}
return true;
}
}
return false;
}
public static function remove_theme_templates_with_custom_alternative( $templates ) {
$customised_template_slugs = array_map(
function ( $template ) {
return $template->slug;
},
array_values(
array_filter(
$templates,
function ( $template ) {
return 'custom' === $template->source;
}
)
)
);
return array_values(
array_filter(
$templates,
function ( $template ) use ( $customised_template_slugs ) {
return ! ( 'theme' === $template->source && in_array( $template->slug, $customised_template_slugs, true ) );
}
)
);
}
public static function remove_duplicate_customized_templates( $templates, $theme_slug ) {
$filtered_templates = array_filter(
$templates,
function ( $template ) use ( $templates, $theme_slug ) {
if ( $template->theme === $theme_slug ) {
return true;
}
$is_there_a_customized_theme_template = array_filter(
$templates,
function ( $theme_template ) use ( $template, $theme_slug ) {
return $theme_template->slug === $template->slug && $theme_template->theme === $theme_slug;
}
);
if ( $is_there_a_customized_theme_template ) {
return false;
}
return true;
},
);
return $filtered_templates;
}
public static function should_use_blockified_product_grid_templates() {
$use_blockified_templates = get_option( Options::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE );
if ( false === $use_blockified_templates ) {
return wc_current_theme_is_fse_theme();
}
return wc_string_to_bool( $use_blockified_templates );
}
public static function template_has_legacy_template_block( $template ) {
return has_block( 'woocommerce/legacy-template', $template->content );
}
public static function update_template_data( $template, $template_type ) {
if ( ! $template ) {
return $template;
}
if ( empty( $template->title ) || $template->title === $template->slug ) {
$template->title = self::get_block_template_title( $template->slug );
}
if ( empty( $template->description ) ) {
$template->description = self::get_block_template_description( $template->slug );
}
if ( empty( $template->area ) || 'uncategorized' === $template->area ) {
$template->area = self::get_block_template_area( $template->slug, $template_type );
}
return $template;
}
public static function get_block_templates_from_db( $slugs = array(), $template_type = 'wp_template' ) {
$check_query_args = array(
'post_type' => $template_type,
'posts_per_page' => -1,
'no_found_rows' => true,
'tax_query' => array(
array(
'taxonomy' => 'wp_theme',
'field' => 'name',
'terms' => array( self::DEPRECATED_PLUGIN_SLUG, self::PLUGIN_SLUG, get_stylesheet() ),
),
),
);
if ( is_array( $slugs ) && count( $slugs ) > 0 ) {
$check_query_args['post_name__in'] = $slugs;
}
$check_query = new \WP_Query( $check_query_args );
$saved_woo_templates = $check_query->posts;
return array_map(
function ( $saved_woo_template ) {
return self::build_template_result_from_post( $saved_woo_template );
},
$saved_woo_templates
);
}
public static function get_template_part( $slug ) {
$templates_from_db = self::get_block_templates_from_db( array( $slug ), 'wp_template_part' );
if ( count( $templates_from_db ) > 0 ) {
$template_slug_to_load = $templates_from_db[0]->theme;
} else {
$theme_has_template = self::theme_has_template_part( $slug );
$template_slug_to_load = $theme_has_template ? get_stylesheet() : self::PLUGIN_SLUG;
}
$template_part = get_block_template( $template_slug_to_load . '//' . $slug, 'wp_template_part' );
if ( $template_part && ! empty( $template_part->content ) ) {
return $template_part->content;
}
return file_get_contents( self::get_templates_directory( 'wp_template_part' ) . DIRECTORY_SEPARATOR . $slug . '.html' );
}
}