rebase on oct-10-2023
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* AbstractPageTemplate class.
|
||||
*
|
||||
* Shared logic for page templates.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractPageTemplate {
|
||||
/**
|
||||
* Page Template functionality is only initialized when using a block theme.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
add_filter( 'page_template_hierarchy', array( $this, 'page_template_hierarchy' ), 1 );
|
||||
add_action( 'current_screen', array( $this, 'page_template_editor_redirect' ) );
|
||||
add_filter( 'pre_get_document_title', array( $this, 'page_template_title' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template slug.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public static function get_slug();
|
||||
|
||||
/**
|
||||
* Returns the page object assigned to this template/page.
|
||||
*
|
||||
* @return \WP_Post|null Post object or null.
|
||||
*/
|
||||
abstract public static function get_placeholder_page();
|
||||
|
||||
/**
|
||||
* Should return the title of the page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public static function get_template_title();
|
||||
|
||||
/**
|
||||
* Should return true on pages/endpoints/routes where the template should be shown.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function is_active_template();
|
||||
|
||||
/**
|
||||
* Returns the URL to edit the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_edit_template_url() {
|
||||
return admin_url( 'site-editor.php?postType=wp_template&postId=woocommerce%2Fwoocommerce%2F%2F' . $this->get_slug() );
|
||||
}
|
||||
|
||||
/**
|
||||
* When the page should be displaying the template, add it to the hierarchy.
|
||||
*
|
||||
* This places the template name e.g. `cart`, at the beginning of the template hierarchy array. The hook priority
|
||||
* is 1 to ensure it runs first; other consumers e.g. extensions, could therefore inject their own template instead
|
||||
* of this one when using the default priority of 10.
|
||||
*
|
||||
* @param array $templates Templates that match the pages_template_hierarchy.
|
||||
*/
|
||||
public function page_template_hierarchy( $templates ) {
|
||||
if ( $this->is_active_template() ) {
|
||||
array_unshift( $templates, $this->get_slug() );
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the edit page screen to the template editor.
|
||||
*
|
||||
* @param \WP_Screen $current_screen Current screen information.
|
||||
*/
|
||||
public function page_template_editor_redirect( \WP_Screen $current_screen ) {
|
||||
$page = $this->get_placeholder_page();
|
||||
$edit_page_id = 'page' === $current_screen->id && ! empty( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( $page && $edit_page_id === $page->ID ) {
|
||||
wp_safe_redirect( $this->get_edit_template_url() );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the page title when the template is active.
|
||||
*
|
||||
* @param string $title Page title.
|
||||
* @return string
|
||||
*/
|
||||
public function page_template_title( $title ) {
|
||||
if ( $this->is_active_template() ) {
|
||||
return $this->get_template_title();
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ abstract class AbstractTemplateCompatibility {
|
||||
* The array format:
|
||||
* [
|
||||
* <hook-name> => [
|
||||
* block_name => <block-name>,
|
||||
* block_names => [ <block-name>, ... ],
|
||||
* position => before|after,
|
||||
* hooked => [
|
||||
* <function-name> => <priority>,
|
||||
@@ -113,7 +113,7 @@ abstract class AbstractTemplateCompatibility {
|
||||
* ]
|
||||
* Where:
|
||||
* - hook-name is the name of the hook that will be replaced.
|
||||
* - block-name is the name of the block that will replace the hook.
|
||||
* - block-names is the array block names that hook will be attached to.
|
||||
* - position is the position of the block relative to the hook.
|
||||
* - hooked is an array of functions hooked to the hook that will be
|
||||
* replaced. The key is the function name and the value is the
|
||||
|
||||
@@ -47,7 +47,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
array_walk( $parsed_block['innerBlocks'], array( $this, 'inner_blocks_walker' ) );
|
||||
$this->inner_blocks_walker( $parsed_block );
|
||||
|
||||
return $parsed_block;
|
||||
}
|
||||
@@ -72,48 +72,75 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
|
||||
$block_name = $block['blockName'];
|
||||
|
||||
/**
|
||||
* The core/post-template has two different block names:
|
||||
* - core/post-template when the wrapper is rendered.
|
||||
* - core/null when the loop item is rendered.
|
||||
*/
|
||||
if (
|
||||
'core/null' === $block_name &&
|
||||
isset( $block['attrs']['__woocommerceNamespace'] ) &&
|
||||
'woocommerce/product-query/product-template' === $block['attrs']['__woocommerceNamespace']
|
||||
) {
|
||||
if ( $this->is_null_post_template( $block ) ) {
|
||||
$block_name = self::LOOP_ITEM_ID;
|
||||
}
|
||||
|
||||
$supported_blocks = array_map(
|
||||
function( $hook ) {
|
||||
return $hook['block_name'];
|
||||
},
|
||||
array_values( $this->hook_data )
|
||||
$block_hooks = array_filter(
|
||||
$this->hook_data,
|
||||
function( $hook ) use ( $block_name ) {
|
||||
return in_array( $block_name, $hook['block_names'], true );
|
||||
}
|
||||
);
|
||||
|
||||
// We want to inject hooks to the core/post-template or product template block only when the products exist:
|
||||
// https://github.com/woocommerce/woocommerce-blocks/issues/9463.
|
||||
if ( $this->is_post_or_product_template( $block_name ) && ! empty( $block_content ) ) {
|
||||
$this->restore_default_hooks();
|
||||
$content = sprintf(
|
||||
'%1$s%2$s%3$s',
|
||||
$this->get_hooks_buffer( $block_hooks, 'before' ),
|
||||
$block_content,
|
||||
$this->get_hooks_buffer( $block_hooks, 'after' )
|
||||
);
|
||||
$this->remove_default_hooks();
|
||||
return $content;
|
||||
}
|
||||
|
||||
$supported_blocks = array_merge(
|
||||
[],
|
||||
...array_map(
|
||||
function( $hook ) {
|
||||
return $hook['block_names'];
|
||||
},
|
||||
array_values( $this->hook_data )
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! in_array( $block_name, $supported_blocks, true ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* `core/query-no-result` is a special case because it can return two
|
||||
* different content depending on the context. We need to check if the
|
||||
* block content is empty to determine if we need to inject hooks.
|
||||
*/
|
||||
if (
|
||||
'core/query-no-results' === $block_name &&
|
||||
empty( trim( $block_content ) )
|
||||
'core/query-no-results' === $block_name
|
||||
) {
|
||||
return $block_content;
|
||||
|
||||
/**
|
||||
* `core/query-no-result` is a special case because it can return two
|
||||
* different content depending on the context. We need to check if the
|
||||
* block content is empty to determine if we need to inject hooks.
|
||||
*/
|
||||
if ( empty( trim( $block_content ) ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$this->restore_default_hooks();
|
||||
|
||||
$content = sprintf(
|
||||
'%1$s%2$s%3$s',
|
||||
$this->get_hooks_buffer( $block_hooks, 'before' ),
|
||||
$block_content,
|
||||
$this->get_hooks_buffer( $block_hooks, 'after' )
|
||||
);
|
||||
|
||||
$this->remove_default_hooks();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$block_hooks = array_filter(
|
||||
$this->hook_data,
|
||||
function( $hook ) use ( $block_name ) {
|
||||
return $hook['block_name'] === $block_name;
|
||||
}
|
||||
);
|
||||
if ( empty( $block_content ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s%2$s%3$s',
|
||||
@@ -136,6 +163,9 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
* <function-name> => <priority>,
|
||||
* ...
|
||||
* ],
|
||||
* permanently_removed_actions => [
|
||||
* <function-name>
|
||||
* ]
|
||||
* ],
|
||||
* ]
|
||||
* Where:
|
||||
@@ -145,89 +175,101 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
* - hooked is an array of functions hooked to the hook that will be
|
||||
* replaced. The key is the function name and the value is the
|
||||
* priority.
|
||||
* - permanently_removed_actions is an array of functions that we do not want to re-add after they have been removed to avoid duplicate content with the Products block and its inner blocks.
|
||||
*/
|
||||
protected function set_hook_data() {
|
||||
$this->hook_data = array(
|
||||
'woocommerce_before_main_content' => array(
|
||||
'block_name' => 'core/query',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/query', 'woocommerce/product-collection' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_content_wrapper' => 10,
|
||||
'woocommerce_breadcrumb' => 20,
|
||||
),
|
||||
),
|
||||
'woocommerce_after_main_content' => array(
|
||||
'block_name' => 'core/query',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/query', 'woocommerce/product-collection' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_content_wrapper_end' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_before_shop_loop_item_title' => array(
|
||||
'block_name' => 'core/post-title',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/post-title' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_show_product_loop_sale_flash' => 10,
|
||||
'woocommerce_template_loop_product_thumbnail' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_shop_loop_item_title' => array(
|
||||
'block_name' => 'core/post-title',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/post-title' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_template_loop_product_title' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_after_shop_loop_item_title' => array(
|
||||
'block_name' => 'core/post-title',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/post-title' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_template_loop_rating' => 5,
|
||||
'woocommerce_template_loop_price' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_before_shop_loop_item' => array(
|
||||
'block_name' => self::LOOP_ITEM_ID,
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( self::LOOP_ITEM_ID ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_template_loop_product_link_open' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_after_shop_loop_item' => array(
|
||||
'block_name' => self::LOOP_ITEM_ID,
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array( self::LOOP_ITEM_ID ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_template_loop_product_link_close' => 5,
|
||||
'woocommerce_template_loop_add_to_cart' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_before_shop_loop' => array(
|
||||
'block_name' => 'core/post-template',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/post-template', 'woocommerce/product-template' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_all_notices' => 10,
|
||||
'woocommerce_result_count' => 20,
|
||||
'woocommerce_catalog_ordering' => 30,
|
||||
),
|
||||
'permanently_removed_actions' => array(
|
||||
'woocommerce_output_all_notices',
|
||||
'woocommerce_result_count',
|
||||
'woocommerce_catalog_ordering',
|
||||
),
|
||||
),
|
||||
'woocommerce_after_shop_loop' => array(
|
||||
'block_name' => 'core/post-template',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/post-template', 'woocommerce/product-template' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_pagination' => 10,
|
||||
),
|
||||
'permanently_removed_actions' => array(
|
||||
'woocommerce_pagination',
|
||||
),
|
||||
),
|
||||
'woocommerce_no_products_found' => array(
|
||||
'block_name' => 'core/query-no-results',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/query-no-results' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'wc_no_products_found' => 10,
|
||||
),
|
||||
'permanently_removed_actions' => array(
|
||||
'wc_no_products_found',
|
||||
),
|
||||
),
|
||||
'woocommerce_archive_description' => array(
|
||||
'block_name' => 'core/term-description',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'core/term-description' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_taxonomy_archive_description' => 10,
|
||||
'woocommerce_product_archive_description' => 10,
|
||||
),
|
||||
@@ -250,11 +292,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
*/
|
||||
private function inner_blocks_walker( &$block ) {
|
||||
if (
|
||||
'core/query' === $block['blockName'] &&
|
||||
isset( $block['attrs']['namespace'] ) &&
|
||||
'woocommerce/product-query' === $block['attrs']['namespace'] &&
|
||||
isset( $block['attrs']['query']['inherit'] ) &&
|
||||
$block['attrs']['query']['inherit']
|
||||
$this->is_products_block_with_inherit_query( $block ) || $this->is_product_collection_block_with_inherit_query( $block )
|
||||
) {
|
||||
$this->inject_attribute( $block );
|
||||
$this->remove_default_hooks();
|
||||
@@ -265,6 +303,111 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore default hooks except the ones that are not supposed to be re-added.
|
||||
*/
|
||||
private function restore_default_hooks() {
|
||||
foreach ( $this->hook_data as $hook => $data ) {
|
||||
if ( ! isset( $data['hooked'] ) ) {
|
||||
continue;
|
||||
}
|
||||
foreach ( $data['hooked'] as $callback => $priority ) {
|
||||
if ( ! in_array( $callback, $data['permanently_removed_actions'] ?? [], true ) ) {
|
||||
add_action( $hook, $callback, $priority );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if block is within the product-query namespace
|
||||
*
|
||||
* @param array $block Parsed block data.
|
||||
*/
|
||||
private function is_block_within_namespace( $block ) {
|
||||
$attributes = $block['attrs'];
|
||||
|
||||
return isset( $attributes['__woocommerceNamespace'] ) && 'woocommerce/product-query/product-template' === $attributes['__woocommerceNamespace'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if block has isInherited attribute asigned
|
||||
*
|
||||
* @param array $block Parsed block data.
|
||||
*/
|
||||
private function is_block_inherited( $block ) {
|
||||
$attributes = $block['attrs'];
|
||||
|
||||
$outcome = isset( $attributes['isInherited'] ) && 1 === $attributes['isInherited'];
|
||||
|
||||
return $outcome;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core/post-template has two different block names:
|
||||
* - core/post-template when the wrapper is rendered.
|
||||
* - core/null when the loop item is rendered.
|
||||
*
|
||||
* @param array $block Parsed block data.
|
||||
*/
|
||||
private function is_null_post_template( $block ) {
|
||||
$block_name = $block['blockName'];
|
||||
|
||||
return 'core/null' === $block_name && ( $this->is_block_inherited( $block ) || $this->is_block_within_namespace( $block ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if block is a Post template
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
*/
|
||||
private function is_post_template( $block_name ) {
|
||||
return 'core/post-template' === $block_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if block is a Product Template
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
*/
|
||||
private function is_product_template( $block_name ) {
|
||||
return 'woocommerce/product-template' === $block_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if block is eaither a Post template or Product Template
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
*/
|
||||
private function is_post_or_product_template( $block_name ) {
|
||||
return $this->is_post_template( $block_name ) || $this->is_product_template( $block_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the block is a Products block that inherits query from template.
|
||||
*
|
||||
* @param array $block Parsed block data.
|
||||
*/
|
||||
private function is_products_block_with_inherit_query( $block ) {
|
||||
return 'core/query' === $block['blockName'] &&
|
||||
isset( $block['attrs']['namespace'] ) &&
|
||||
'woocommerce/product-query' === $block['attrs']['namespace'] &&
|
||||
isset( $block['attrs']['query']['inherit'] ) &&
|
||||
$block['attrs']['query']['inherit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the block is a Product Collection block that inherits query from template.
|
||||
*
|
||||
* @param array $block Parsed block data.
|
||||
*/
|
||||
private function is_product_collection_block_with_inherit_query( $block ) {
|
||||
return 'woocommerce/product-collection' === $block['blockName'] &&
|
||||
isset( $block['attrs']['query']['inherit'] ) &&
|
||||
$block['attrs']['query']['inherit'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recursively inject the custom attribute to all nested blocks.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateMigrationUtils;
|
||||
|
||||
/**
|
||||
* CartTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CartTemplate extends AbstractPageTemplate {
|
||||
/**
|
||||
* Template slug.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'cart';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object assigned to this template/page.
|
||||
*
|
||||
* @return \WP_Post|null Post object or null.
|
||||
*/
|
||||
public static function get_placeholder_page() {
|
||||
$page_id = wc_get_page_id( 'cart' );
|
||||
return $page_id ? get_post( $page_id ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when viewing the cart page or cart endpoint.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_active_template() {
|
||||
|
||||
if ( ! BlockTemplateMigrationUtils::has_migrated_page( 'cart' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $post;
|
||||
$placeholder = $this->get_placeholder_page();
|
||||
return null !== $placeholder && $post instanceof \WP_Post && $placeholder->post_name === $post->post_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should return the title of the page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_template_title() {
|
||||
return __( 'Cart', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* CheckoutHeader Template class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CheckoutHeaderTemplate {
|
||||
|
||||
const SLUG = 'checkout-header';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateMigrationUtils;
|
||||
|
||||
/**
|
||||
* CheckoutTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CheckoutTemplate extends AbstractPageTemplate {
|
||||
/**
|
||||
* Template slug.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'checkout';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object assigned to this template/page.
|
||||
*
|
||||
* @return \WP_Post|null Post object or null.
|
||||
*/
|
||||
public static function get_placeholder_page() {
|
||||
$page_id = wc_get_page_id( 'checkout' );
|
||||
return $page_id ? get_post( $page_id ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should return the title of the page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_template_title() {
|
||||
return __( 'Checkout', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* True when viewing the checkout page or checkout endpoint.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active_template() {
|
||||
|
||||
if ( ! BlockTemplateMigrationUtils::has_migrated_page( 'checkout' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $post;
|
||||
$placeholder = $this->get_placeholder_page();
|
||||
return null !== $placeholder && $post instanceof \WP_Post && $placeholder->post_name === $post->post_name;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ class ClassicTemplatesCompatibility {
|
||||
global $pagenow;
|
||||
|
||||
if ( is_shop() || is_product_taxonomy() || 'widgets.php' === $pagenow ) {
|
||||
$this->asset_data_registry->add( 'has_filterable_products', true, true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class ClassicTemplatesCompatibility {
|
||||
*/
|
||||
public function set_php_template_data() {
|
||||
if ( is_shop() || is_product_taxonomy() ) {
|
||||
$this->asset_data_registry->add( 'is_rendering_php_template', true, true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* OrderConfirmationTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OrderConfirmationTemplate extends AbstractPageTemplate {
|
||||
/**
|
||||
* Template slug.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'order-confirmation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object assigned to this template/page.
|
||||
*
|
||||
* @return \WP_Post|null Post object or null.
|
||||
*/
|
||||
public static function get_placeholder_page() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when viewing the Order Received endpoint.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_active_template() {
|
||||
return is_wc_endpoint_url( 'order-received' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should return the title of the page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_template_title() {
|
||||
return __( 'Order Confirmation', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
$block_hooks = array_filter(
|
||||
$this->hook_data,
|
||||
function( $hook ) use ( $block_name ) {
|
||||
return $hook['block_name'] === $block_name;
|
||||
return in_array( $block_name, $hook['block_names'], true );
|
||||
}
|
||||
);
|
||||
|
||||
@@ -170,46 +170,46 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
protected function set_hook_data() {
|
||||
$this->hook_data = array(
|
||||
'woocommerce_before_main_content' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_content_wrapper' => 10,
|
||||
'woocommerce_breadcrumb' => 20,
|
||||
),
|
||||
),
|
||||
'woocommerce_after_main_content' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_content_wrapper_end' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_sidebar' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_get_sidebar' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_before_single_product' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_all_notices' => 10,
|
||||
),
|
||||
),
|
||||
'woocommerce_before_single_product_summary' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_show_product_sale_flash' => 10,
|
||||
'woocommerce_show_product_images' => 20,
|
||||
),
|
||||
),
|
||||
'woocommerce_single_product_summary' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array(),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_template_single_title' => 5,
|
||||
'woocommerce_template_single_rating' => 10,
|
||||
'woocommerce_template_single_price' => 10,
|
||||
@@ -220,31 +220,32 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
),
|
||||
),
|
||||
'woocommerce_after_single_product' => array(
|
||||
'block_name' => '',
|
||||
'position' => 'after',
|
||||
'hooked' => array(),
|
||||
'block_names' => array(),
|
||||
'position' => 'after',
|
||||
'hooked' => array(),
|
||||
),
|
||||
'woocommerce_product_meta_start' => array(
|
||||
'block_name' => 'woocommerce/product-meta',
|
||||
'position' => 'before',
|
||||
'hooked' => array(),
|
||||
'block_names' => array( 'woocommerce/product-meta' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(),
|
||||
),
|
||||
'woocommerce_product_meta_end' => array(
|
||||
'block_name' => 'woocommerce/product-meta',
|
||||
'position' => 'after',
|
||||
'hooked' => array(),
|
||||
'block_names' => array( 'woocommerce/product-meta' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(),
|
||||
),
|
||||
'woocommerce_share' => array(
|
||||
'block_name' => 'woocommerce/product-details',
|
||||
'position' => 'before',
|
||||
'hooked' => array(),
|
||||
'block_names' => array( 'woocommerce/product-details' ),
|
||||
'position' => 'before',
|
||||
'hooked' => array(),
|
||||
),
|
||||
'woocommerce_after_single_product_summary' => array(
|
||||
'block_name' => 'woocommerce/product-details',
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'block_names' => array( 'woocommerce/product-details' ),
|
||||
'position' => 'after',
|
||||
'hooked' => array(
|
||||
'woocommerce_output_product_data_tabs' => 10,
|
||||
'woocommerce_upsell_display' => 15,
|
||||
// We want to display the upsell products after the last block that belongs to the Single Product.
|
||||
// 'woocommerce_upsell_display' => 15.
|
||||
'woocommerce_output_related_products' => 20,
|
||||
),
|
||||
),
|
||||
@@ -258,20 +259,16 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
* @return string
|
||||
*/
|
||||
public static function add_compatibility_layer( $template_content ) {
|
||||
$parsed_blocks = parse_blocks( $template_content );
|
||||
|
||||
if ( ! self::has_single_product_template_blocks( $parsed_blocks ) ) {
|
||||
$template = self::inject_custom_attributes_to_first_and_last_block_single_product_template( $parsed_blocks );
|
||||
return self::serialize_blocks( $template );
|
||||
}
|
||||
|
||||
$wrapped_blocks = self::wrap_single_product_template( $template_content );
|
||||
$template = self::inject_custom_attributes_to_first_and_last_block_single_product_template( $wrapped_blocks );
|
||||
|
||||
return array_reduce(
|
||||
$template,
|
||||
function( $carry, $item ) {
|
||||
if ( is_array( $item ) ) {
|
||||
return $carry . serialize_blocks( $item );
|
||||
}
|
||||
return $carry . serialize_block( $item );
|
||||
},
|
||||
''
|
||||
);
|
||||
|
||||
return self::serialize_blocks( $template );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -285,15 +282,13 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
$parsed_blocks = parse_blocks( $template_content );
|
||||
$grouped_blocks = self::group_blocks( $parsed_blocks );
|
||||
|
||||
$single_product_template_blocks = array( 'woocommerce/product-image-gallery', 'woocommerce/product-details', 'woocommerce/add-to-cart-form', 'woocommerce/product-meta', 'woocommerce/product-price', 'woocommerce/breadcrumbs' );
|
||||
|
||||
$wrapped_blocks = array_map(
|
||||
function( $blocks ) use ( $single_product_template_blocks ) {
|
||||
function( $blocks ) {
|
||||
if ( 'core/template-part' === $blocks[0]['blockName'] ) {
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
$has_single_product_template_blocks = self::has_single_product_template_blocks( $blocks, $single_product_template_blocks );
|
||||
$has_single_product_template_blocks = self::has_single_product_template_blocks( $blocks );
|
||||
|
||||
if ( $has_single_product_template_blocks ) {
|
||||
$wrapped_block = self::create_wrap_block_group( $blocks );
|
||||
@@ -319,7 +314,8 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
|
||||
$index = $carry['index'];
|
||||
$carry['index'] = $carry['index'] + 1;
|
||||
$block = $item[0];
|
||||
// If the block is a child of a group block, we need to get the first block of the group.
|
||||
$block = isset( $item[0] ) ? $item[0] : $item;
|
||||
|
||||
if ( 'core/template-part' === $block['blockName'] || self::is_custom_html( $block ) ) {
|
||||
$carry['template'][] = $block;
|
||||
@@ -395,10 +391,11 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
* woocommerce/product-gallery-image, woocommerce/product-details, woocommerce/add-to-cart-form]
|
||||
*
|
||||
* @param array $parsed_blocks Array of parsed block objects.
|
||||
* @param array $single_product_template_blocks Array of single product template blocks.
|
||||
* @return bool True if the template has a single product template block, false otherwise.
|
||||
*/
|
||||
private static function has_single_product_template_blocks( $parsed_blocks, $single_product_template_blocks ) {
|
||||
private static function has_single_product_template_blocks( $parsed_blocks ) {
|
||||
$single_product_template_blocks = array( 'woocommerce/product-image-gallery', 'woocommerce/product-details', 'woocommerce/add-to-cart-form', 'woocommerce/product-meta', 'woocommerce/product-price', 'woocommerce/breadcrumbs' );
|
||||
|
||||
$found = false;
|
||||
|
||||
foreach ( $parsed_blocks as $block ) {
|
||||
@@ -477,4 +474,23 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
private static function is_custom_html( $block ) {
|
||||
return empty( $block['blockName'] ) && ! empty( $block['innerHTML'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize template.
|
||||
*
|
||||
* @param array $parsed_blocks Parsed blocks.
|
||||
* @return string
|
||||
*/
|
||||
private static function serialize_blocks( $parsed_blocks ) {
|
||||
return array_reduce(
|
||||
$parsed_blocks,
|
||||
function( $carry, $item ) {
|
||||
if ( is_array( $item ) ) {
|
||||
return $carry . serialize_blocks( $item );
|
||||
}
|
||||
return $carry . serialize_block( $item );
|
||||
},
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user