Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Check block templates and parts directories to the Gutenberg 12.1.0 convention #5455

Merged
merged 10 commits into from
Dec 28, 2021
52 changes: 38 additions & 14 deletions src/BlockTemplatesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,58 @@ class BlockTemplatesController {
private $template_parts_directory;

/**
* Directory name of the block template directory.
* Directory name of all the templates.
*
* @var string
*/
const TEMPLATES_DIR_NAME = 'block-templates';
const TEMPLATES_ROOT_DIR = 'templates';

/**
* Old directory name of the block templates directory.
*
* The convention has changed with Gutenberg 12.1.0, but we need to keep this
* for backwards compatibility.
*
* @deprecated
* @var string
*/
const DEPRECATED_TEMPLATES_DIR_NAME = 'block-templates';

/**
* Old directory name of the block template parts directory.
*
* The convention has changed with Gutenberg 12.1.0, but we need to keep this
* for backwards compatibility.
*
* @deprecated
* @var string
*/
const DEPRECATED_TEMPLATE_PARTS_DIR_NAME = 'block-templates-parts';

/**
* Directory name of the block templates directory.
*
* @var string
*/
const TEMPLATES_DIR_NAME = 'templates';

/**
* Directory name of the block template parts directory.
*
* @var string
*/
const TEMPLATE_PARTS_DIR_NAME = 'block-template-parts';
const TEMPLATE_PARTS_DIR_NAME = 'parts';

/**
* Constructor.
*/
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', '>=' ) ) {
$this->templates_directory = plugin_dir_path( __DIR__ ) . 'templates/' . self::TEMPLATES_DIR_NAME;
$this->template_parts_directory = plugin_dir_path( __DIR__ ) . 'templates/' . self::TEMPLATE_PARTS_DIR_NAME;
$root_path = plugin_dir_path( __DIR__ ) . self::TEMPLATES_ROOT_DIR . DIRECTORY_SEPARATOR;

$this->templates_directory = $root_path . self::TEMPLATES_DIR_NAME;
$this->template_parts_directory = $root_path . self::TEMPLATE_PARTS_DIR_NAME;
$this->init();
}
}
Expand Down Expand Up @@ -311,15 +342,8 @@ public function get_block_templates_from_woocommerce( $slugs, $already_found_tem
$template_files = BlockTemplateUtils::gutenberg_get_template_paths( $directory );
$templates = array();

if ( 'wp_template_part' === $template_type ) {
$dir_name = self::TEMPLATE_PARTS_DIR_NAME;
} else {
$dir_name = self::TEMPLATES_DIR_NAME;
}

foreach ( $template_files as $template_file ) {
$template_slug = BlockTemplateUtils::generate_template_slug_from_path( $template_file, $dir_name );

$template_slug = BlockTemplateUtils::generate_template_slug_from_path( $template_file );
// This template does not have a slug we're looking for. Skip it.
if ( is_array( $slugs ) && count( $slugs ) > 0 && ! in_array( $template_slug, $slugs, true ) ) {
continue;
Expand All @@ -343,7 +367,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 ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) {
$template_file = get_stylesheet_directory() . '/' . self::TEMPLATES_DIR_NAME . '/archive-product.html';
$template_file = BlockTemplateUtils::get_theme_template_path( 'archive-product' );
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
continue;
}
Expand Down
72 changes: 60 additions & 12 deletions src/Utils/BlockTemplateUtils.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php
namespace Automattic\WooCommerce\Blocks\Utils;

use Automattic\WooCommerce\Blocks\BlockTemplatesController;

/**
* BlockTemplateUtils class used for serving block templates from Woo Blocks.
* IMPORTANT: These methods have been duplicated from Gutenberg/lib/full-site-editing/block-templates.php as those functions are not for public usage.
Expand Down Expand Up @@ -233,15 +235,63 @@ public static function convert_slug_to_title( $template_slug ) {
* Converts template paths into a slug
*
* @param string $path The template's path.
* @param string $directory_name The template's directory name.
* @return string slug
*/
public static function generate_template_slug_from_path( $path, $directory_name = 'block-templates' ) {
return substr(
$path,
strpos( $path, $directory_name . DIRECTORY_SEPARATOR ) + 1 + strlen( $directory_name ),
-5
public static function generate_template_slug_from_path( $path ) {
$template_extension = '.html';

return basename( $path, $template_extension );
}

/**
* Gets the first matching template part within themes directories
*
* Since [Gutenberg 12.1.0](https://github.com/WordPress/gutenberg/releases/tag/v12.1.0), the conventions for
* block templates and parts directory has changed from `block-templates` and `block-templates-parts`
* to `templates` and `parts` respectively.
*
* This function traverses all possible combinations of directory paths where a template or part
* could be located and returns the first one which is readable, prioritizing the new convention
* over the deprecated one, but maintaining that one for backwards compatibility.
*
* @param string $template_slug The slug of the template (i.e. without the file extension).
* @param string $template_type Either `wp_template` or `wp_template_part`.
*
* @return string|null The matched path or `null` if no match was found.
*/
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(
BlockTemplatesController::TEMPLATES_DIR_NAME,
BlockTemplatesController::DEPRECATED_TEMPLATES_DIR_NAME,
) : array(
BlockTemplatesController::TEMPLATE_PARTS_DIR_NAME,
BlockTemplatesController::DEPRECATED_TEMPLATE_PARTS_DIR_NAME,
);

// Combine the possible root directory names with either the template directory
// or the stylesheet directory for child themes.
$possible_paths = array_reduce(
tjcafferkey marked this conversation as resolved.
Show resolved Hide resolved
$possible_templates_dir,
function( $carry, $item ) use ( $template_filename ) {
$filepath = DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $template_filename;

$carry[] = get_template_directory() . $filepath;
$carry[] = get_stylesheet_directory() . $filepath;
Comment on lines +301 to +302
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being late on this one, but if I'm not wrong this is searching templates in this order:

  1. Parent theme templates folder.
  2. Child theme templates folder.
  3. Parent theme block-templates folder.
  4. Child theme block-templates folder.

But I think the child theme should always have priority. Something like this:

  1. Child theme templates folder.
  2. Child theme block-templates folder.
  3. Parent theme templates folder.
  4. Parent theme block-templates folder.

What do you think @sunyatasattva?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aljullu Indeed this is correct! I'll open a PR to amend this! Thanks for spotting that.


return $carry;
},
array()
);

// Return the first matching.
foreach ( $possible_paths as $path ) {
if ( is_readable( $path ) ) {
return $path;
}
}

return null;
}

/**
Expand All @@ -251,8 +301,7 @@ public static function generate_template_slug_from_path( $path, $directory_name
* @return boolean
*/
public static function theme_has_template( $template_name ) {
return is_readable( get_template_directory() . '/block-templates/' . $template_name . '.html' ) ||
is_readable( get_stylesheet_directory() . '/block-templates/' . $template_name . '.html' );
return ! ! self::get_theme_template_path( $template_name, 'wp_template' );
}

/**
Expand All @@ -262,8 +311,7 @@ public static function theme_has_template( $template_name ) {
* @return boolean
*/
public static function theme_has_template_part( $template_name ) {
return is_readable( get_template_directory() . '/block-template-parts/' . $template_name . '.html' ) ||
is_readable( get_stylesheet_directory() . '/block-template-parts/' . $template_name . '.html' );
return ! ! self::get_theme_template_path( $template_name, 'wp_template_part' );
}

/**
Expand Down Expand Up @@ -311,8 +359,8 @@ public static function template_is_eligible_for_product_archive_fallback( $templ
*
* 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.
* @param array $query_result Array of template objects.
* @param object $template A specific template object which could have a fallback.
*
* @return boolean
*/
Expand Down
5 changes: 5 additions & 0 deletions templates/templates/single-product.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- wp:template-part {"slug":"header"} /-->
<!-- wp:group {"layout":{"inherit":true}} -->
<div class="wp-block-group"><!-- wp:woocommerce/legacy-template {"template":"single-product"} /--></div>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer"} /-->