rebase on oct-10-2023
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Utils\SettingsUtils;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateMigrationUtils;
|
||||
use \WP_Post;
|
||||
|
||||
/**
|
||||
* BlockTypesController class.
|
||||
@@ -62,6 +70,7 @@ class BlockTemplatesController {
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
add_filter( 'default_wp_template_part_areas', array( $this, 'register_mini_cart_template_part_area' ), 10, 1 );
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
add_filter( 'pre_get_block_template', array( $this, 'get_block_template_fallback' ), 10, 3 );
|
||||
add_filter( 'pre_get_block_file_template', array( $this, 'get_block_file_template' ), 10, 3 );
|
||||
@@ -69,12 +78,96 @@ class BlockTemplatesController {
|
||||
add_filter( 'current_theme_supports-block-templates', array( $this, 'remove_block_template_support_for_shop_page' ) );
|
||||
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 );
|
||||
add_filter( 'post_type_archive_title', array( $this, 'update_product_archive_title' ), 10, 2 );
|
||||
add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 );
|
||||
|
||||
if ( $this->package->is_experimental_build() ) {
|
||||
add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 );
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
add_action( 'init', array( $this, 'maybe_migrate_content' ) );
|
||||
add_filter( 'woocommerce_settings_pages', array( $this, 'template_permalink_settings' ) );
|
||||
add_filter( 'pre_update_option', array( $this, 'update_template_permalink' ), 10, 2 );
|
||||
add_action( 'woocommerce_admin_field_permalink', array( SettingsUtils::class, 'permalink_input_field' ) );
|
||||
|
||||
// By default, the Template Part Block only supports template parts that are in the current theme directory.
|
||||
// This render_callback wrapper allows us to add support for plugin-housed template parts.
|
||||
add_filter(
|
||||
'block_type_metadata_settings',
|
||||
function( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/template-part' === $metadata['name'] &&
|
||||
in_array( $settings['render_callback'], [ 'render_block_core_template_part', 'gutenberg_render_block_core_template_part' ], true )
|
||||
) {
|
||||
$settings['render_callback'] = [ $this, 'render_woocommerce_template_part' ];
|
||||
}
|
||||
return $settings;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
// Prevents shortcodes in templates having their HTML content broken by wpautop.
|
||||
// @see https://core.trac.wordpress.org/ticket/58366 for more info.
|
||||
add_filter(
|
||||
'block_type_metadata_settings',
|
||||
function( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/shortcode' === $metadata['name']
|
||||
) {
|
||||
$settings['original_render_callback'] = $settings['render_callback'];
|
||||
$settings['render_callback'] = function( $attributes, $content ) use ( $settings ) {
|
||||
// The shortcode has already been rendered, so look for the cart/checkout HTML.
|
||||
if ( strstr( $content, 'woocommerce-cart-form' ) || strstr( $content, 'woocommerce-checkout-form' ) ) {
|
||||
// Return early before wpautop runs again.
|
||||
return $content;
|
||||
}
|
||||
|
||||
$render_callback = $settings['original_render_callback'];
|
||||
|
||||
return $render_callback( $attributes, $content );
|
||||
};
|
||||
}
|
||||
return $settings;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Mini-Cart to the default template part areas.
|
||||
*
|
||||
* @param array $default_area_definitions An array of supported area objects.
|
||||
* @return array The supported template part areas including the Mini-Cart one.
|
||||
*/
|
||||
public function register_mini_cart_template_part_area( $default_area_definitions ) {
|
||||
$mini_cart_template_part_area = [
|
||||
'area' => 'mini-cart',
|
||||
'label' => __( 'Mini-Cart', 'woocommerce' ),
|
||||
'description' => __( 'The Mini-Cart template allows shoppers to see their cart items and provides access to the Cart and Checkout pages.', 'woocommerce' ),
|
||||
'icon' => 'mini-cart',
|
||||
'area_tag' => 'mini-cart',
|
||||
];
|
||||
return array_merge( $default_area_definitions, [ $mini_cart_template_part_area ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the `core/template-part` block on the server.
|
||||
*
|
||||
* @param array $attributes The block attributes.
|
||||
* @return string The render.
|
||||
*/
|
||||
public function render_woocommerce_template_part( $attributes ) {
|
||||
if ( 'woocommerce/woocommerce' === $attributes['theme'] ) {
|
||||
$template_part = BlockTemplateUtils::get_block_template( $attributes['theme'] . '//' . $attributes['slug'], 'wp_template_part' );
|
||||
|
||||
if ( $template_part && ! empty( $template_part->content ) ) {
|
||||
return do_blocks( $template_part->content );
|
||||
}
|
||||
}
|
||||
return function_exists( '\gutenberg_render_block_core_template_part' ) ? \gutenberg_render_block_core_template_part( $attributes ) : \render_block_core_template_part( $attributes );
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used on the `pre_get_block_template` hook to return the fallback template from the db in case
|
||||
* the template is eligible for it.
|
||||
@@ -250,7 +343,7 @@ class BlockTemplatesController {
|
||||
* @return array
|
||||
*/
|
||||
public function add_block_templates( $query_result, $query, $template_type ) {
|
||||
if ( ! BlockTemplateUtils::supports_block_templates() ) {
|
||||
if ( ! BlockTemplateUtils::supports_block_templates( $template_type ) ) {
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
@@ -315,6 +408,33 @@ class BlockTemplatesController {
|
||||
*/
|
||||
$query_result = array_map(
|
||||
function( $template ) {
|
||||
if ( str_contains( $template->slug, 'single-product' ) ) {
|
||||
// We don't want to add the compatibility layer on the Editor Side.
|
||||
// The second condition is necessary to not apply the compatibility layer on the REST API. Gutenberg uses the REST API to clone the template.
|
||||
// More details: https://github.com/woocommerce/woocommerce-blocks/issues/9662.
|
||||
if ( ( ! is_admin() && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && ! BlockTemplateUtils::template_has_legacy_template_block( $template ) ) {
|
||||
// Add the product class to the body. We should move this to a more appropriate place.
|
||||
add_filter(
|
||||
'body_class',
|
||||
function( $classes ) {
|
||||
return array_merge( $classes, wc_get_product_class() );
|
||||
}
|
||||
);
|
||||
|
||||
global $product;
|
||||
|
||||
if ( ! $product instanceof \WC_Product ) {
|
||||
$product_id = get_the_ID();
|
||||
if ( $product_id ) {
|
||||
wc_setup_product_data( $product_id );
|
||||
}
|
||||
}
|
||||
|
||||
$new_content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content );
|
||||
$template->content = $new_content;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'theme' === $template->origin && BlockTemplateUtils::template_has_title( $template ) ) {
|
||||
return $template;
|
||||
}
|
||||
@@ -324,16 +444,6 @@ class BlockTemplatesController {
|
||||
if ( ! $template->description ) {
|
||||
$template->description = BlockTemplateUtils::get_block_template_description( $template->slug );
|
||||
}
|
||||
|
||||
if ( str_contains( $template->slug, 'single-product' ) ) {
|
||||
if ( ! is_admin() && ! BlockTemplateUtils::template_has_legacy_template_block( $template ) ) {
|
||||
|
||||
$new_content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content );
|
||||
$template->content = $new_content;
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
return $template;
|
||||
},
|
||||
$query_result
|
||||
@@ -351,32 +461,8 @@ class BlockTemplatesController {
|
||||
* @return int[]|\WP_Post[] An array of found templates.
|
||||
*/
|
||||
public 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( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
||||
array(
|
||||
'taxonomy' => 'wp_theme',
|
||||
'field' => 'name',
|
||||
'terms' => array( BlockTemplateUtils::DEPRECATED_PLUGIN_SLUG, BlockTemplateUtils::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 BlockTemplateUtils::build_template_result_from_post( $saved_woo_template );
|
||||
},
|
||||
$saved_woo_templates
|
||||
);
|
||||
wc_deprecated_function( 'BlockTemplatesController::get_block_templates_from_db()', '7.8', '\Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils::get_block_templates_from_db()' );
|
||||
return BlockTemplateUtils::get_block_templates_from_db( $slugs, $template_type );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,11 +482,7 @@ class BlockTemplatesController {
|
||||
|
||||
foreach ( $template_files as $template_file ) {
|
||||
// Skip the template if it's blockified, and we should only use classic ones.
|
||||
// Until the blockified Product Grid Block is implemented, we need to always skip the blockified templates.
|
||||
// phpcs:ignore Squiz.PHP.CommentedOutCode
|
||||
if ( // $this->package->is_experimental_build() &&
|
||||
// ! BlockTemplateUtils::should_use_blockified_product_grid_templates() &&
|
||||
strpos( $template_file, 'blockified' ) !== false ) {
|
||||
if ( ! BlockTemplateUtils::should_use_blockified_product_grid_templates() && strpos( $template_file, 'blockified' ) !== false ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -470,7 +552,7 @@ class BlockTemplatesController {
|
||||
* @return array WP_Block_Template[] An array of block template objects.
|
||||
*/
|
||||
public function get_block_templates( $slugs = array(), $template_type = 'wp_template' ) {
|
||||
$templates_from_db = $this->get_block_templates_from_db( $slugs, $template_type );
|
||||
$templates_from_db = BlockTemplateUtils::get_block_templates_from_db( $slugs, $template_type );
|
||||
$templates_from_woo = $this->get_block_templates_from_woocommerce( $slugs, $templates_from_db, $template_type );
|
||||
$templates = array_merge( $templates_from_db, $templates_from_woo );
|
||||
|
||||
@@ -489,10 +571,9 @@ class BlockTemplatesController {
|
||||
return $this->template_parts_directory;
|
||||
}
|
||||
|
||||
// When the blockified Product Grid Block will be implemented, we need to use the blockified templates.
|
||||
// if ( $this->package->is_experimental_build() && BlockTemplateUtils::should_use_blockified_product_grid_templates() ) {
|
||||
// return $this->templates_directory . '/blockified';
|
||||
// }.
|
||||
if ( BlockTemplateUtils::should_use_blockified_product_grid_templates() ) {
|
||||
return $this->templates_directory . '/blockified';
|
||||
}
|
||||
|
||||
return $this->templates_directory;
|
||||
}
|
||||
@@ -539,7 +620,13 @@ class BlockTemplatesController {
|
||||
if (
|
||||
is_singular( 'product' ) && $this->block_template_is_available( 'single-product' )
|
||||
) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( 'single-product' ) ) );
|
||||
global $post;
|
||||
|
||||
$valid_slugs = [ 'single-product' ];
|
||||
if ( 'product' === $post->post_type && $post->post_name ) {
|
||||
$valid_slugs[] = 'single-product-' . $post->post_name;
|
||||
}
|
||||
$templates = get_block_templates( array( 'slug__in' => $valid_slugs ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
@@ -572,6 +659,16 @@ class BlockTemplatesController {
|
||||
if ( ! BlockTemplateUtils::theme_has_template( 'taxonomy-product_tag' ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
} elseif ( is_post_type_archive( 'product' ) && is_search() ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( ProductSearchResultsTemplate::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
if ( ! BlockTemplateUtils::theme_has_template( ProductSearchResultsTemplate::SLUG ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
} elseif (
|
||||
( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) && $this->block_template_is_available( 'archive-product' )
|
||||
) {
|
||||
@@ -584,6 +681,22 @@ class BlockTemplatesController {
|
||||
if ( ! BlockTemplateUtils::theme_has_template( 'archive-product' ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
} elseif (
|
||||
is_cart() &&
|
||||
! BlockTemplateUtils::theme_has_template( CartTemplate::get_slug() ) && $this->block_template_is_available( CartTemplate::get_slug() )
|
||||
) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
} elseif (
|
||||
is_checkout() &&
|
||||
! BlockTemplateUtils::theme_has_template( CheckoutTemplate::get_slug() ) && $this->block_template_is_available( CheckoutTemplate::get_slug() )
|
||||
) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
} elseif (
|
||||
is_wc_endpoint_url( 'order-received' )
|
||||
&& ! BlockTemplateUtils::theme_has_template( OrderConfirmationTemplate::get_slug() )
|
||||
&& $this->block_template_is_available( OrderConfirmationTemplate::get_slug() )
|
||||
) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
} else {
|
||||
$queried_object = get_queried_object();
|
||||
if ( is_null( $queried_object ) ) {
|
||||
@@ -650,4 +763,120 @@ class BlockTemplatesController {
|
||||
|
||||
return $post_type_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates page content to templates if needed.
|
||||
*/
|
||||
public function maybe_migrate_content() {
|
||||
// Migration should occur on a normal request to ensure every requirement is met.
|
||||
// We are postponing it if WP is in maintenance mode, installing, WC installing or if the request is part of a WP-CLI command.
|
||||
if ( wp_is_maintenance_mode() || ! get_option( 'woocommerce_db_version', false ) || Constants::is_defined( 'WP_SETUP_CONFIG' ) || Constants::is_defined( 'WC_INSTALLING' ) || Constants::is_defined( 'WP_CLI' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! BlockTemplateMigrationUtils::has_migrated_page( 'cart' ) ) {
|
||||
BlockTemplateMigrationUtils::migrate_page( 'cart', CartTemplate::get_placeholder_page() );
|
||||
}
|
||||
if ( ! BlockTemplateMigrationUtils::has_migrated_page( 'checkout' ) ) {
|
||||
BlockTemplateMigrationUtils::migrate_page( 'checkout', CheckoutTemplate::get_placeholder_page() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces page settings in WooCommerce with text based permalinks which point to a template.
|
||||
*
|
||||
* @param array $settings Settings pages.
|
||||
* @return array
|
||||
*/
|
||||
public function template_permalink_settings( $settings ) {
|
||||
foreach ( $settings as $key => $setting ) {
|
||||
if ( 'woocommerce_checkout_page_id' === $setting['id'] ) {
|
||||
$checkout_page = CheckoutTemplate::get_placeholder_page();
|
||||
$settings[ $key ] = [
|
||||
'title' => __( 'Checkout page', 'woocommerce' ),
|
||||
'desc' => sprintf(
|
||||
// translators: %1$s: opening anchor tag, %2$s: closing anchor tag.
|
||||
__( 'The checkout template can be %1$s edited here%2$s.', 'woocommerce' ),
|
||||
'<a href="' . esc_url( admin_url( 'site-editor.php?postType=wp_template&postId=woocommerce%2Fwoocommerce%2F%2F' . CheckoutTemplate::get_slug() ) ) . '" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'desc_tip' => __( 'This is the URL to the checkout page.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_checkout_page_endpoint',
|
||||
'type' => 'permalink',
|
||||
'default' => $checkout_page ? $checkout_page->post_name : CheckoutTemplate::get_slug(),
|
||||
'autoload' => false,
|
||||
];
|
||||
}
|
||||
if ( 'woocommerce_cart_page_id' === $setting['id'] ) {
|
||||
$cart_page = CartTemplate::get_placeholder_page();
|
||||
$settings[ $key ] = [
|
||||
'title' => __( 'Cart page', 'woocommerce' ),
|
||||
'desc' => sprintf(
|
||||
// translators: %1$s: opening anchor tag, %2$s: closing anchor tag.
|
||||
__( 'The cart template can be %1$s edited here%2$s.', 'woocommerce' ),
|
||||
'<a href="' . esc_url( admin_url( 'site-editor.php?postType=wp_template&postId=woocommerce%2Fwoocommerce%2F%2F' . CartTemplate::get_slug() ) ) . '" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'desc_tip' => __( 'This is the URL to the cart page.', 'woocommerce' ),
|
||||
'id' => 'woocommerce_cart_page_endpoint',
|
||||
'type' => 'permalink',
|
||||
'default' => $cart_page ? $cart_page->post_name : CartTemplate::get_slug(),
|
||||
'autoload' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs entered permalink with the pages and returns the correct value.
|
||||
*
|
||||
* @param string $value Value of the option.
|
||||
* @param string $option Name of the option.
|
||||
* @return string
|
||||
*/
|
||||
public function update_template_permalink( $value, $option ) {
|
||||
if ( 'woocommerce_checkout_page_endpoint' === $option ) {
|
||||
return $this->sync_endpoint_with_page( CheckoutTemplate::get_placeholder_page(), 'checkout', $value );
|
||||
}
|
||||
if ( 'woocommerce_cart_page_endpoint' === $option ) {
|
||||
return $this->sync_endpoint_with_page( CartTemplate::get_placeholder_page(), 'cart', $value );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs the provided permalink with the actual WP page.
|
||||
*
|
||||
* @param WP_Post|null $page The page object, or null if it does not exist.
|
||||
* @param string $page_slug The identifier for the page e.g. cart, checkout.
|
||||
* @param string $permalink The new permalink to use.
|
||||
* @return string THe actual permalink assigned to the page. May differ from $permalink if it was already taken.
|
||||
*/
|
||||
protected function sync_endpoint_with_page( $page, $page_slug, $permalink ) {
|
||||
if ( ! $page ) {
|
||||
$updated_page_id = wc_create_page(
|
||||
esc_sql( $permalink ),
|
||||
'woocommerce_' . $page_slug . '_page_id',
|
||||
$page_slug,
|
||||
'',
|
||||
'',
|
||||
'publish'
|
||||
);
|
||||
} else {
|
||||
$updated_page_id = wp_update_post(
|
||||
[
|
||||
'ID' => $page->ID,
|
||||
'post_name' => esc_sql( $permalink ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Get post again in case slug was updated with a suffix.
|
||||
if ( $updated_page_id && ! is_wp_error( $updated_page_id ) ) {
|
||||
return get_post( $updated_page_id )->post_name;
|
||||
}
|
||||
return $permalink;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user