auto-patch 638-dev-dev01-2024-05-14T20_44_36
This commit is contained in:
@@ -13,6 +13,7 @@ use WpOrg\Requests\Requests;
|
||||
*/
|
||||
class Connection {
|
||||
const TEXT_COMPLETION_API_URL = 'https://public-api.wordpress.com/wpcom/v2/text-completion';
|
||||
const MODEL = 'gpt-3.5-turbo-1106';
|
||||
|
||||
/**
|
||||
* The post request.
|
||||
@@ -33,6 +34,7 @@ class Connection {
|
||||
'feature' => 'woocommerce_blocks_patterns',
|
||||
'prompt' => $prompt,
|
||||
'token' => $token,
|
||||
'model' => self::MODEL,
|
||||
);
|
||||
|
||||
if ( $response_format ) {
|
||||
@@ -77,6 +79,7 @@ class Connection {
|
||||
'feature' => 'woocommerce_blocks_patterns',
|
||||
'prompt' => $prompt,
|
||||
'token' => $token,
|
||||
'model' => self::MODEL,
|
||||
);
|
||||
|
||||
if ( $response_format ) {
|
||||
|
||||
@@ -80,24 +80,25 @@ class AssetDataRegistry {
|
||||
*/
|
||||
protected function get_core_data() {
|
||||
return [
|
||||
'adminUrl' => admin_url(),
|
||||
'countries' => WC()->countries->get_countries(),
|
||||
'currency' => $this->get_currency_data(),
|
||||
'currentUserId' => get_current_user_id(),
|
||||
'currentUserIsAdmin' => current_user_can( 'manage_woocommerce' ),
|
||||
'dateFormat' => wc_date_format(),
|
||||
'homeUrl' => esc_url( home_url( '/' ) ),
|
||||
'locale' => $this->get_locale_data(),
|
||||
'dashboardUrl' => wc_get_account_endpoint_url( 'dashboard' ),
|
||||
'orderStatuses' => $this->get_order_statuses(),
|
||||
'placeholderImgSrc' => wc_placeholder_img_src(),
|
||||
'productsSettings' => $this->get_products_settings(),
|
||||
'siteTitle' => wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ),
|
||||
'storePages' => $this->get_store_pages(),
|
||||
'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ),
|
||||
'wcVersion' => defined( 'WC_VERSION' ) ? WC_VERSION : '',
|
||||
'wpLoginUrl' => wp_login_url(),
|
||||
'wpVersion' => get_bloginfo( 'version' ),
|
||||
'adminUrl' => admin_url(),
|
||||
'countries' => WC()->countries->get_countries(),
|
||||
'currency' => $this->get_currency_data(),
|
||||
'currentUserId' => get_current_user_id(),
|
||||
'currentUserIsAdmin' => current_user_can( 'manage_woocommerce' ),
|
||||
'currentThemeIsFSETheme' => wc_current_theme_is_fse_theme(),
|
||||
'dateFormat' => wc_date_format(),
|
||||
'homeUrl' => esc_url( home_url( '/' ) ),
|
||||
'locale' => $this->get_locale_data(),
|
||||
'dashboardUrl' => wc_get_account_endpoint_url( 'dashboard' ),
|
||||
'orderStatuses' => $this->get_order_statuses(),
|
||||
'placeholderImgSrc' => wc_placeholder_img_src(),
|
||||
'productsSettings' => $this->get_products_settings(),
|
||||
'siteTitle' => wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ),
|
||||
'storePages' => $this->get_store_pages(),
|
||||
'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ),
|
||||
'wcVersion' => defined( 'WC_VERSION' ) ? WC_VERSION : '',
|
||||
'wpLoginUrl' => wp_login_url(),
|
||||
'wpVersion' => get_bloginfo( 'version' ),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -297,24 +298,15 @@ class AssetDataRegistry {
|
||||
* @param string $key The key used to reference the data being registered. This should use camelCase.
|
||||
* @param mixed $data If not a function, registered to the registry as is. If a function, then the
|
||||
* callback is invoked right before output to the screen.
|
||||
* @param boolean $check_key_exists If set to true, duplicate data will be ignored if the key exists.
|
||||
* @param boolean $check_key_exists Deprecated. If set to true, duplicate data will be ignored if the key exists.
|
||||
* If false, duplicate data will cause an exception.
|
||||
*
|
||||
* @throws InvalidArgumentException Only throws when site is in debug mode. Always logs the error.
|
||||
*/
|
||||
public function add( $key, $data, $check_key_exists = false ) {
|
||||
if ( $check_key_exists && $this->exists( $key ) ) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$this->add_data( $key, $data );
|
||||
} catch ( Exception $e ) {
|
||||
if ( $this->debug() ) {
|
||||
// bubble up.
|
||||
throw $e;
|
||||
}
|
||||
wc_caught_exception( $e, __METHOD__, [ $key, $data ] );
|
||||
if ( $check_key_exists ) {
|
||||
wc_deprecated_argument( 'Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry::add()', '8.9', 'The $check_key_exists parameter is no longer used: all duplicate data will be ignored if the key exists by default' );
|
||||
}
|
||||
|
||||
$this->add_data( $key, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +333,7 @@ class AssetDataRegistry {
|
||||
public function hydrate_data_from_api_request( $key, $path, $check_key_exists = false ) {
|
||||
$this->add(
|
||||
$key,
|
||||
function() use ( $path ) {
|
||||
function () use ( $path ) {
|
||||
if ( isset( $this->preloaded_api_requests[ $path ], $this->preloaded_api_requests[ $path ]['body'] ) ) {
|
||||
return $this->preloaded_api_requests[ $path ]['body'];
|
||||
}
|
||||
@@ -414,24 +406,19 @@ class AssetDataRegistry {
|
||||
*
|
||||
* @param string $key Key for the data.
|
||||
* @param mixed $data Value for the data.
|
||||
*
|
||||
* @throws InvalidArgumentException If key is not a string or already
|
||||
* exists in internal data cache.
|
||||
*/
|
||||
protected function add_data( $key, $data ) {
|
||||
if ( ! is_string( $key ) ) {
|
||||
if ( $this->debug() ) {
|
||||
throw new InvalidArgumentException(
|
||||
'Key for the data being registered must be a string'
|
||||
);
|
||||
}
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||
trigger_error( esc_html__( 'Key for the data being registered must be a string', 'woocommerce' ), E_USER_WARNING );
|
||||
return;
|
||||
}
|
||||
if ( $this->exists( $key ) ) {
|
||||
return;
|
||||
}
|
||||
if ( isset( $this->data[ $key ] ) ) {
|
||||
if ( $this->debug() ) {
|
||||
throw new InvalidArgumentException(
|
||||
'Overriding existing data with an already registered key is not allowed'
|
||||
);
|
||||
}
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||
trigger_error( esc_html__( 'Overriding existing data with an already registered key is not allowed', 'woocommerce' ), E_USER_WARNING );
|
||||
return;
|
||||
}
|
||||
if ( \is_callable( $data ) ) {
|
||||
|
||||
@@ -50,6 +50,7 @@ final class AssetsController {
|
||||
$this->register_style( 'wc-blocks-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-blocks', 'css' ), dirname( __DIR__ ) ), array(), 'all', true );
|
||||
$this->register_style( 'wc-blocks-editor-style', plugins_url( $this->api->get_block_asset_build_path( 'wc-blocks-editor-style', 'css' ), dirname( __DIR__ ) ), array( 'wp-edit-blocks' ), 'all', true );
|
||||
|
||||
$this->api->register_script( 'wc-types', $this->api->get_block_asset_build_path( 'wc-types' ), array(), false );
|
||||
$this->api->register_script( 'wc-blocks-middleware', 'assets/client/blocks/wc-blocks-middleware.js', array(), false );
|
||||
$this->api->register_script( 'wc-blocks-data-store', 'assets/client/blocks/wc-blocks-data.js', array( 'wc-blocks-middleware' ) );
|
||||
$this->api->register_script( 'wc-blocks-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-vendors' ), array(), false );
|
||||
@@ -61,8 +62,9 @@ final class AssetsController {
|
||||
// The price package is shared externally so has no blocks prefix.
|
||||
$this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false );
|
||||
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js', array() );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js', array() );
|
||||
$this->api->register_script( 'wc-blocks-vendors-frontend', $this->api->get_block_asset_build_path( 'wc-blocks-vendors-frontend' ), array(), false );
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
|
||||
// Register the interactivity components here for now.
|
||||
$this->api->register_script( 'wc-interactivity-dropdown', 'assets/client/blocks/wc-interactivity-dropdown.js', array() );
|
||||
@@ -206,7 +208,7 @@ final class AssetsController {
|
||||
$this->get_script_dependency_src_array( $script_data['dependencies'] )
|
||||
);
|
||||
return array_map(
|
||||
function( $src ) {
|
||||
function ( $src ) {
|
||||
return array(
|
||||
'href' => $src,
|
||||
'as' => 'script',
|
||||
@@ -226,7 +228,7 @@ final class AssetsController {
|
||||
$wp_scripts = wp_scripts();
|
||||
return array_reduce(
|
||||
$dependencies,
|
||||
function( $src, $handle ) use ( $wp_scripts ) {
|
||||
function ( $src, $handle ) use ( $wp_scripts ) {
|
||||
if ( isset( $wp_scripts->registered[ $handle ] ) ) {
|
||||
$src[] = esc_url( add_query_arg( 'ver', $wp_scripts->registered[ $handle ]->ver, $this->get_absolute_url( $wp_scripts->registered[ $handle ]->src ) ) );
|
||||
$src = array_merge( $src, $this->get_script_dependency_src_array( $wp_scripts->registered[ $handle ]->deps ) );
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Blocks\AI\Connection;
|
||||
use Automattic\WooCommerce\Blocks\Images\Pexels;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Package;
|
||||
@@ -119,6 +120,7 @@ class BlockPatterns {
|
||||
'keywords' => 'Keywords',
|
||||
'blockTypes' => 'Block Types',
|
||||
'inserter' => 'Inserter',
|
||||
'featureFlag' => 'Feature Flag',
|
||||
);
|
||||
|
||||
if ( ! file_exists( $this->patterns_path ) ) {
|
||||
@@ -170,6 +172,10 @@ class BlockPatterns {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $pattern_data['featureFlag'] && ! Features::is_enabled( $pattern_data['featureFlag'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Title is a required property.
|
||||
if ( ! $pattern_data['title'] ) {
|
||||
_doing_it_wrong(
|
||||
@@ -298,7 +304,7 @@ class BlockPatterns {
|
||||
public function schedule_on_plugin_update( $upgrader_object, $options ) {
|
||||
if ( 'update' === $options['action'] && 'plugin' === $options['type'] && isset( $options['plugins'] ) ) {
|
||||
foreach ( $options['plugins'] as $plugin ) {
|
||||
if ( str_contains( $plugin, 'woocommerce-gutenberg-products-block.php' ) || str_contains( $plugin, 'woocommerce.php' ) ) {
|
||||
if ( str_contains( $plugin, 'woocommerce.php' ) ) {
|
||||
$business_description = get_option( 'woo_ai_describe_store_description' );
|
||||
|
||||
if ( $business_description ) {
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
<?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\Templates\ProductCatalogTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonTemplate;
|
||||
|
||||
/**
|
||||
* BlockTypesController class.
|
||||
@@ -26,57 +19,30 @@ class BlockTemplatesController {
|
||||
*/
|
||||
const TEMPLATES_ROOT_DIR = 'templates';
|
||||
|
||||
/**
|
||||
* Package instance.
|
||||
*
|
||||
* @var Package
|
||||
*/
|
||||
private $package;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Package $package An instance of Package.
|
||||
*/
|
||||
public function __construct( Package $package ) {
|
||||
$this->package = $package;
|
||||
|
||||
$feature_gating = $package->feature();
|
||||
$is_block_templates_controller_refactor_enabled = $feature_gating->is_block_templates_controller_refactor_enabled();
|
||||
|
||||
// This feature is gated for WooCommerce versions 6.0.0 and above.
|
||||
if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '6.0.0', '>=' ) && ! $is_block_templates_controller_refactor_enabled ) {
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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' ) );
|
||||
public function init() {
|
||||
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 );
|
||||
add_filter( 'get_block_template', array( $this, 'add_block_template_details' ), 10, 1 );
|
||||
add_filter( 'get_block_template', array( $this, 'add_block_template_details' ), 10, 3 );
|
||||
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
|
||||
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 );
|
||||
add_filter( 'post_type_archive_title', array( $this, 'update_product_archive_title' ), 10, 2 );
|
||||
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
// 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 ) {
|
||||
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 )
|
||||
in_array( $settings['render_callback'], array( 'render_block_core_template_part', 'gutenberg_render_block_core_template_part' ), true )
|
||||
) {
|
||||
$settings['render_callback'] = [ $this, 'render_woocommerce_template_part' ];
|
||||
$settings['render_callback'] = array( $this, 'render_woocommerce_template_part' );
|
||||
}
|
||||
return $settings;
|
||||
},
|
||||
@@ -88,13 +54,13 @@ class BlockTemplatesController {
|
||||
// @see https://core.trac.wordpress.org/ticket/58366 for more info.
|
||||
add_filter(
|
||||
'block_type_metadata_settings',
|
||||
function( $settings, $metadata ) {
|
||||
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 ) {
|
||||
$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, 'wc-empty-cart-message' ) || strstr( $content, 'woocommerce-checkout-form' ) ) {
|
||||
// Return early before wpautop runs again.
|
||||
@@ -126,7 +92,7 @@ class BlockTemplatesController {
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( $current_screen && 'page' === $current_screen->id && ! empty( $_GET['post'] ) && in_array( absint( $_GET['post'] ), [ wc_get_page_id( 'cart' ), wc_get_page_id( 'checkout' ) ], true ) ) {
|
||||
if ( $current_screen && 'page' === $current_screen->id && ! empty( $_GET['post'] ) && in_array( absint( $_GET['post'] ), array( wc_get_page_id( 'cart' ), wc_get_page_id( 'checkout' ) ), true ) ) {
|
||||
wp_add_inline_style( 'wc-blocks-editor-style', '.edit-post-post-template { display: none; }' );
|
||||
}
|
||||
},
|
||||
@@ -135,23 +101,6 @@ class BlockTemplatesController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -201,7 +150,7 @@ class BlockTemplatesController {
|
||||
}
|
||||
|
||||
$wp_query_args = array(
|
||||
'post_name__in' => array( 'archive-product', $slug ),
|
||||
'post_name__in' => array( ProductCatalogTemplate::SLUG, $slug ),
|
||||
'post_type' => $template_type,
|
||||
'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
|
||||
'no_found_rows' => true,
|
||||
@@ -222,7 +171,7 @@ class BlockTemplatesController {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( count( $posts ) > 0 && 'archive-product' === $posts[0]->post_name ) {
|
||||
if ( count( $posts ) > 0 && ProductCatalogTemplate::SLUG === $posts[0]->post_name ) {
|
||||
$template = _build_block_template_result_from_post( $posts[0] );
|
||||
|
||||
if ( ! is_wp_error( $template ) ) {
|
||||
@@ -253,11 +202,13 @@ class BlockTemplatesController {
|
||||
|
||||
$templates_eligible_for_fallback = array_filter(
|
||||
$template_slugs,
|
||||
array( BlockTemplateUtils::class, 'template_is_eligible_for_product_archive_fallback' )
|
||||
function ( $template_slug ) {
|
||||
return BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug );
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $templates_eligible_for_fallback ) > 0 ) {
|
||||
$template_hierarchy[] = 'archive-product';
|
||||
$template_hierarchy[] = ProductCatalogTemplate::SLUG;
|
||||
}
|
||||
|
||||
return $template_hierarchy;
|
||||
@@ -305,7 +256,7 @@ class BlockTemplatesController {
|
||||
|
||||
// If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) ) {
|
||||
$template_path = BlockTemplateUtils::get_theme_template_path( 'archive-product' );
|
||||
$template_path = BlockTemplateUtils::get_theme_template_path( ProductCatalogTemplate::SLUG );
|
||||
$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 );
|
||||
}
|
||||
@@ -351,19 +302,12 @@ class BlockTemplatesController {
|
||||
* Add the template title and description to WooCommerce templates.
|
||||
*
|
||||
* @param WP_Block_Template|null $block_template The found block template, or null if there isn't one.
|
||||
* @param string $id Template unique identifier (example: 'theme_slug//template_slug').
|
||||
* @param array $template_type Template type: 'wp_template' or 'wp_template_part'.
|
||||
* @return WP_Block_Template|null
|
||||
*/
|
||||
public function add_block_template_details( $block_template ) {
|
||||
if ( ! $block_template ) {
|
||||
return $block_template;
|
||||
}
|
||||
if ( ! BlockTemplateUtils::template_has_title( $block_template ) ) {
|
||||
$block_template->title = BlockTemplateUtils::get_block_template_title( $block_template->slug );
|
||||
}
|
||||
if ( ! $block_template->description ) {
|
||||
$block_template->description = BlockTemplateUtils::get_block_template_description( $block_template->slug );
|
||||
}
|
||||
return $block_template;
|
||||
public function add_block_template_details( $block_template, $id, $template_type ) {
|
||||
return BlockTemplateUtils::update_template_data( $block_template, $template_type );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,12 +319,13 @@ class BlockTemplatesController {
|
||||
* @return array
|
||||
*/
|
||||
public function add_block_templates( $query_result, $query, $template_type ) {
|
||||
if ( ! BlockTemplateUtils::supports_block_templates( $template_type ) ) {
|
||||
$slugs = isset( $query['slug__in'] ) ? $query['slug__in'] : array();
|
||||
|
||||
if ( ! BlockTemplateUtils::supports_block_templates( $template_type ) && ! in_array( ComingSoonTemplate::SLUG, $slugs, true ) ) {
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
$post_type = isset( $query['post_type'] ) ? $query['post_type'] : '';
|
||||
$slugs = isset( $query['slug__in'] ) ? $query['slug__in'] : array();
|
||||
$template_files = $this->get_block_templates( $slugs, $template_type );
|
||||
$theme_slug = wp_get_theme()->get_stylesheet();
|
||||
|
||||
@@ -406,9 +351,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::build_template_result_from_file( $template_file, $template_type );
|
||||
} else {
|
||||
if ( 'custom' === $template_file->source ) {
|
||||
$query_result[] = $template_file;
|
||||
continue;
|
||||
}
|
||||
@@ -424,6 +367,7 @@ class BlockTemplatesController {
|
||||
! isset( $query['area'] ) || ( property_exists( $template_file, 'area' ) && $template_file->area === $query['area'] );
|
||||
$should_include = $is_not_custom && $fits_slug_query && $fits_area_query;
|
||||
if ( $should_include ) {
|
||||
$template = BlockTemplateUtils::build_template_result_from_file( $template_file, $template_type );
|
||||
$query_result[] = $template;
|
||||
}
|
||||
}
|
||||
@@ -443,45 +387,8 @@ class BlockTemplatesController {
|
||||
* templates that aren't listed in theme.json.
|
||||
*/
|
||||
$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 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( post_password_required() ) {
|
||||
$template->content = SingleProductTemplate::add_password_form( $template->content );
|
||||
} else {
|
||||
$template->content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! BlockTemplateUtils::template_has_title( $template ) ) {
|
||||
$template->title = BlockTemplateUtils::get_block_template_title( $template->slug );
|
||||
}
|
||||
if ( ! $template->description ) {
|
||||
$template->description = BlockTemplateUtils::get_block_template_description( $template->slug );
|
||||
}
|
||||
|
||||
return $template;
|
||||
function ( $template ) use ( $template_type ) {
|
||||
return BlockTemplateUtils::update_template_data( $template, $template_type );
|
||||
},
|
||||
$query_result
|
||||
);
|
||||
@@ -513,16 +420,10 @@ class BlockTemplatesController {
|
||||
* @return array Templates from the WooCommerce blocks plugin directory.
|
||||
*/
|
||||
public function get_block_templates_from_woocommerce( $slugs, $already_found_templates, $template_type = 'wp_template' ) {
|
||||
$directory = BlockTemplateUtils::get_templates_directory( $template_type );
|
||||
$template_files = BlockTemplateUtils::get_template_paths( $directory );
|
||||
$template_files = BlockTemplateUtils::get_template_paths( $template_type );
|
||||
$templates = array();
|
||||
|
||||
foreach ( $template_files as $template_file ) {
|
||||
// Skip the Product Gallery template part, as it is not supposed to be exposed at this point.
|
||||
if ( str_contains( $template_file, 'templates/parts/product-gallery.html' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip the template if it's blockified, and we should only use classic ones.
|
||||
if ( ! BlockTemplateUtils::should_use_blockified_product_grid_templates() && strpos( $template_file, 'blockified' ) !== false ) {
|
||||
continue;
|
||||
@@ -564,7 +465,7 @@ class BlockTemplatesController {
|
||||
|
||||
// If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) ) {
|
||||
$template_file = BlockTemplateUtils::get_theme_template_path( 'archive-product' );
|
||||
$template_file = BlockTemplateUtils::get_theme_template_path( ProductCatalogTemplate::SLUG );
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
|
||||
continue;
|
||||
}
|
||||
@@ -572,7 +473,7 @@ class BlockTemplatesController {
|
||||
// At this point the template only exists in the Blocks filesystem, if is a taxonomy-product_cat/tag/attribute.html template
|
||||
// let's use the archive-product.html template from Blocks.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) {
|
||||
$template_file = $this->get_template_path_from_woocommerce( 'archive-product' );
|
||||
$template_file = $this->get_template_path_from_woocommerce( ProductCatalogTemplate::SLUG );
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, false );
|
||||
continue;
|
||||
}
|
||||
@@ -631,138 +532,13 @@ class BlockTemplatesController {
|
||||
) || $this->get_block_templates( array( $template_name ), $template_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( is_embed() || ! BlockTemplateUtils::supports_block_templates() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
is_singular( 'product' ) && $this->block_template_is_available( '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' );
|
||||
}
|
||||
|
||||
if ( ! BlockTemplateUtils::theme_has_template( 'single-product' ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
} elseif (
|
||||
( is_product_taxonomy() && is_tax( 'product_cat' ) ) && $this->block_template_is_available( 'taxonomy-product_cat' )
|
||||
) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( 'taxonomy-product_cat' ) ) );
|
||||
|
||||
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( 'taxonomy-product_cat' ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
} elseif (
|
||||
( is_product_taxonomy() && is_tax( 'product_tag' ) ) && $this->block_template_is_available( 'taxonomy-product_tag' )
|
||||
) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( 'taxonomy-product_tag' ) ) );
|
||||
|
||||
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( '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' )
|
||||
) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( 'archive-product' ) ) );
|
||||
|
||||
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( '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 );
|
||||
} else {
|
||||
$queried_object = get_queried_object();
|
||||
if ( is_null( $queried_object ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $queried_object->taxonomy ) && taxonomy_is_product_attribute( $queried_object->taxonomy ) && $this->block_template_is_available( ProductAttributeTemplate::SLUG )
|
||||
) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( ProductAttributeTemplate::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( ProductAttributeTemplate::SLUG ) ) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the template panel from the Sidebar of the Shop page because
|
||||
* the Site Editor handles it.
|
||||
*
|
||||
* @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/6278
|
||||
*
|
||||
* @param bool $is_support Whether the active theme supports block templates.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function remove_block_template_support_for_shop_page( $is_support ) {
|
||||
global $pagenow, $post;
|
||||
|
||||
if (
|
||||
is_admin() &&
|
||||
'post.php' === $pagenow &&
|
||||
function_exists( 'wc_get_page_id' ) &&
|
||||
is_a( $post, 'WP_Post' ) &&
|
||||
wc_get_page_id( 'shop' ) === $post->ID
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $is_support;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the product archive title to "Shop".
|
||||
*
|
||||
* Attention: this method is run in classic themes as well, so it
|
||||
* can't be moved to the ProductCatalogTemplate class. See:
|
||||
* https://github.com/woocommerce/woocommerce/pull/46429
|
||||
*
|
||||
* @param string $post_type_name Post type 'name' label.
|
||||
* @param string $post_type Post type.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Blocks\Templates\AbstractTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\AbstractTemplatePart;
|
||||
use Automattic\WooCommerce\Blocks\Templates\MiniCartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutHeaderTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonEntireSiteTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonStoreOnlyTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCategoryTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductTagTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplate;
|
||||
|
||||
/**
|
||||
* BlockTemplatesRegistry class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BlockTemplatesRegistry {
|
||||
|
||||
/**
|
||||
* The array of registered templates.
|
||||
*
|
||||
* @var AbstractTemplate[]|AbstractTemplatePart[]
|
||||
*/
|
||||
private $templates = array();
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
if ( BlockTemplateUtils::supports_block_templates( 'wp_template' ) ) {
|
||||
$templates = array(
|
||||
ProductCatalogTemplate::SLUG => new ProductCatalogTemplate(),
|
||||
ProductCategoryTemplate::SLUG => new ProductCategoryTemplate(),
|
||||
ProductTagTemplate::SLUG => new ProductTagTemplate(),
|
||||
ProductAttributeTemplate::SLUG => new ProductAttributeTemplate(),
|
||||
ProductSearchResultsTemplate::SLUG => new ProductSearchResultsTemplate(),
|
||||
CartTemplate::SLUG => new CartTemplate(),
|
||||
CheckoutTemplate::SLUG => new CheckoutTemplate(),
|
||||
OrderConfirmationTemplate::SLUG => new OrderConfirmationTemplate(),
|
||||
SingleProductTemplate::SLUG => new SingleProductTemplate(),
|
||||
);
|
||||
} else {
|
||||
$templates = array();
|
||||
}
|
||||
if ( Features::is_enabled( 'launch-your-store' ) ) {
|
||||
$templates[ ComingSoonTemplate::SLUG ] = new ComingSoonTemplate();
|
||||
}
|
||||
if ( BlockTemplateUtils::supports_block_templates( 'wp_template_part' ) ) {
|
||||
$template_parts = array(
|
||||
MiniCartTemplate::SLUG => new MiniCartTemplate(),
|
||||
CheckoutHeaderTemplate::SLUG => new CheckoutHeaderTemplate(),
|
||||
);
|
||||
} else {
|
||||
$template_parts = array();
|
||||
}
|
||||
$this->templates = array_merge( $templates, $template_parts );
|
||||
|
||||
// Init all templates.
|
||||
foreach ( $this->templates as $template ) {
|
||||
$template->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the template matching the slug
|
||||
*
|
||||
* @param string $template_slug Slug of the template to retrieve.
|
||||
*
|
||||
* @return AbstractTemplate|AbstractTemplatePart|null
|
||||
*/
|
||||
public function get_template( $template_slug ) {
|
||||
if ( array_key_exists( $template_slug, $this->templates ) ) {
|
||||
$registered_template = $this->templates[ $template_slug ];
|
||||
return $registered_template;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -463,6 +463,25 @@ abstract class AbstractBlock {
|
||||
* @return array
|
||||
*/
|
||||
protected function get_routes_from_namespace( $namespace ) {
|
||||
/**
|
||||
* Gives opportunity to return routes without invoking the compute intensive REST API.
|
||||
*
|
||||
* @since 8.7.0
|
||||
* @param array $routes Array of routes.
|
||||
* @param string $namespace Namespace for routes.
|
||||
* @param string $context Context, can be edit or view.
|
||||
*/
|
||||
$routes = apply_filters(
|
||||
'woocommerce_blocks_pre_get_routes_from_namespace',
|
||||
array(),
|
||||
$namespace,
|
||||
'view'
|
||||
);
|
||||
|
||||
if ( ! empty( $routes ) ) {
|
||||
return $routes;
|
||||
}
|
||||
|
||||
$rest_server = rest_get_server();
|
||||
$namespace_index = $rest_server->get_namespace_index(
|
||||
[
|
||||
|
||||
@@ -695,12 +695,12 @@ abstract class AbstractProductGrid extends AbstractDynamicBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'minColumns', wc_get_theme_support( 'product_blocks::min_columns', 1 ), true );
|
||||
$this->asset_data_registry->add( 'maxColumns', wc_get_theme_support( 'product_blocks::max_columns', 6 ), true );
|
||||
$this->asset_data_registry->add( 'defaultColumns', wc_get_theme_support( 'product_blocks::default_columns', 3 ), true );
|
||||
$this->asset_data_registry->add( 'minRows', wc_get_theme_support( 'product_blocks::min_rows', 1 ), true );
|
||||
$this->asset_data_registry->add( 'maxRows', wc_get_theme_support( 'product_blocks::max_rows', 6 ), true );
|
||||
$this->asset_data_registry->add( 'defaultRows', wc_get_theme_support( 'product_blocks::default_rows', 3 ), true );
|
||||
$this->asset_data_registry->add( 'minColumns', wc_get_theme_support( 'product_blocks::min_columns', 1 ) );
|
||||
$this->asset_data_registry->add( 'maxColumns', wc_get_theme_support( 'product_blocks::max_columns', 6 ) );
|
||||
$this->asset_data_registry->add( 'defaultColumns', wc_get_theme_support( 'product_blocks::default_columns', 3 ) );
|
||||
$this->asset_data_registry->add( 'minRows', wc_get_theme_support( 'product_blocks::min_rows', 1 ) );
|
||||
$this->asset_data_registry->add( 'maxRows', wc_get_theme_support( 'product_blocks::max_rows', 6 ) );
|
||||
$this->asset_data_registry->add( 'defaultRows', wc_get_theme_support( 'product_blocks::default_rows', 3 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,13 +22,13 @@ class AllProducts extends AbstractBlock {
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
// Set this so filter blocks being used as widgets know when to render.
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
$this->asset_data_registry->add( 'minColumns', wc_get_theme_support( 'product_blocks::min_columns', 1 ), true );
|
||||
$this->asset_data_registry->add( 'maxColumns', wc_get_theme_support( 'product_blocks::max_columns', 6 ), true );
|
||||
$this->asset_data_registry->add( 'defaultColumns', wc_get_theme_support( 'product_blocks::default_columns', 3 ), true );
|
||||
$this->asset_data_registry->add( 'minRows', wc_get_theme_support( 'product_blocks::min_rows', 1 ), true );
|
||||
$this->asset_data_registry->add( 'maxRows', wc_get_theme_support( 'product_blocks::max_rows', 6 ), true );
|
||||
$this->asset_data_registry->add( 'defaultRows', wc_get_theme_support( 'product_blocks::default_rows', 3 ), true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true );
|
||||
$this->asset_data_registry->add( 'minColumns', wc_get_theme_support( 'product_blocks::min_columns', 1 ) );
|
||||
$this->asset_data_registry->add( 'maxColumns', wc_get_theme_support( 'product_blocks::max_columns', 6 ) );
|
||||
$this->asset_data_registry->add( 'defaultColumns', wc_get_theme_support( 'product_blocks::default_columns', 3 ) );
|
||||
$this->asset_data_registry->add( 'minRows', wc_get_theme_support( 'product_blocks::min_rows', 1 ) );
|
||||
$this->asset_data_registry->add( 'maxRows', wc_get_theme_support( 'product_blocks::max_rows', 6 ) );
|
||||
$this->asset_data_registry->add( 'defaultRows', wc_get_theme_support( 'product_blocks::default_rows', 3 ) );
|
||||
|
||||
// Hydrate the All Product block with data from the API. This is for the add to cart buttons which show current quantity in cart, and events.
|
||||
if ( ! is_admin() && ! WC()->is_rest_api_request() ) {
|
||||
|
||||
@@ -37,7 +37,7 @@ class AllReviews extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled(), true );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ), true );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled() );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class AttributeFilter extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'attributes', array_values( wc_get_attribute_taxonomies() ), true );
|
||||
$this->asset_data_registry->add( 'attributes', array_values( wc_get_attribute_taxonomies() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
|
||||
|
||||
/**
|
||||
* Cart class.
|
||||
@@ -233,21 +234,20 @@ class Cart extends AbstractBlock {
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data(), true );
|
||||
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true );
|
||||
$this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ), true );
|
||||
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ), true );
|
||||
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ), true );
|
||||
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled(), true );
|
||||
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled(), true );
|
||||
$this->asset_data_registry->add( 'shippingEnabled', wc_shipping_enabled(), true );
|
||||
$this->asset_data_registry->add( 'hasDarkEditorStyleSupport', current_theme_supports( 'dark-editor-style' ), true );
|
||||
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data() );
|
||||
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location() );
|
||||
$this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ) );
|
||||
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ) );
|
||||
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ) );
|
||||
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled() );
|
||||
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled() );
|
||||
$this->asset_data_registry->add( 'shippingEnabled', wc_shipping_enabled() );
|
||||
$this->asset_data_registry->add( 'hasDarkEditorStyleSupport', current_theme_supports( 'dark-editor-style' ) );
|
||||
$this->asset_data_registry->register_page_id( isset( $attributes['checkoutPageId'] ) ? $attributes['checkoutPageId'] : 0 );
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme(), true );
|
||||
$this->asset_data_registry->add( 'activeShippingZones', CartCheckoutUtils::get_shipping_zones(), true );
|
||||
|
||||
$pickup_location_settings = get_option( 'woocommerce_pickup_location_settings', [] );
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', wc_string_to_bool( $pickup_location_settings['enabled'] ?? 'no' ), true );
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme() );
|
||||
$this->asset_data_registry->add( 'activeShippingZones', CartCheckoutUtils::get_shipping_zones() );
|
||||
$pickup_location_settings = LocalPickupUtils::get_local_pickup_settings();
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', $pickup_location_settings['enabled'] );
|
||||
|
||||
// Hydrate the following data depending on admin or frontend context.
|
||||
if ( ! is_admin() && ! WC()->is_rest_api_request() ) {
|
||||
@@ -285,6 +285,7 @@ class Cart extends AbstractBlock {
|
||||
'Cart',
|
||||
'CartOrderSummaryTaxesBlock',
|
||||
'CartOrderSummarySubtotalBlock',
|
||||
'CartOrderSummaryTotalsBlock',
|
||||
'FilledCartBlock',
|
||||
'EmptyCartBlock',
|
||||
'CartTotalsBlock',
|
||||
|
||||
@@ -11,4 +11,68 @@ class CartOrderSummaryBlock extends AbstractInnerBlock {
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'cart-order-summary-block';
|
||||
|
||||
/**
|
||||
* Get the contents of the given inner block.
|
||||
*
|
||||
* @param string $block_name Name of the order summary inner block.
|
||||
* @param string $content The content to search.
|
||||
* @return array|bool
|
||||
*/
|
||||
private function get_inner_block_content( $block_name, $content ) {
|
||||
if ( preg_match( $this->inner_block_regex( $block_name ), $content, $matches ) ) {
|
||||
return $matches[0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regex that will return an inner block.
|
||||
*
|
||||
* @param string $block_name Name of the order summary inner block.
|
||||
* @return string Regex pattern.
|
||||
*/
|
||||
private function inner_block_regex( $block_name ) {
|
||||
return '/<div data-block-name="woocommerce\/cart-order-summary-' . $block_name . '-block"(.+?)>(.*?)<\/div>/si';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Cart Order Summary block.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
* @param string $content Block content.
|
||||
* @param object $block Block object.
|
||||
* @return string Rendered block.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
// The order-summary-totals block was introduced as a new parent block for the totals
|
||||
// (subtotal, discount, fees, shipping and taxes) blocks.
|
||||
$regex_for_cart_order_summary_totals = '/<div data-block-name="woocommerce\/cart-order-summary-totals-block"(.+?)>/';
|
||||
$order_summary_totals_content = '<div data-block-name="woocommerce/cart-order-summary-totals-block" class="wp-block-woocommerce-cart-order-summary-totals-block">';
|
||||
|
||||
$totals_inner_blocks = array( 'subtotal', 'discount', 'fee', 'shipping', 'taxes' ); // We want to move these blocks inside a parent 'totals' block.
|
||||
|
||||
if ( preg_match( $regex_for_cart_order_summary_totals, $content ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ( $totals_inner_blocks as $key => $block_name ) {
|
||||
$inner_block_content = $this->get_inner_block_content( $block_name, $content );
|
||||
|
||||
if ( $inner_block_content ) {
|
||||
$order_summary_totals_content .= "\n" . $inner_block_content;
|
||||
|
||||
// The last block is replaced with the totals block.
|
||||
if ( count( $totals_inner_blocks ) - 1 === $key ) {
|
||||
$order_summary_totals_content .= '</div>';
|
||||
$content = preg_replace( $this->inner_block_regex( $block_name ), $order_summary_totals_content, $content );
|
||||
} else {
|
||||
// Otherwise, remove the block.
|
||||
$content = preg_replace( $this->inner_block_regex( $block_name ), '', $content );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preg_replace( '/\n\n( *?)/i', '', $content );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* CartOrderSummaryTotalsBlock class.
|
||||
*/
|
||||
class CartOrderSummaryTotalsBlock extends AbstractInnerBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'cart-order-summary-totals-block';
|
||||
}
|
||||
@@ -41,6 +41,8 @@ class Checkout extends AbstractBlock {
|
||||
return isset( $_GET['_wp-find-template'] ) ? false : $return;
|
||||
}
|
||||
);
|
||||
|
||||
add_action( 'save_post', array( $this, 'update_local_pickup_title' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,6 +244,73 @@ class Checkout extends AbstractBlock {
|
||||
return is_wc_endpoint_url( 'order-pay' ) || is_wc_endpoint_url( 'order-received' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the local pickup title in WooCommerce Settings when the checkout page containing a Checkout block is saved.
|
||||
*
|
||||
* @param int $post_id The post ID.
|
||||
* @param \WP_Post $post The post object.
|
||||
* @return void
|
||||
*/
|
||||
public function update_local_pickup_title( $post_id, $post ) {
|
||||
|
||||
// This is not a proper save action, maybe an autosave, so don't continue.
|
||||
if ( empty( $post->post_status ) || 'inherit' === $post->post_status ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we are editing the checkout page and that it contains a Checkout block.
|
||||
// Cast to string for Checkout page ID comparison because get_option can return it as a string, so better to compare both values as strings.
|
||||
if ( ! empty( $post->post_type ) && 'wp_template' !== $post->post_type && ( false === has_block( 'woocommerce/checkout', $post ) || (string) get_option( 'woocommerce_checkout_page_id' ) !== (string) $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ( ! empty( $post->post_type ) && ! empty( $post->post_name ) && 'page-checkout' !== $post->post_name && 'wp_template' === $post->post_type ) || false === has_block( 'woocommerce/checkout', $post ) ) {
|
||||
return;
|
||||
}
|
||||
$pickup_location_settings = LocalPickupUtils::get_local_pickup_settings( 'edit' );
|
||||
|
||||
if ( ! isset( $pickup_location_settings['title'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $post->post_content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_blocks = parse_blocks( $post->post_content );
|
||||
$title = $this->find_local_pickup_text_in_checkout_block( $post_blocks );
|
||||
|
||||
if ( $title ) {
|
||||
$pickup_location_settings['title'] = $title;
|
||||
update_option( 'woocommerce_pickup_location_settings', $pickup_location_settings );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurse through the blocks to find the shipping methods block, then get the value of the localPickupText attribute from it.
|
||||
*
|
||||
* @param array $blocks The block(s) to search for the local pickup text.
|
||||
* @return null|string The local pickup text if found, otherwise void.
|
||||
*/
|
||||
private function find_local_pickup_text_in_checkout_block( $blocks ) {
|
||||
if ( ! is_array( $blocks ) ) {
|
||||
return null;
|
||||
}
|
||||
foreach ( $blocks as $block ) {
|
||||
if ( ! empty( $block['blockName'] ) && 'woocommerce/checkout-shipping-method-block' === $block['blockName'] ) {
|
||||
if ( ! empty( $block['attrs']['localPickupText'] ) ) {
|
||||
return $block['attrs']['localPickupText'];
|
||||
}
|
||||
}
|
||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||
$answer = $this->find_local_pickup_text_in_checkout_block( $block['innerBlocks'] );
|
||||
if ( $answer ) {
|
||||
return $answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data passed through from server to client for block.
|
||||
*
|
||||
@@ -252,37 +321,49 @@ class Checkout extends AbstractBlock {
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data(), true );
|
||||
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true );
|
||||
$country_data = CartCheckoutUtils::get_country_data();
|
||||
$address_formats = WC()->countries->get_address_formats();
|
||||
|
||||
// Move the address format into the 'countryData' setting.
|
||||
// We need to skip 'default' because that's not a valid country.
|
||||
foreach ( $address_formats as $country_code => $format ) {
|
||||
if ( 'default' === $country_code ) {
|
||||
continue;
|
||||
}
|
||||
$country_data[ $country_code ]['format'] = $format;
|
||||
}
|
||||
|
||||
$this->asset_data_registry->add( 'countryData', $country_data );
|
||||
$this->asset_data_registry->add( 'defaultAddressFormat', $address_formats['default'] );
|
||||
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location() );
|
||||
$this->asset_data_registry->add(
|
||||
'checkoutAllowsGuest',
|
||||
false === filter_var(
|
||||
wc()->checkout()->is_registration_required(),
|
||||
FILTER_VALIDATE_BOOLEAN
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
$this->asset_data_registry->add(
|
||||
'checkoutAllowsSignup',
|
||||
filter_var(
|
||||
wc()->checkout()->is_registration_enabled(),
|
||||
FILTER_VALIDATE_BOOLEAN
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
$this->asset_data_registry->add( 'checkoutShowLoginReminder', filter_var( get_option( 'woocommerce_enable_checkout_login_reminder' ), FILTER_VALIDATE_BOOLEAN ), true );
|
||||
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ), true );
|
||||
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ), true );
|
||||
$this->asset_data_registry->add( 'forcedBillingAddress', 'billing_only' === get_option( 'woocommerce_ship_to_destination' ), true );
|
||||
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled(), true );
|
||||
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled(), true );
|
||||
$this->asset_data_registry->add( 'shippingEnabled', wc_shipping_enabled(), true );
|
||||
$this->asset_data_registry->add( 'hasDarkEditorStyleSupport', current_theme_supports( 'dark-editor-style' ), true );
|
||||
$this->asset_data_registry->add( 'checkoutShowLoginReminder', filter_var( get_option( 'woocommerce_enable_checkout_login_reminder' ), FILTER_VALIDATE_BOOLEAN ) );
|
||||
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ) );
|
||||
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ) );
|
||||
$this->asset_data_registry->add( 'forcedBillingAddress', 'billing_only' === get_option( 'woocommerce_ship_to_destination' ) );
|
||||
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled() );
|
||||
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled() );
|
||||
$this->asset_data_registry->add( 'shippingEnabled', wc_shipping_enabled() );
|
||||
$this->asset_data_registry->add( 'hasDarkEditorStyleSupport', current_theme_supports( 'dark-editor-style' ) );
|
||||
$this->asset_data_registry->register_page_id( isset( $attributes['cartPageId'] ) ? $attributes['cartPageId'] : 0 );
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme(), true );
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme() );
|
||||
|
||||
$pickup_location_settings = get_option( 'woocommerce_pickup_location_settings', [] );
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', wc_string_to_bool( $pickup_location_settings['enabled'] ?? 'no' ), true );
|
||||
$pickup_location_settings = LocalPickupUtils::get_local_pickup_settings();
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', $pickup_location_settings['enabled'] );
|
||||
$this->asset_data_registry->add( 'localPickupText', $pickup_location_settings['title'] );
|
||||
|
||||
$is_block_editor = $this->is_block_editor();
|
||||
|
||||
@@ -489,6 +570,7 @@ class Checkout extends AbstractBlock {
|
||||
'CheckoutOrderSummaryShippingBlock',
|
||||
'CheckoutOrderSummarySubtotalBlock',
|
||||
'CheckoutOrderSummaryTaxesBlock',
|
||||
'CheckoutOrderSummaryTotalsBlock',
|
||||
'CheckoutPaymentBlock',
|
||||
'CheckoutShippingAddressBlock',
|
||||
'CheckoutShippingMethodsBlock',
|
||||
|
||||
@@ -11,4 +11,70 @@ class CheckoutOrderSummaryBlock extends AbstractInnerBlock {
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'checkout-order-summary-block';
|
||||
|
||||
/**
|
||||
* Get the contents of the given inner block.
|
||||
*
|
||||
* @param string $block_name Name of the order summary inner block.
|
||||
* @param string $content The content to search.
|
||||
* @return array|bool
|
||||
*/
|
||||
private function get_inner_block_content( $block_name, $content ) {
|
||||
if ( preg_match( $this->inner_block_regex( $block_name ), $content, $matches ) ) {
|
||||
return $matches[0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regex that will return an inner block.
|
||||
*
|
||||
* @param string $block_name Name of the order summary inner block.
|
||||
* @return string Regex pattern.
|
||||
*/
|
||||
private function inner_block_regex( $block_name ) {
|
||||
return '/<div data-block-name="woocommerce\/checkout-order-summary-' . $block_name . '-block"(.+?)>(.*?)<\/div>/si';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Checkout Order Summary block.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
* @param string $content Block content.
|
||||
* @param object $block Block object.
|
||||
* @return string Rendered block.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
// The order-summary-totals block was introduced as a new parent block for the totals
|
||||
// (subtotal, discount, fees, shipping and taxes) blocks.
|
||||
$regex_for_checkout_order_summary_totals = '/<div data-block-name="woocommerce\/checkout-order-summary-totals-block"(.+?)>/';
|
||||
$order_summary_totals_content = '<div data-block-name="woocommerce/checkout-order-summary-totals-block" class="wp-block-woocommerce-checkout-order-summary-totals-block">';
|
||||
|
||||
// We want to move these blocks inside a parent 'totals' block.
|
||||
$totals_inner_blocks = array( 'subtotal', 'discount', 'fee', 'shipping', 'taxes' );
|
||||
|
||||
if ( preg_match( $regex_for_checkout_order_summary_totals, $content ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ( $totals_inner_blocks as $key => $block_name ) {
|
||||
$inner_block_content = $this->get_inner_block_content( $block_name, $content );
|
||||
|
||||
if ( $inner_block_content ) {
|
||||
$order_summary_totals_content .= "\n" . $inner_block_content;
|
||||
|
||||
// The last block is replaced with the totals block.
|
||||
if ( count( $totals_inner_blocks ) - 1 === $key ) {
|
||||
$order_summary_totals_content .= '</div>';
|
||||
$content = preg_replace( $this->inner_block_regex( $block_name ), $order_summary_totals_content, $content );
|
||||
} else {
|
||||
// Otherwise, remove the block.
|
||||
$content = preg_replace( $this->inner_block_regex( $block_name ), '', $content );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty lines.
|
||||
return preg_replace( '/\n\n( *?)/i', '', $content );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* CheckoutOrderSummaryTotalsBlock class.
|
||||
*/
|
||||
class CheckoutOrderSummaryTotalsBlock extends AbstractInnerBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'checkout-order-summary-totals-block';
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCategoryTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductTagTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils;
|
||||
@@ -49,7 +52,7 @@ class ClassicTemplate extends AbstractDynamicBlock {
|
||||
|
||||
// Indicate to interactivity powered components that this block is on the page,
|
||||
// and needs refresh to update data.
|
||||
$this->asset_data_registry->add( 'needsRefreshForInteractivityAPI', true, true );
|
||||
$this->asset_data_registry->add( 'needsRefreshForInteractivityAPI', true );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +102,7 @@ class ClassicTemplate extends AbstractDynamicBlock {
|
||||
$frontend_scripts::load_scripts();
|
||||
}
|
||||
|
||||
if ( OrderConfirmationTemplate::get_slug() === $attributes['template'] ) {
|
||||
if ( OrderConfirmationTemplate::SLUG === $attributes['template'] ) {
|
||||
return $this->render_order_received();
|
||||
}
|
||||
|
||||
@@ -109,9 +112,9 @@ class ClassicTemplate extends AbstractDynamicBlock {
|
||||
|
||||
$valid = false;
|
||||
$archive_templates = array(
|
||||
'archive-product',
|
||||
'taxonomy-product_cat',
|
||||
'taxonomy-product_tag',
|
||||
ProductCatalogTemplate::SLUG,
|
||||
ProductCategoryTemplate::SLUG,
|
||||
ProductTagTemplate::SLUG,
|
||||
ProductAttributeTemplate::SLUG,
|
||||
ProductSearchResultsTemplate::SLUG,
|
||||
);
|
||||
@@ -130,15 +133,14 @@ class ClassicTemplate extends AbstractDynamicBlock {
|
||||
|
||||
if ( $valid ) {
|
||||
// Set this so that our product filters can detect if it's a PHP template.
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true, true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true );
|
||||
|
||||
// Set this so filter blocks being used as widgets know when to render.
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true );
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'pageUrl',
|
||||
html_entity_decode( get_pagenum_link() ),
|
||||
''
|
||||
html_entity_decode( get_pagenum_link() )
|
||||
);
|
||||
|
||||
return $this->render_archive_product();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* ComingSoon class.
|
||||
*/
|
||||
class ComingSoon extends AbstractBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'coming-soon';
|
||||
|
||||
/**
|
||||
* It is necessary to register and enqueue assets during the render phase because we want to load assets only if the block has the content.
|
||||
*/
|
||||
protected function register_block_type_assets() {
|
||||
parent::register_block_type_assets();
|
||||
$this->register_chunk_translations( [ $this->block_name ] );
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,14 @@
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockHooksTrait;
|
||||
|
||||
/**
|
||||
* CustomerAccount class.
|
||||
*/
|
||||
class CustomerAccount extends AbstractBlock {
|
||||
use BlockHooksTrait;
|
||||
|
||||
const TEXT_ONLY = 'text_only';
|
||||
const ICON_ONLY = 'icon_only';
|
||||
const DISPLAY_ALT = 'alt';
|
||||
@@ -19,6 +22,85 @@ class CustomerAccount extends AbstractBlock {
|
||||
*/
|
||||
protected $block_name = 'customer-account';
|
||||
|
||||
/**
|
||||
* Block Hook API placements.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hooked_block_placements = array(
|
||||
array(
|
||||
'position' => 'after',
|
||||
'anchor' => 'core/navigation',
|
||||
'area' => 'header',
|
||||
'callback' => 'should_unhook_block',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize this block type.
|
||||
*/
|
||||
protected function initialize() {
|
||||
parent::initialize();
|
||||
/**
|
||||
* The hooked_block_{$hooked_block_type} filter was added in WordPress 6.5.
|
||||
* We are the only code adding the filter 'hooked_block_woocommerce/customer-account'.
|
||||
* Using has_filter() for a compatibility check won't work because add_filter() is used in the same file.
|
||||
*/
|
||||
if ( version_compare( get_bloginfo( 'version' ), '6.5', '>=' ) ) {
|
||||
add_filter( 'hooked_block_woocommerce/customer-account', array( $this, 'modify_hooked_block_attributes' ), 10, 5 );
|
||||
add_filter( 'hooked_block_types', array( $this, 'register_hooked_block' ), 9, 4 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the Block Hooks API to modify the attributes of the hooked block.
|
||||
*
|
||||
* @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block.
|
||||
* @param string $hooked_block_type The hooked block type name.
|
||||
* @param string $relative_position The relative position of the hooked block.
|
||||
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
|
||||
* @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type,
|
||||
* or pattern that the anchor block belongs to.
|
||||
* @return array|null
|
||||
*/
|
||||
public function modify_hooked_block_attributes( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) {
|
||||
$parsed_hooked_block['attrs']['displayStyle'] = 'icon_only';
|
||||
|
||||
/*
|
||||
* The Mini Cart block (which is hooked into the header) has a margin of 0.5em on the left side.
|
||||
* We want to match that margin for the Customer Account block so it looks consistent.
|
||||
*/
|
||||
$parsed_hooked_block['attrs']['style']['spacing']['margin']['left'] = '0.5em';
|
||||
return $parsed_hooked_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the Block Hooks API to determine if the block should be auto-inserted.
|
||||
*
|
||||
* @param array $hooked_blocks An array of block slugs hooked into a given context.
|
||||
* @param string $position Position of the block insertion point.
|
||||
* @param string $anchor_block The block acting as the anchor for the inserted block.
|
||||
* @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function should_unhook_block( $hooked_blocks, $position, $anchor_block, $context ) {
|
||||
$block_name = $this->namespace . '/' . $this->block_name;
|
||||
$block_is_hooked = in_array( $block_name, $hooked_blocks, true );
|
||||
|
||||
if ( $block_is_hooked ) {
|
||||
$active_theme = wp_get_theme()->get( 'Name' );
|
||||
$exclude_themes = array( 'Twenty Twenty-Two', 'Twenty Twenty-Three' );
|
||||
|
||||
if ( in_array( $active_theme, $exclude_themes, true ) ) {
|
||||
$key = array_search( $block_name, $hooked_blocks, true );
|
||||
unset( $hooked_blocks[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $hooked_blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the block.
|
||||
*
|
||||
@@ -47,9 +129,11 @@ class CustomerAccount extends AbstractBlock {
|
||||
),
|
||||
);
|
||||
|
||||
$label_markup = self::ICON_ONLY === $attributes['displayStyle'] ? '' : '<span class="label">' . wp_kses( $this->render_label(), array() ) . '</span>';
|
||||
|
||||
return "<div class='wp-block-woocommerce-customer-account " . esc_attr( $classes_and_styles['classes'] ) . "' style='" . esc_attr( $classes_and_styles['styles'] ) . "'>
|
||||
<a href='" . esc_attr( $account_link ) . "'>
|
||||
" . wp_kses( $this->render_icon( $attributes ), $allowed_svg ) . "<span class='label'>" . wp_kses( $this->render_label( $attributes ), array() ) . '</span>
|
||||
" . wp_kses( $this->render_icon( $attributes ), $allowed_svg ) . $label_markup . '
|
||||
</a>
|
||||
</div>';
|
||||
}
|
||||
@@ -93,15 +177,9 @@ class CustomerAccount extends AbstractBlock {
|
||||
/**
|
||||
* Gets the label to render depending on the displayStyle.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*
|
||||
* @return string Label to render on the block.
|
||||
*/
|
||||
private function render_label( $attributes ) {
|
||||
if ( self::ICON_ONLY === $attributes['displayStyle'] ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
private function render_label() {
|
||||
return get_current_user_id()
|
||||
? __( 'My Account', 'woocommerce' )
|
||||
: __( 'Login', 'woocommerce' );
|
||||
|
||||
@@ -322,6 +322,6 @@ abstract class FeaturedItem extends AbstractDynamicBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'defaultHeight', wc_get_theme_support( 'featured_block::default_height', 500 ), true );
|
||||
$this->asset_data_registry->add( 'defaultHeight', wc_get_theme_support( 'featured_block::default_height', 500 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class FeaturedProduct extends FeaturedItem {
|
||||
$id = absint( $attributes['productId'] ?? 0 );
|
||||
|
||||
$product = wc_get_product( $id );
|
||||
if ( ! $product ) {
|
||||
if ( ! $product || ( 'publish' !== $product->get_status() && ! current_user_can( 'read_product', $id ) ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,15 +159,13 @@ class MiniCart extends AbstractBlock {
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'taxLabel',
|
||||
$this->tax_label,
|
||||
''
|
||||
$this->tax_label
|
||||
);
|
||||
}
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'displayCartPricesIncludingTax',
|
||||
$this->display_cart_prices_including_tax,
|
||||
true
|
||||
$this->display_cart_prices_including_tax
|
||||
);
|
||||
|
||||
$template_part_edit_uri = '';
|
||||
@@ -206,8 +204,7 @@ class MiniCart extends AbstractBlock {
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'templatePartEditUri',
|
||||
$template_part_edit_uri,
|
||||
''
|
||||
$template_part_edit_uri
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,8 +35,8 @@ class AdditionalFields extends AbstractOrderConfirmationBlock {
|
||||
$content .= $this->render_additional_fields(
|
||||
$controller->filter_fields_for_order_confirmation(
|
||||
array_merge(
|
||||
$controller->get_order_additional_fields_with_values( $order, 'contact', '', 'view' ),
|
||||
$controller->get_order_additional_fields_with_values( $order, 'additional', '', 'view' ),
|
||||
$controller->get_order_additional_fields_with_values( $order, 'contact', 'other', 'view' ),
|
||||
$controller->get_order_additional_fields_with_values( $order, 'order', 'other', 'view' ),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ class AdditionalFieldsWrapper extends AbstractOrderConfirmationBlock {
|
||||
// Contact and additional fields are currently grouped in this section.
|
||||
$additional_fields = array_merge(
|
||||
Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'contact' ),
|
||||
Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'additional' )
|
||||
Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'order' )
|
||||
);
|
||||
|
||||
return empty( $additional_fields ) ? '' : $content;
|
||||
@@ -48,7 +48,7 @@ class AdditionalFieldsWrapper extends AbstractOrderConfirmationBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'additionalFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'additional' ) );
|
||||
$this->asset_data_registry->add( 'additionalFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'order' ) );
|
||||
$this->asset_data_registry->add( 'additionalContactFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'contact' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@ class BillingAddress extends AbstractOrderConfirmationBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'additionalAddressFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'address' ), true );
|
||||
$this->asset_data_registry->add( 'additionalAddressFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'address' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@ class ShippingAddress extends AbstractOrderConfirmationBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'additionalAddressFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'address' ), true );
|
||||
$this->asset_data_registry->add( 'additionalAddressFields', Package::container()->get( CheckoutFields::class )->get_fields_for_location( 'address' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* ProductAddToCart class.
|
||||
*/
|
||||
class ProductAddToCart extends AbstractBlock {
|
||||
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'product-add-to-cart';
|
||||
|
||||
/**
|
||||
* API version name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $api_version = '2';
|
||||
|
||||
/**
|
||||
* Register script and style assets for the block type before it is registered.
|
||||
*
|
||||
* This registers the scripts; it does not enqueue them.
|
||||
*/
|
||||
protected function register_block_type_assets() {
|
||||
parent::register_block_type_assets();
|
||||
$this->register_chunk_translations( [ $this->block_name ] );
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,10 @@ class ProductCollection extends AbstractBlock {
|
||||
add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 );
|
||||
|
||||
add_filter( 'posts_clauses', array( $this, 'add_price_range_filter_posts_clauses' ), 10, 2 );
|
||||
|
||||
// Disable client-side-navigation if incompatible blocks are detected.
|
||||
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,8 +99,9 @@ class ProductCollection extends AbstractBlock {
|
||||
* @return string Updated block content with added interactivity attributes.
|
||||
*/
|
||||
public function enhance_product_collection_with_interactivity( $block_content, $block ) {
|
||||
$is_product_collection_block = $block['attrs']['query']['isProductCollectionBlock'] ?? false;
|
||||
if ( $is_product_collection_block ) {
|
||||
$is_product_collection_block = $block['attrs']['query']['isProductCollectionBlock'] ?? false;
|
||||
$is_enhanced_pagination_enabled = ! ( $block['attrs']['forcePageReload'] ?? false );
|
||||
if ( $is_product_collection_block && $is_enhanced_pagination_enabled ) {
|
||||
// Enqueue the Interactivity API runtime.
|
||||
wp_enqueue_script( 'wc-interactivity' );
|
||||
|
||||
@@ -166,13 +171,15 @@ class ProductCollection extends AbstractBlock {
|
||||
* @param \WP_Block $instance The block instance.
|
||||
*/
|
||||
public function add_navigation_link_directives( $block_content, $block, $instance ) {
|
||||
$query_context = $instance->context['query'] ?? array();
|
||||
$is_product_collection_block = $query_context['isProductCollectionBlock'] ?? false;
|
||||
$query_id = $instance->context['queryId'] ?? null;
|
||||
$parsed_query_id = $this->parsed_block['attrs']['queryId'] ?? null;
|
||||
$query_context = $instance->context['query'] ?? array();
|
||||
$is_product_collection_block = $query_context['isProductCollectionBlock'] ?? false;
|
||||
$query_id = $instance->context['queryId'] ?? null;
|
||||
$parsed_query_id = $this->parsed_block['attrs']['queryId'] ?? null;
|
||||
$is_enhanced_pagination_enabled = ! ( $this->parsed_block['attrs']['forcePageReload'] ?? false );
|
||||
|
||||
// Only proceed if the block is a product collection block and query IDs match.
|
||||
if ( $is_product_collection_block && $query_id === $parsed_query_id ) {
|
||||
// Only proceed if the block is a product collection block,
|
||||
// enhaced pagination is enabled and query IDs match.
|
||||
if ( $is_product_collection_block && $is_enhanced_pagination_enabled && $query_id === $parsed_query_id ) {
|
||||
$block_content = $this->process_pagination_links( $block_content );
|
||||
}
|
||||
|
||||
@@ -231,6 +238,110 @@ class ProductCollection extends AbstractBlock {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the inner block is compatible with Interactivity API.
|
||||
*
|
||||
* @param string $block_name Name of the block to verify.
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_block_compatible( $block_name ) {
|
||||
// Check for explicitly unsupported blocks.
|
||||
if ( 'core/post-content' === $block_name ||
|
||||
'woocommerce/mini-cart' === $block_name ||
|
||||
'woocommerce/featured-product' === $block_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported prefixes.
|
||||
if (
|
||||
str_starts_with( $block_name, 'core/' ) ||
|
||||
str_starts_with( $block_name, 'woocommerce/' )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise block is unsupported.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check inner blocks of Product Collection block if there's one
|
||||
* incompatible with Interactivity API and if so, disable client-side
|
||||
* naviagtion.
|
||||
*
|
||||
* @param array $parsed_block The block being rendered.
|
||||
* @return string Returns the parsed block, unmodified.
|
||||
*/
|
||||
public function disable_enhanced_pagination( $parsed_block ) {
|
||||
static $enhanced_query_stack = array();
|
||||
static $dirty_enhanced_queries = array();
|
||||
static $render_product_collection_callback = null;
|
||||
|
||||
$block_name = $parsed_block['blockName'];
|
||||
$force_page_reload_global =
|
||||
$parsed_block['attrs']['forcePageReload'] ?? false &&
|
||||
isset( $block['attrs']['queryId'] );
|
||||
|
||||
if (
|
||||
'woocommerce/product-collection' === $block_name &&
|
||||
! $force_page_reload_global
|
||||
) {
|
||||
$enhanced_query_stack[] = $parsed_block['attrs']['queryId'];
|
||||
|
||||
if ( ! isset( $render_product_collection_callback ) ) {
|
||||
/**
|
||||
* Filter that disables the enhanced pagination feature during block
|
||||
* rendering when a plugin block has been found inside. It does so
|
||||
* by adding an attribute called `data-wp-navigation-disabled` which
|
||||
* is later handled by the front-end logic.
|
||||
*
|
||||
* @param string $content The block content.
|
||||
* @param array $block The full block, including name and attributes.
|
||||
* @return string Returns the modified output of the query block.
|
||||
*/
|
||||
$render_product_collection_callback = static function ( $content, $block ) use ( &$enhanced_query_stack, &$dirty_enhanced_queries, &$render_product_collection_callback ) {
|
||||
$force_page_reload =
|
||||
$parsed_block['attrs']['forcePageReload'] ?? false &&
|
||||
isset( $block['attrs']['queryId'] );
|
||||
|
||||
if ( $force_page_reload ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
if ( isset( $dirty_enhanced_queries[ $block['attrs']['queryId'] ] ) ) {
|
||||
$p = new \WP_HTML_Tag_Processor( $content );
|
||||
if ( $p->next_tag() ) {
|
||||
$p->set_attribute( 'data-wc-navigation-disabled', 'true' );
|
||||
}
|
||||
$content = $p->get_updated_html();
|
||||
$dirty_enhanced_queries[ $block['attrs']['queryId'] ] = null;
|
||||
}
|
||||
|
||||
array_pop( $enhanced_query_stack );
|
||||
|
||||
if ( empty( $enhanced_query_stack ) ) {
|
||||
remove_filter( 'render_block_woocommerce/product-collection', $render_product_collection_callback );
|
||||
$render_product_collection_callback = null;
|
||||
}
|
||||
|
||||
return $content;
|
||||
};
|
||||
|
||||
add_filter( 'render_block_woocommerce/product-collection', $render_product_collection_callback, 10, 2 );
|
||||
}
|
||||
} elseif (
|
||||
! empty( $enhanced_query_stack ) &&
|
||||
isset( $block_name ) &&
|
||||
! $this->is_block_compatible( $block_name )
|
||||
) {
|
||||
foreach ( $enhanced_query_stack as $query_id ) {
|
||||
$dirty_enhanced_queries[ $query_id ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data passed through from server to client for block.
|
||||
*
|
||||
@@ -243,7 +354,7 @@ class ProductCollection extends AbstractBlock {
|
||||
|
||||
// The `loop_shop_per_page` filter can be found in WC_Query::product_query().
|
||||
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
$this->asset_data_registry->add( 'loopShopPerPage', apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() ), true );
|
||||
$this->asset_data_registry->add( 'loopShopPerPage', apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,12 +415,12 @@ class ProductCollection extends AbstractBlock {
|
||||
}
|
||||
|
||||
$this->parsed_block = $parsed_block;
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true );
|
||||
/**
|
||||
* It enables the page to refresh when a filter is applied, ensuring that the product collection block,
|
||||
* which is a server-side rendered (SSR) block, retrieves the products that match the filters.
|
||||
*/
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true, true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true );
|
||||
|
||||
return $pre_render;
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ final class ProductFilter extends AbstractBlock {
|
||||
global $pagenow;
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme(), true );
|
||||
$this->asset_data_registry->add( 'isProductArchive', is_shop() || is_product_taxonomy(), true );
|
||||
$this->asset_data_registry->add( 'isSiteEditor', 'site-editor.php' === $pagenow, true );
|
||||
$this->asset_data_registry->add( 'isWidgetEditor', 'widgets.php' === $pagenow || 'customize.php' === $pagenow, true );
|
||||
$this->asset_data_registry->add( 'isBlockTheme', wc_current_theme_is_fse_theme() );
|
||||
$this->asset_data_registry->add( 'isProductArchive', is_shop() || is_product_taxonomy() );
|
||||
$this->asset_data_registry->add( 'isSiteEditor', 'site-editor.php' === $pagenow );
|
||||
$this->asset_data_registry->add( 'isWidgetEditor', 'widgets.php' === $pagenow || 'customize.php' === $pagenow );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,7 +141,6 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
) = $attributes;
|
||||
|
||||
$wrapper_attributes = array(
|
||||
'class' => $show_input_fields && $inline_input ? 'inline-input' : '',
|
||||
'data-wc-interactive' => wp_json_encode( array( 'namespace' => $this->get_full_block_name() ) ),
|
||||
'data-wc-context' => wp_json_encode( $data ),
|
||||
'data-has-filter' => 'no',
|
||||
@@ -163,12 +162,14 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
sprintf(
|
||||
'<input
|
||||
class="min"
|
||||
name="min"
|
||||
type="text"
|
||||
value="%d"
|
||||
data-wc-bind--value="context.minPrice"
|
||||
value="%s"
|
||||
data-wc-bind--value="state.formattedMinPrice"
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
pattern=""
|
||||
/>',
|
||||
esc_attr( $min_price )
|
||||
wp_strip_all_tags( $formatted_min_price )
|
||||
) : sprintf(
|
||||
'<span data-wc-text="state.formattedMinPrice">%s</span>',
|
||||
// Not escaped, as this is HTML.
|
||||
@@ -179,12 +180,13 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
sprintf(
|
||||
'<input
|
||||
class="max"
|
||||
name="max"
|
||||
type="text"
|
||||
value="%d"
|
||||
data-wc-bind--value="context.maxPrice"
|
||||
value="%s"
|
||||
data-wc-bind--value="state.formattedMaxPrice"
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
/>',
|
||||
esc_attr( $max_price )
|
||||
wp_strip_all_tags( $formatted_max_price )
|
||||
) : sprintf(
|
||||
'<span data-wc-text="state.formattedMaxPrice">%s</span>',
|
||||
// Not escaped, as this is HTML.
|
||||
@@ -193,13 +195,23 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
|
||||
$wrapper_attributes['data-has-filter'] = 'yes';
|
||||
|
||||
$filter_price_content_classes = array(
|
||||
'wp-block-woocommerce-product-filter-price-content',
|
||||
$show_input_fields && $inline_input ? 'wp-block-woocommerce-product-filter-price-content--inline' : '',
|
||||
);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div <?php echo get_block_wrapper_attributes( $wrapper_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<?php echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<div class="filter-controls">
|
||||
<div
|
||||
class="<?php echo esc_attr( implode( ' ', $filter_price_content_classes ) ); ?>"
|
||||
>
|
||||
<div class="wp-block-woocommerce-product-filter-price-content-left-input text">
|
||||
<?php echo $price_min; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
</div>
|
||||
<div
|
||||
class="range"
|
||||
class="wp-block-woocommerce-product-filter-price-content-price-range-slider range"
|
||||
style="<?php echo esc_attr( $range_style ); ?>"
|
||||
data-wc-bind--style="state.rangeStyle"
|
||||
>
|
||||
@@ -229,9 +241,8 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
>
|
||||
</div>
|
||||
<div class="text">
|
||||
<div class="wp-block-woocommerce-product-filter-price-content-right-input text">
|
||||
<?php // $price_min and $price_max are escaped in the sprintf() calls above. ?>
|
||||
<?php echo $price_min; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php echo $price_max; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,8 +110,8 @@ final class ProductFilterStockStatus extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $stock_statuses = [] ) {
|
||||
parent::enqueue_data( $stock_statuses );
|
||||
$this->asset_data_registry->add( 'stockStatusOptions', wc_get_product_stock_status_options(), true );
|
||||
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ), true );
|
||||
$this->asset_data_registry->add( 'stockStatusOptions', wc_get_product_stock_status_options() );
|
||||
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,7 +180,7 @@ class ProductImage extends AbstractBlock {
|
||||
* not in the post content on editor load.
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
$this->asset_data_registry->add( 'isBlockThemeEnabled', wc_current_theme_is_fse_theme(), false );
|
||||
$this->asset_data_registry->add( 'isBlockThemeEnabled', wc_current_theme_is_fse_theme() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class ProductQuery extends AbstractBlock {
|
||||
|
||||
// The `loop_shop_per_page` filter can be found in WC_Query::product_query().
|
||||
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
$this->asset_data_registry->add( 'loopShopPerPage', apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() ), true );
|
||||
$this->asset_data_registry->add( 'loopShopPerPage', apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,12 +180,11 @@ class ProductQuery extends AbstractBlock {
|
||||
// and needs refresh to update data.
|
||||
$this->asset_data_registry->add(
|
||||
'needsRefreshForInteractivityAPI',
|
||||
true,
|
||||
true
|
||||
);
|
||||
// Set this so that our product filters can detect if it's a PHP template.
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true, true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true );
|
||||
add_filter(
|
||||
'query_loop_block_query_vars',
|
||||
array( $this, 'build_query' ),
|
||||
|
||||
@@ -111,7 +111,7 @@ class ProductRating extends AbstractBlock {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$post_id = $block->context['postId'];
|
||||
$post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : '';
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( $product && $product->get_review_count() > 0 ) {
|
||||
|
||||
@@ -35,19 +35,34 @@ class ProductResultsCount extends AbstractBlock {
|
||||
* @return string Rendered block output.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
// Buffer the result count and use it as the block's frontend content.
|
||||
ob_start();
|
||||
echo '<div>';
|
||||
woocommerce_result_count();
|
||||
echo '</div>';
|
||||
$product_results_count = ob_get_clean();
|
||||
|
||||
$classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes );
|
||||
$classname = isset( $attributes['className'] ) ? $attributes['className'] : '';
|
||||
$p = new \WP_HTML_Tag_Processor( $product_results_count );
|
||||
|
||||
return sprintf(
|
||||
'<div class="woocommerce wc-block-product-results-count wp-block-woocommerce-product-results-count %1$s %2$s" style="%3$s">%4$s</div>',
|
||||
esc_attr( $classes_and_styles['classes'] ),
|
||||
esc_attr( $classname ),
|
||||
esc_attr( $classes_and_styles['styles'] ),
|
||||
$product_results_count
|
||||
// Advance to the wrapper and add the attributes necessary for the block.
|
||||
$p->next_tag( 'div' );
|
||||
$parsed_style_attributes = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes );
|
||||
$classes = array_merge(
|
||||
explode( ' ', $parsed_style_attributes['classes'] ),
|
||||
array(
|
||||
'woocommerce',
|
||||
'wc-block-product-results-count',
|
||||
'wp-block-woocommerce-product-results-count',
|
||||
),
|
||||
isset( $attributes['className'] ) ? array( $attributes['className'] ) : array(),
|
||||
);
|
||||
$p->set_attribute( 'class', implode( ' ', $classes ) );
|
||||
$p->set_attribute( 'style', $parsed_style_attributes['styles'] );
|
||||
$p->set_attribute(
|
||||
'data-wc-navigation-id',
|
||||
'wc-product-results-count-' . ( isset( $block->context['queryId'] ) ? $block->context['queryId'] : 0 )
|
||||
);
|
||||
|
||||
return $p->get_updated_html();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ProductSKU extends AbstractBlock {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$post_id = $block->context['postId'];
|
||||
$post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : '';
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
|
||||
@@ -97,7 +97,7 @@ class ProductSaleBadge extends AbstractBlock {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$post_id = $block->context['postId'];
|
||||
$post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : '';
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
|
||||
@@ -45,10 +45,10 @@ class ProductStockIndicator extends AbstractBlock {
|
||||
* - Available on backorder
|
||||
* - 2 left in stock
|
||||
*
|
||||
* @param [bool] $is_in_stock Whether the product is in stock.
|
||||
* @param [bool] $is_low_stock Whether the product is low in stock.
|
||||
* @param [int|null] $low_stock_amount The amount of stock that is considered low.
|
||||
* @param [bool] $is_on_backorder Whether the product is on backorder.
|
||||
* @param bool $is_in_stock Whether the product is in stock.
|
||||
* @param bool $is_low_stock Whether the product is low in stock.
|
||||
* @param int|null $low_stock_amount The amount of stock that is considered low.
|
||||
* @param bool $is_on_backorder Whether the product is on backorder.
|
||||
* @return string Stock text.
|
||||
*/
|
||||
protected static function getTextBasedOnStock( $is_in_stock, $is_low_stock, $low_stock_amount, $is_on_backorder ) {
|
||||
@@ -84,7 +84,7 @@ class ProductStockIndicator extends AbstractBlock {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$post_id = $block->context['postId'];
|
||||
$post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : '';
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
|
||||
@@ -64,7 +64,7 @@ class ProductTag extends AbstractProductGrid {
|
||||
|
||||
$tag_count = wp_count_terms( 'product_tag' );
|
||||
|
||||
$this->asset_data_registry->add( 'hasTags', $tag_count > 0, true );
|
||||
$this->asset_data_registry->add( 'limitTags', $tag_count > 100, true );
|
||||
$this->asset_data_registry->add( 'hasTags', $tag_count > 0 );
|
||||
$this->asset_data_registry->add( 'limitTags', $tag_count > 100 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class ReviewsByCategory extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled(), true );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ), true );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled() );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class ReviewsByProduct extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled(), true );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ), true );
|
||||
$this->asset_data_registry->add( 'reviewRatingsEnabled', wc_review_ratings_enabled() );
|
||||
$this->asset_data_registry->add( 'showAvatars', '1' === get_option( 'show_avatars' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class StockFilter extends AbstractBlock {
|
||||
*/
|
||||
protected function enqueue_data( array $stock_statuses = [] ) {
|
||||
parent::enqueue_data( $stock_statuses );
|
||||
$this->asset_data_registry->add( 'stockStatusOptions', wc_get_product_stock_status_options(), true );
|
||||
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ), true );
|
||||
$this->asset_data_registry->add( 'stockStatusOptions', wc_get_product_stock_status_options() );
|
||||
$this->asset_data_registry->add( 'hideOutOfStockItems', 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -54,14 +54,14 @@ final class BlockTypesController {
|
||||
add_action( 'woocommerce_delete_product_transients', array( $this, 'delete_product_transients' ) );
|
||||
add_filter(
|
||||
'woocommerce_is_checkout',
|
||||
function( $return ) {
|
||||
return $return || $this->has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', 'checkout' );
|
||||
function ( $ret ) {
|
||||
return $ret || $this->has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', 'checkout' );
|
||||
}
|
||||
);
|
||||
add_filter(
|
||||
'woocommerce_is_cart',
|
||||
function( $return ) {
|
||||
return $return || $this->has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', 'cart' );
|
||||
function ( $ret ) {
|
||||
return $ret || $this->has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', 'cart' );
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -225,6 +225,7 @@ final class BlockTypesController {
|
||||
'CatalogSorting',
|
||||
'ClassicTemplate',
|
||||
'ClassicShortcode',
|
||||
'ComingSoon',
|
||||
'CustomerAccount',
|
||||
'FeaturedCategory',
|
||||
'FeaturedProduct',
|
||||
@@ -233,7 +234,6 @@ final class BlockTypesController {
|
||||
'MiniCart',
|
||||
'StoreNotices',
|
||||
'PriceFilter',
|
||||
'ProductAddToCart',
|
||||
'ProductBestSellers',
|
||||
'ProductButton',
|
||||
'ProductCategories',
|
||||
@@ -352,5 +352,4 @@ final class BlockTypesController {
|
||||
|
||||
return $block_types;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Domain;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use Automattic\WooCommerce\Blocks\AssetsController;
|
||||
use Automattic\WooCommerce\Blocks\BlockPatterns;
|
||||
use Automattic\WooCommerce\Blocks\BlockTemplatesRegistry;
|
||||
use Automattic\WooCommerce\Blocks\BlockTemplatesController;
|
||||
use Automattic\WooCommerce\Blocks\BlockTypesController;
|
||||
use Automattic\WooCommerce\Blocks\QueryFilters;
|
||||
@@ -27,13 +29,7 @@ use Automattic\WooCommerce\Blocks\Payments\Integrations\Cheque;
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\PayPal;
|
||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use Automattic\WooCommerce\Blocks\Registry\Container;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutHeaderTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ClassicTemplatesCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\StoreApi\RoutesController;
|
||||
use Automattic\WooCommerce\StoreApi\SchemaController;
|
||||
use Automattic\WooCommerce\StoreApi\StoreApi;
|
||||
@@ -151,13 +147,8 @@ class Bootstrap {
|
||||
// regular rest requests to maintain compatibility with the store editor.
|
||||
$this->container->get( BlockPatterns::class );
|
||||
$this->container->get( BlockTypesController::class );
|
||||
$this->container->get( BlockTemplatesController::class );
|
||||
$this->container->get( ProductSearchResultsTemplate::class );
|
||||
$this->container->get( ProductAttributeTemplate::class );
|
||||
$this->container->get( CartTemplate::class );
|
||||
$this->container->get( CheckoutTemplate::class );
|
||||
$this->container->get( CheckoutHeaderTemplate::class );
|
||||
$this->container->get( OrderConfirmationTemplate::class );
|
||||
$this->container->get( BlockTemplatesRegistry::class )->init();
|
||||
$this->container->get( BlockTemplatesController::class )->init();
|
||||
$this->container->get( ClassicTemplatesCompatibility::class );
|
||||
$this->container->get( ArchiveProductTemplatesCompatibility::class )->init();
|
||||
$this->container->get( SingleProductTemplateCompatibility::class )->init();
|
||||
@@ -190,11 +181,12 @@ class Bootstrap {
|
||||
function() {
|
||||
echo '<div class="error"><p>';
|
||||
printf(
|
||||
/* translators: %1$s is the install command, %2$s is the build command, %3$s is the watch command. */
|
||||
esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the plugin directory, run %1$s to install dependencies, %2$s to build the files or %3$s to build the files and watch for changes.', 'woocommerce' ),
|
||||
'<code>npm install</code>',
|
||||
'<code>npm run build</code>',
|
||||
'<code>npm start</code>'
|
||||
/* translators: %1$s is the node install command, %2$s is the install command, %3$s is the build command, %4$s is the watch command. */
|
||||
esc_html__( 'WooCommerce Blocks development mode requires files to be built. From the root directory, run %1$s to ensure your node version is aligned, run %2$s to install dependencies, %3$s to build the files or %4$s to build the files and watch for changes.', 'woocommerce' ),
|
||||
'<code>nvm use</code>',
|
||||
'<code>pnpm install</code>',
|
||||
'<code>pnpm --filter="@woocommerce/plugin-woocommerce" build</code>',
|
||||
'<code>pnpm --filter="@woocommerce/plugin-woocommerce" watch:build</code>'
|
||||
);
|
||||
echo '</p></div>';
|
||||
}
|
||||
@@ -256,46 +248,16 @@ class Bootstrap {
|
||||
return new BlockTypesController( $asset_api, $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesRegistry::class,
|
||||
function ( Container $container ) {
|
||||
return new BlockTemplatesRegistry();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesController::class,
|
||||
function ( Container $container ) {
|
||||
return new BlockTemplatesController( $container->get( Package::class ) );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ProductSearchResultsTemplate::class,
|
||||
function () {
|
||||
return new ProductSearchResultsTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ProductAttributeTemplate::class,
|
||||
function () {
|
||||
return new ProductAttributeTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CartTemplate::class,
|
||||
function () {
|
||||
return new CartTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CheckoutTemplate::class,
|
||||
function () {
|
||||
return new CheckoutTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
CheckoutHeaderTemplate::class,
|
||||
function () {
|
||||
return new CheckoutHeaderTemplate();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
OrderConfirmationTemplate::class,
|
||||
function () {
|
||||
return new OrderConfirmationTemplate();
|
||||
return new BlockTemplatesController();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
@@ -311,7 +273,6 @@ class Bootstrap {
|
||||
return new ArchiveProductTemplatesCompatibility();
|
||||
}
|
||||
);
|
||||
|
||||
$this->container->register(
|
||||
SingleProductTemplateCompatibility::class,
|
||||
function () {
|
||||
@@ -387,28 +348,28 @@ class Bootstrap {
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\Formatters',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\Formatters', '7.2.0', 'Automattic\WooCommerce\StoreApi\Formatters', '7.4.0' );
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\Formatters', '6.4.0', 'Automattic\WooCommerce\StoreApi\Formatters', '6.5.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Formatters::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi', '7.2.0', 'Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema', '7.4.0' );
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi', '6.4.0', 'Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema', '6.5.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( \Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\SchemaController',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\SchemaController', '7.2.0', 'Automattic\WooCommerce\StoreApi\SchemaController', '7.4.0' );
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\SchemaController', '6.4.0', 'Automattic\WooCommerce\StoreApi\SchemaController', '6.5.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( SchemaController::class );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\RoutesController',
|
||||
function( Container $container ) {
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\RoutesController', '7.2.0', 'Automattic\WooCommerce\StoreApi\RoutesController', '7.4.0' );
|
||||
$this->deprecated_dependency( 'Automattic\WooCommerce\Blocks\StoreApi\RoutesController', '6.4.0', 'Automattic\WooCommerce\StoreApi\RoutesController', '6.5.0' );
|
||||
return $container->get( StoreApi::class )->container()->get( RoutesController::class );
|
||||
}
|
||||
);
|
||||
@@ -479,7 +440,7 @@ class Bootstrap {
|
||||
}
|
||||
|
||||
// If the $trigger_error_version was not yet reached, only log the error.
|
||||
if ( version_compare( $this->package->get_version(), $trigger_error_version, '<' ) ) {
|
||||
if ( version_compare( Constants::get_constant( 'WC_VERSION' ), $trigger_error_version, '<' ) ) {
|
||||
$log_error = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,12 @@ class Package {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the plugin.
|
||||
* Returns the version of WooCommerce Blocks.
|
||||
*
|
||||
* Note: since Blocks was merged into WooCommerce Core, the version of
|
||||
* WC Blocks doesn't update anymore. Use
|
||||
* `Constants::get_constant( 'WC_VERSION' )` when possible to get the
|
||||
* WooCommerce Core version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -65,7 +70,7 @@ class Package {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the plugin stored in the database.
|
||||
* Returns the version of WooCommerce Blocks stored in the database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -74,12 +79,11 @@ class Package {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version of the plugin stored in the database.
|
||||
* Sets the version of WooCommerce Blocks in the database.
|
||||
* This is useful during the first installation or after the upgrade process.
|
||||
*/
|
||||
public function set_version_stored_on_db() {
|
||||
update_option( Options::WC_BLOCK_VERSION, $this->get_version() );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ class CheckoutFieldsAdmin {
|
||||
add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_address_fields' ), 10, 3 );
|
||||
add_filter( 'woocommerce_admin_billing_fields', array( $this, 'admin_contact_fields' ), 10, 3 );
|
||||
add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_address_fields' ), 10, 3 );
|
||||
add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_additional_fields' ), 10, 3 );
|
||||
add_filter( 'woocommerce_admin_shipping_fields', array( $this, 'admin_order_fields' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +73,9 @@ class CheckoutFieldsAdmin {
|
||||
* @param \WC_Order $order The order to update the field for.
|
||||
*/
|
||||
public function update_callback( $key, $value, $order ) {
|
||||
$this->checkout_fields_controller->persist_field_for_order( $key, $value, $order, false );
|
||||
list( $group, $key ) = explode( '/', $key, 2 );
|
||||
$group = CheckoutFields::get_group_name( $group );
|
||||
$this->checkout_fields_controller->persist_field_for_order( $key, $value, $order, $group, false );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,11 +91,11 @@ class CheckoutFieldsAdmin {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$group = doing_action( 'woocommerce_admin_billing_fields' ) ? 'billing' : 'shipping';
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'address', $group, $context );
|
||||
$group_name = doing_action( 'woocommerce_admin_billing_fields' ) ? 'billing' : 'shipping';
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'address', $group_name, $context );
|
||||
foreach ( $additional_fields as $key => $field ) {
|
||||
$group_key = '/' . $group . '/' . $key;
|
||||
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $group_key );
|
||||
$prefixed_key = CheckoutFields::get_group_key( $group_name ) . $key;
|
||||
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
|
||||
}
|
||||
|
||||
array_splice(
|
||||
@@ -123,16 +125,14 @@ class CheckoutFieldsAdmin {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', '', $context );
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', 'other', $context );
|
||||
|
||||
return array_merge(
|
||||
$fields,
|
||||
array_map(
|
||||
array( $this, 'format_field_for_meta_box' ),
|
||||
$additional_fields,
|
||||
array_keys( $additional_fields )
|
||||
)
|
||||
);
|
||||
foreach ( $additional_fields as $key => $field ) {
|
||||
$prefixed_key = CheckoutFields::get_group_key( 'other' ) . $key;
|
||||
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
|
||||
}
|
||||
|
||||
return array_merge( $fields, $additional_fields );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,20 +143,18 @@ class CheckoutFieldsAdmin {
|
||||
* @param string $context The context to show the fields for.
|
||||
* @return array
|
||||
*/
|
||||
public function admin_additional_fields( $fields, $order = null, $context = 'edit' ) {
|
||||
public function admin_order_fields( $fields, $order = null, $context = 'edit' ) {
|
||||
if ( ! $order instanceof \WC_Order ) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'additional', '', $context );
|
||||
$additional_fields = $this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'order', 'other', $context );
|
||||
|
||||
return array_merge(
|
||||
$fields,
|
||||
array_map(
|
||||
array( $this, 'format_field_for_meta_box' ),
|
||||
$additional_fields,
|
||||
array_keys( $additional_fields )
|
||||
)
|
||||
);
|
||||
foreach ( $additional_fields as $key => $field ) {
|
||||
$prefixed_key = CheckoutFields::get_group_key( 'other' ) . $key;
|
||||
$additional_fields[ $key ] = $this->format_field_for_meta_box( $field, $prefixed_key );
|
||||
}
|
||||
|
||||
return array_merge( $fields, $additional_fields );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class CheckoutFieldsFrontend {
|
||||
public function init() {
|
||||
// Show custom checkout fields on the order details page.
|
||||
add_action( 'woocommerce_order_details_after_customer_address', array( $this, 'render_order_address_fields' ), 10, 2 );
|
||||
add_action( 'woocommerce_order_details_after_customer_details', array( $this, 'render_order_additional_fields' ), 10 );
|
||||
add_action( 'woocommerce_order_details_after_customer_details', array( $this, 'render_order_other_fields' ), 10 );
|
||||
|
||||
// Show custom checkout fields on the My Account page.
|
||||
add_action( 'woocommerce_my_account_after_my_address', array( $this, 'render_address_fields' ), 10, 1 );
|
||||
@@ -87,10 +87,10 @@ class CheckoutFieldsFrontend {
|
||||
*
|
||||
* @param WC_Order $order Order object.
|
||||
*/
|
||||
public function render_order_additional_fields( $order ) {
|
||||
public function render_order_other_fields( $order ) {
|
||||
$fields = array_merge(
|
||||
$this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', '', 'view' ),
|
||||
$this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'additional', '', 'view' ),
|
||||
$this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'contact', 'other', 'view' ),
|
||||
$this->checkout_fields_controller->get_order_additional_fields_with_values( $order, 'order', 'other', 'view' ),
|
||||
);
|
||||
|
||||
if ( ! $fields ) {
|
||||
@@ -122,7 +122,7 @@ class CheckoutFieldsFrontend {
|
||||
|
||||
foreach ( $fields as $key => $field ) {
|
||||
$value = $this->checkout_fields_controller->format_additional_field_value(
|
||||
$this->checkout_fields_controller->get_field_from_customer( $key, $customer, $address_type ),
|
||||
$this->checkout_fields_controller->get_field_from_object( $key, $customer, $address_type ),
|
||||
$field
|
||||
);
|
||||
|
||||
@@ -160,8 +160,10 @@ class CheckoutFieldsFrontend {
|
||||
$fields = $this->checkout_fields_controller->get_fields_for_location( 'contact' );
|
||||
|
||||
foreach ( $fields as $key => $field ) {
|
||||
$field_key = CheckoutFields::get_group_key( 'other' ) . $key;
|
||||
$form_field = $field;
|
||||
$form_field['value'] = $this->checkout_fields_controller->get_field_from_customer( $key, $customer, 'contact' );
|
||||
$form_field['id'] = $field_key;
|
||||
$form_field['value'] = $this->checkout_fields_controller->get_field_from_object( $key, $customer, 'contact' );
|
||||
|
||||
if ( 'select' === $field['type'] ) {
|
||||
$form_field['options'] = array_column( $field['options'], 'label', 'value' );
|
||||
@@ -189,12 +191,13 @@ class CheckoutFieldsFrontend {
|
||||
$additional_fields = $this->checkout_fields_controller->get_fields_for_location( 'contact' );
|
||||
$field_values = array();
|
||||
|
||||
foreach ( $additional_fields as $key => $field ) {
|
||||
if ( ! isset( $_POST[ $key ] ) ) {
|
||||
foreach ( array_keys( $additional_fields ) as $key ) {
|
||||
$post_key = CheckoutFields::get_group_key( 'other' ) . $key;
|
||||
if ( ! isset( $_POST[ $post_key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$field_value = $this->checkout_fields_controller->sanitize_field( $key, wc_clean( wp_unslash( $_POST[ $key ] ) ) );
|
||||
$field_value = $this->checkout_fields_controller->sanitize_field( $key, wc_clean( wp_unslash( $_POST[ $post_key ] ) ) );
|
||||
$validation = $this->checkout_fields_controller->validate_field( $key, $field_value );
|
||||
|
||||
if ( is_wp_error( $validation ) && $validation->has_errors() ) {
|
||||
@@ -207,11 +210,11 @@ class CheckoutFieldsFrontend {
|
||||
|
||||
// Persist individual additional fields to customer.
|
||||
foreach ( $field_values as $key => $value ) {
|
||||
$this->checkout_fields_controller->persist_field_for_customer( $key, $value, $customer );
|
||||
$this->checkout_fields_controller->persist_field_for_customer( $key, $value, $customer, 'other' );
|
||||
}
|
||||
|
||||
// Validate all fields for this location.
|
||||
$location_validation = $this->checkout_fields_controller->validate_fields_for_location( $field_values, 'contact' );
|
||||
$location_validation = $this->checkout_fields_controller->validate_fields_for_location( $field_values, 'contact', 'other' );
|
||||
|
||||
if ( is_wp_error( $location_validation ) && $location_validation->has_errors() ) {
|
||||
wc_add_notice( $location_validation->get_error_message(), 'error' );
|
||||
@@ -233,9 +236,9 @@ class CheckoutFieldsFrontend {
|
||||
$fields = $this->checkout_fields_controller->get_fields_for_location( 'address' );
|
||||
|
||||
foreach ( $fields as $key => $field ) {
|
||||
$field_key = "/{$address_type}/{$key}";
|
||||
$field_key = CheckoutFields::get_group_key( $address_type ) . $key;
|
||||
$address[ $field_key ] = $field;
|
||||
$address[ $field_key ]['value'] = $this->checkout_fields_controller->get_field_from_customer( $key, $customer, $address_type );
|
||||
$address[ $field_key ]['value'] = $this->checkout_fields_controller->get_field_from_object( $key, $customer, $address_type );
|
||||
|
||||
if ( 'select' === $field['type'] ) {
|
||||
$address[ $field_key ]['options'] = array_column( $field['options'], 'label', 'value' );
|
||||
@@ -266,8 +269,8 @@ class CheckoutFieldsFrontend {
|
||||
$additional_fields = $this->checkout_fields_controller->get_fields_for_location( 'address' );
|
||||
$field_values = array();
|
||||
|
||||
foreach ( $additional_fields as $key => $field ) {
|
||||
$post_key = "/{$address_type}/{$key}";
|
||||
foreach ( array_keys( $additional_fields ) as $key ) {
|
||||
$post_key = CheckoutFields::get_group_key( $address_type ) . $key;
|
||||
|
||||
if ( ! isset( $_POST[ $post_key ] ) ) {
|
||||
continue;
|
||||
@@ -286,7 +289,7 @@ class CheckoutFieldsFrontend {
|
||||
|
||||
// Persist individual additional fields to customer.
|
||||
foreach ( $field_values as $key => $value ) {
|
||||
$this->checkout_fields_controller->persist_field_for_customer( "/{$address_type}/{$key}", $value, $customer );
|
||||
$this->checkout_fields_controller->persist_field_for_customer( $key, $value, $customer, $address_type );
|
||||
}
|
||||
|
||||
// Validate all fields for this location.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
namespace Automattic\WooCommerce\Blocks\Domain\Services;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use Automattic\WooCommerce\StoreApi\RoutesController;
|
||||
use Automattic\WooCommerce\StoreApi\SchemaController;
|
||||
use Automattic\WooCommerce\StoreApi\StoreApi;
|
||||
|
||||
/**
|
||||
* Service class that handles hydration of API data for blocks.
|
||||
@@ -19,7 +22,7 @@ class Hydration {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cached_store_notices = [];
|
||||
protected $cached_store_notices = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -38,24 +41,162 @@ class Hydration {
|
||||
* @return array Response data.
|
||||
*/
|
||||
public function get_rest_api_response_data( $path = '' ) {
|
||||
$this->cache_store_notices();
|
||||
if ( ! str_starts_with( $path, '/wc/store' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Allow-list only store API routes. No other request can be hydrated for safety.
|
||||
$available_routes = StoreApi::container()->get( RoutesController::class )->get_all_routes( 'v1', true );
|
||||
$controller_class = $this->match_route_to_handler( $path, $available_routes );
|
||||
|
||||
/**
|
||||
* We disable nonce check to support endpoints such as checkout. The caveat here is that we need to be careful to only support GET requests. No other request type should be processed without nonce check. Additionally, no GET request can modify data as part of hydration request, for example adding items to cart.
|
||||
*
|
||||
* Long term, we should consider validating nonce here, instead of disabling it temporarily.
|
||||
*/
|
||||
$this->disable_nonce_check();
|
||||
|
||||
// Preload the request and add it to the array. It will be $preloaded_requests['path'] and contain 'body' and 'headers'.
|
||||
$preloaded_requests = rest_preload_api_request( [], $path );
|
||||
$this->cache_store_notices();
|
||||
|
||||
$preloaded_data = array();
|
||||
|
||||
if ( null !== $controller_class ) {
|
||||
try {
|
||||
$response = $this->get_response_from_controller( $controller_class, $path );
|
||||
if ( $response ) {
|
||||
$preloaded_data = array(
|
||||
'body' => $response->get_data(),
|
||||
'headers' => $response->get_headers(),
|
||||
);
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
// This is executing in frontend of the site, a failure in hydration should not stop the site from working.
|
||||
wc_get_logger()->warning(
|
||||
'Error in hydrating REST API request: ' . $e->getMessage(),
|
||||
array(
|
||||
'source' => 'blocks-hydration',
|
||||
'data' => array(
|
||||
'path' => $path,
|
||||
'controller' => $controller_class,
|
||||
),
|
||||
'backtrace' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Preload the request and add it to the array. It will be $preloaded_requests['path'] and contain 'body' and 'headers'.
|
||||
$preloaded_requests = rest_preload_api_request( array(), $path );
|
||||
$preloaded_data = $preloaded_requests[ $path ] ?? array();
|
||||
}
|
||||
|
||||
$this->restore_cached_store_notices();
|
||||
$this->restore_nonce_check();
|
||||
|
||||
// Returns just the single preloaded request, or an empty array if it doesn't exist.
|
||||
return $preloaded_requests[ $path ] ?? [];
|
||||
return $preloaded_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate GET response from a controller. Also fires the `rest_request_after_callbacks` for backward compatibility.
|
||||
*
|
||||
* @param string $controller_class Controller class FQN that will respond to the request.
|
||||
* @param string $path Request path regex.
|
||||
*
|
||||
* @return false|mixed|null Response
|
||||
*/
|
||||
private function get_response_from_controller( $controller_class, $path ) {
|
||||
if ( null === $controller_class ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$request = new \WP_REST_Request( 'GET', $path );
|
||||
$schema_controller = StoreApi::container()->get( SchemaController::class );
|
||||
$controller = new $controller_class(
|
||||
$schema_controller,
|
||||
$schema_controller->get( $controller_class::SCHEMA_TYPE, $controller_class::SCHEMA_VERSION )
|
||||
);
|
||||
|
||||
$controller_args = is_callable( array( $controller, 'get_args' ) ) ? $controller->get_args() : array();
|
||||
|
||||
if ( empty( $controller_args ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the handler that responds to read request.
|
||||
$handler = current(
|
||||
array_filter(
|
||||
$controller_args,
|
||||
function ( $method_handler ) {
|
||||
return is_array( $method_handler ) && isset( $method_handler['methods'] ) && \WP_REST_Server::READABLE === $method_handler['methods'];
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $handler ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to WP core's `rest_dispatch_request` filter, this allows plugin to override hydrating the request.
|
||||
* Allows backward compatibility with the `rest_dispatch_request` filter by providing the same arguments.
|
||||
*
|
||||
* @since 8.9.0
|
||||
*
|
||||
* @param mixed $hydration_result Result of the hydration. If not null, this will be used as the response.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
* @param string $path Request path matched for the request..
|
||||
* @param array $handler Route handler used for the request.
|
||||
*/
|
||||
$hydration_result = apply_filters( 'woocommerce_hydration_dispatch_request', null, $request, $path, $handler );
|
||||
|
||||
if ( null !== $hydration_result ) {
|
||||
$response = $hydration_result;
|
||||
} else {
|
||||
$response = call_user_func_array( $handler['callback'], array( $request ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to WP core's `rest_request_after_callbacks` filter, this allows to modify the response after it has been generated.
|
||||
* Allows backward compatibility with the `rest_request_after_callbacks` filter by providing the same arguments.
|
||||
*
|
||||
* @since 8.9.0
|
||||
*
|
||||
* @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
|
||||
* Usually a WP_REST_Response or WP_Error.
|
||||
* @param array $handler Route handler used for the request.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
$response = apply_filters( 'woocommerce_hydration_request_after_callbacks', $response, $handler, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspired from WP core's `match_request_to_handler`, this matches a given path from available route regexes.
|
||||
* However, unlike WP core, this does not check against query params, request method etc.
|
||||
*
|
||||
* @param string $path The path to match.
|
||||
* @param array $available_routes Available routes in { $regex1 => $contoller_class1, ... } format.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function match_route_to_handler( $path, $available_routes ) {
|
||||
$matched_route = null;
|
||||
foreach ( $available_routes as $route_path => $controller ) {
|
||||
$match = preg_match( '@^' . $route_path . '$@i', $path );
|
||||
if ( $match ) {
|
||||
$matched_route = $controller;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $matched_route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the nonce check temporarily.
|
||||
*/
|
||||
protected function disable_nonce_check() {
|
||||
add_filter( 'woocommerce_store_api_disable_nonce_check', [ $this, 'disable_nonce_check_callback' ] );
|
||||
add_filter( 'woocommerce_store_api_disable_nonce_check', array( $this, 'disable_nonce_check_callback' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +211,7 @@ class Hydration {
|
||||
* Restore the nonce check.
|
||||
*/
|
||||
protected function restore_nonce_check() {
|
||||
remove_filter( 'woocommerce_store_api_disable_nonce_check', [ $this, 'disable_nonce_check_callback' ] );
|
||||
remove_filter( 'woocommerce_store_api_disable_nonce_check', array( $this, 'disable_nonce_check_callback' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +233,6 @@ class Hydration {
|
||||
return;
|
||||
}
|
||||
WC()->session->set( 'wc_notices', $this->cached_store_notices );
|
||||
$this->cached_store_notices = [];
|
||||
$this->cached_store_notices = array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,24 @@ class Notices {
|
||||
* Initialize notice hooks.
|
||||
*/
|
||||
public function init() {
|
||||
if ( wp_is_block_theme() ) {
|
||||
add_filter( 'woocommerce_kses_notice_allowed_tags', [ $this, 'add_kses_notice_allowed_tags' ] );
|
||||
add_filter( 'wc_get_template', [ $this, 'get_notices_template' ], 10, 5 );
|
||||
add_action( 'wp_head', [ $this, 'enqueue_notice_styles' ] );
|
||||
}
|
||||
add_action(
|
||||
'after_setup_theme',
|
||||
function() {
|
||||
/**
|
||||
* Allow classic theme developers to opt-in to using block notices.
|
||||
*
|
||||
* @since 8.8.0
|
||||
* @param bool $use_block_notices_in_classic_theme Whether to use block notices in classic theme.
|
||||
* @return bool
|
||||
*/
|
||||
if ( wp_is_block_theme() || apply_filters( 'woocommerce_use_block_notices_in_classic_theme', false ) ) {
|
||||
add_filter( 'wc_get_template', [ $this, 'get_notices_template' ], 10, 5 );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_filter( 'woocommerce_kses_notice_allowed_tags', [ $this, 'add_kses_notice_allowed_tags' ] );
|
||||
add_action( 'wp_head', [ $this, 'enqueue_notice_styles' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
|
||||
|
||||
if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_field' ) ) {
|
||||
if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
|
||||
/**
|
||||
* Register a checkout field.
|
||||
*
|
||||
* @param array $options Field arguments. See CheckoutFields::register_checkout_field() for details.
|
||||
* @throws \Exception If field registration fails.
|
||||
*/
|
||||
function __experimental_woocommerce_blocks_register_checkout_field( $options ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore
|
||||
function woocommerce_register_additional_checkout_field( $options ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore
|
||||
|
||||
// Check if `woocommerce_blocks_loaded` ran. If not then the CheckoutFields class will not be available yet.
|
||||
// In that case, re-hook `woocommerce_blocks_loaded` and try running this again.
|
||||
@@ -18,8 +18,8 @@ if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_fie
|
||||
if ( ! $woocommerce_blocks_loaded_ran ) {
|
||||
add_action(
|
||||
'woocommerce_blocks_loaded',
|
||||
function() use ( $options ) {
|
||||
__experimental_woocommerce_blocks_register_checkout_field( $options );
|
||||
function () use ( $options ) {
|
||||
woocommerce_register_additional_checkout_field( $options );
|
||||
}
|
||||
);
|
||||
return;
|
||||
@@ -27,7 +27,39 @@ if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_fie
|
||||
$checkout_fields = Package::container()->get( CheckoutFields::class );
|
||||
$result = $checkout_fields->register_checkout_field( $options );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
throw new \Exception( $result->get_error_message() );
|
||||
throw new \Exception( esc_attr( $result->get_error_message() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_field' ) ) {
|
||||
|
||||
/**
|
||||
* Register a checkout field.
|
||||
*
|
||||
* @param array $options Field arguments. See CheckoutFields::register_checkout_field() for details.
|
||||
* @throws \Exception If field registration fails.
|
||||
* @deprecated 5.6.0 Use woocommerce_register_additional_checkout_field() instead.
|
||||
*/
|
||||
function __experimental_woocommerce_blocks_register_checkout_field( $options ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore
|
||||
wc_deprecated_function( __FUNCTION__, '8.9.0', 'woocommerce_register_additional_checkout_field' );
|
||||
woocommerce_register_additional_checkout_field( $options );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( '__internal_woocommerce_blocks_deregister_checkout_field' ) ) {
|
||||
/**
|
||||
* Deregister a checkout field.
|
||||
*
|
||||
* @param string $field_id Field ID.
|
||||
* @throws \Exception If field deregistration fails.
|
||||
* @internal
|
||||
*/
|
||||
function __internal_woocommerce_blocks_deregister_checkout_field( $field_id ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore
|
||||
$checkout_fields = Package::container()->get( CheckoutFields::class );
|
||||
$result = $checkout_fields->deregister_checkout_field( $field_id );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
throw new \Exception( esc_attr( $result->get_error_message() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +43,13 @@ class CheckboxList {
|
||||
// translators: %s: checkbox label.
|
||||
$i18n_label = sprintf( __( 'Checkbox: %s', 'woocommerce' ), $item['aria_label'] ?? '' );
|
||||
?>
|
||||
<li>
|
||||
<li data-wc-key="<?php echo esc_attr( $item['id'] ); ?>">
|
||||
<div class="wc-block-components-checkbox">
|
||||
<label for="<?php echo esc_attr( $item['id'] ); ?>">
|
||||
<input
|
||||
id="<?php echo esc_attr( $item['id'] ); ?>"
|
||||
class="wc-block-components-checkbox__input"
|
||||
type="checkbox"
|
||||
<input
|
||||
id="<?php echo esc_attr( $item['id'] ); ?>"
|
||||
class="wc-block-components-checkbox__input"
|
||||
type="checkbox"
|
||||
aria-invalid="false"
|
||||
aria-label="<?php echo esc_attr( $i18n_label ); ?>"
|
||||
data-wc-on--change--select-item="actions.selectCheckboxItem"
|
||||
|
||||
@@ -44,7 +44,7 @@ class Dropdown {
|
||||
<div class="wc-interactivity-dropdown__dropdown" tabindex="-1" >
|
||||
<div class="wc-interactivity-dropdown__dropdown-selection" id="options-dropdown" tabindex="0" aria-haspopup="listbox">
|
||||
<span class="wc-interactivity-dropdown__placeholder" data-wc-text="state.placeholderText">
|
||||
<?php echo esc_html( $placeholder ); ?>
|
||||
<?php echo empty( $selected_items ) ? esc_html( $placeholder ) : ''; ?>
|
||||
</span>
|
||||
<?php if ( 'multiple' === $select_type ) { ?>
|
||||
<div class="selected-options">
|
||||
@@ -98,7 +98,13 @@ class Dropdown {
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div data-wc-bind--hidden="!context.isOpen" class="wc-interactivity-dropdown__dropdown-list" aria-labelledby="options-dropdown" role="listbox">
|
||||
<div
|
||||
class="wc-interactivity-dropdown__dropdown-list"
|
||||
aria-labelledby="options-dropdown"
|
||||
role="listbox"
|
||||
data-wc-bind--hidden="!context.isOpen"
|
||||
<?php echo esc_attr( $dropdown_context['isOpen'] ? '' : 'hidden' ); ?>
|
||||
>
|
||||
<?php
|
||||
foreach ( $items as $item ) :
|
||||
$context = array( 'item' => $item );
|
||||
|
||||
@@ -235,9 +235,9 @@ final class QueryFilters {
|
||||
$min_price_filter = intval( $wp_query->get( 'min_price' ) );
|
||||
|
||||
if ( $adjust_for_taxes ) {
|
||||
$args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $min_price_filter, 'min_price', '>=' );
|
||||
$args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $min_price_filter, 'max_price', '>=' );
|
||||
} else {
|
||||
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.min_price >= %f ', $min_price_filter );
|
||||
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.max_price >= %f ', $min_price_filter );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,9 +245,9 @@ final class QueryFilters {
|
||||
$max_price_filter = intval( $wp_query->get( 'max_price' ) );
|
||||
|
||||
if ( $adjust_for_taxes ) {
|
||||
$args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $max_price_filter, 'max_price', '<=' );
|
||||
$args['where'] .= $this->get_price_filter_query_for_displayed_taxes( $max_price_filter, 'min_price', '<=' );
|
||||
} else {
|
||||
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.max_price <= %f ', $max_price_filter );
|
||||
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.min_price <= %f ', $max_price_filter );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +475,7 @@ final class QueryFilters {
|
||||
if ( ! empty( $clauses ) ) {
|
||||
// "temp" is needed because the extra derived tables require an alias.
|
||||
$args['where'] .= ' AND (' . join( ' temp ) AND ', $clauses ) . ' temp ))';
|
||||
} elseif ( ! empty( $attributes_to_filter_by ) ) {
|
||||
} elseif ( ! empty( $chosen_attributes ) ) {
|
||||
$args['where'] .= ' AND 1=0';
|
||||
}
|
||||
|
||||
|
||||
@@ -55,18 +55,17 @@ class ShippingController {
|
||||
if ( is_admin() ) {
|
||||
$this->asset_data_registry->add(
|
||||
'countryStates',
|
||||
function() {
|
||||
function () {
|
||||
return WC()->countries->get_states();
|
||||
},
|
||||
true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$this->asset_data_registry->add( 'collectableMethodIds', array( 'Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils', 'get_local_pickup_method_ids' ), true );
|
||||
$this->asset_data_registry->add( 'collectableMethodIds', array( 'Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils', 'get_local_pickup_method_ids' ) );
|
||||
$this->asset_data_registry->add( 'shippingCostRequiresAddress', get_option( 'woocommerce_shipping_cost_requires_address', false ) === 'yes' );
|
||||
add_action( 'rest_api_init', [ $this, 'register_settings' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'hydrate_client_settings' ] );
|
||||
add_action( 'rest_api_init', array( $this, 'register_settings' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'hydrate_client_settings' ) );
|
||||
add_action( 'woocommerce_load_shipping_methods', array( $this, 'register_local_pickup' ) );
|
||||
add_filter( 'woocommerce_local_pickup_methods', array( $this, 'register_local_pickup_method' ) );
|
||||
add_filter( 'woocommerce_order_hide_shipping_address', array( $this, 'hide_shipping_address_for_local_pickup' ), 10 );
|
||||
@@ -75,7 +74,6 @@ class ShippingController {
|
||||
add_filter( 'pre_update_option_woocommerce_pickup_location_settings', array( $this, 'flush_cache' ) );
|
||||
add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) );
|
||||
add_filter( 'woocommerce_shipping_settings', array( $this, 'remove_shipping_settings' ) );
|
||||
add_filter( 'wc_shipping_enabled', array( $this, 'force_shipping_enabled' ), 100, 1 );
|
||||
add_filter( 'woocommerce_order_shipping_to_display', array( $this, 'show_local_pickup_details' ), 10, 2 );
|
||||
|
||||
// This is required to short circuit `show_shipping` from class-wc-cart.php - without it, that function
|
||||
@@ -99,30 +97,17 @@ class ShippingController {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force shipping to be enabled if the Checkout block is in use on the Checkout page.
|
||||
*
|
||||
* @param boolean $enabled Whether shipping is currently enabled.
|
||||
* @return boolean Whether shipping should continue to be enabled/disabled.
|
||||
*/
|
||||
public function force_shipping_enabled( $enabled ) {
|
||||
if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
|
||||
return true;
|
||||
}
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject collection details onto the order received page.
|
||||
*
|
||||
* @param string $return Return value.
|
||||
* @param string $return_value Return value.
|
||||
* @param \WC_Order $order Order object.
|
||||
* @return string
|
||||
*/
|
||||
public function show_local_pickup_details( $return, $order ) {
|
||||
public function show_local_pickup_details( $return_value, $order ) {
|
||||
// Confirm order is valid before proceeding further.
|
||||
if ( ! $order instanceof \WC_Order ) {
|
||||
return $return;
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
$shipping_method_ids = ArrayUtil::select( $order->get_shipping_methods(), 'get_method_id', ArrayUtil::SELECT_BY_OBJECT_METHOD );
|
||||
@@ -130,7 +115,7 @@ class ShippingController {
|
||||
|
||||
// Ensure order used pickup location method, otherwise bail.
|
||||
if ( 'pickup_location' !== $shipping_method_id ) {
|
||||
return $return;
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
$shipping_method = current( $order->get_shipping_methods() );
|
||||
@@ -139,7 +124,7 @@ class ShippingController {
|
||||
$address = $shipping_method->get_meta( 'pickup_address' );
|
||||
|
||||
if ( ! $address ) {
|
||||
return $return;
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
@@ -159,7 +144,11 @@ class ShippingController {
|
||||
if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
|
||||
foreach ( $settings as $index => $setting ) {
|
||||
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
|
||||
$settings[ $index ]['desc'] .= ' (' . __( 'Not available when using WooCommerce Blocks Local Pickup', 'woocommerce' ) . ')';
|
||||
$settings[ $index ]['desc'] = sprintf(
|
||||
/* translators: %s: URL to the documentation. */
|
||||
__( 'Hide shipping costs until an address is entered (Not available when using the <a href="%s">Local pickup options powered by the Checkout block</a>)', 'woocommerce' ),
|
||||
'https://woocommerce.com/document/woocommerce-blocks-local-pickup/'
|
||||
);
|
||||
$settings[ $index ]['disabled'] = true;
|
||||
$settings[ $index ]['value'] = 'no';
|
||||
break;
|
||||
@@ -177,86 +166,86 @@ class ShippingController {
|
||||
register_setting(
|
||||
'options',
|
||||
'woocommerce_pickup_location_settings',
|
||||
[
|
||||
array(
|
||||
'type' => 'object',
|
||||
'description' => 'WooCommerce Local Pickup Method Settings',
|
||||
'default' => [],
|
||||
'show_in_rest' => [
|
||||
'default' => array(),
|
||||
'show_in_rest' => array(
|
||||
'name' => 'pickup_location_settings',
|
||||
'schema' => [
|
||||
'schema' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'enabled' => [
|
||||
'enabled' => array(
|
||||
'description' => __( 'If enabled, this method will appear on the block based checkout.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => [ 'yes', 'no' ],
|
||||
],
|
||||
'title' => [
|
||||
'enum' => array( 'yes', 'no' ),
|
||||
),
|
||||
'title' => array(
|
||||
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'tax_status' => [
|
||||
),
|
||||
'tax_status' => array(
|
||||
'description' => __( 'If a cost is defined, this controls if taxes are applied to that cost.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => [ 'taxable', 'none' ],
|
||||
],
|
||||
'cost' => [
|
||||
'enum' => array( 'taxable', 'none' ),
|
||||
),
|
||||
'cost' => array(
|
||||
'description' => __( 'Optional cost to charge for local pickup.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
register_setting(
|
||||
'options',
|
||||
'pickup_location_pickup_locations',
|
||||
[
|
||||
array(
|
||||
'type' => 'array',
|
||||
'description' => 'WooCommerce Local Pickup Locations',
|
||||
'default' => [],
|
||||
'show_in_rest' => [
|
||||
'default' => array(),
|
||||
'show_in_rest' => array(
|
||||
'name' => 'pickup_locations',
|
||||
'schema' => [
|
||||
'schema' => array(
|
||||
'type' => 'array',
|
||||
'items' => [
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => [
|
||||
'name' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'address' => [
|
||||
),
|
||||
'address' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'address_1' => [
|
||||
'address_1' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'city' => [
|
||||
),
|
||||
'city' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'state' => [
|
||||
),
|
||||
'state' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'postcode' => [
|
||||
),
|
||||
'postcode' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'country' => [
|
||||
),
|
||||
'country' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
'details' => [
|
||||
),
|
||||
'details' => array(
|
||||
'type' => 'string',
|
||||
],
|
||||
'enabled' => [
|
||||
),
|
||||
'enabled' => array(
|
||||
'type' => 'boolean',
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -264,16 +253,16 @@ class ShippingController {
|
||||
* Hydrate client settings
|
||||
*/
|
||||
public function hydrate_client_settings() {
|
||||
$locations = get_option( 'pickup_location_pickup_locations', [] );
|
||||
$locations = get_option( 'pickup_location_pickup_locations', array() );
|
||||
|
||||
$formatted_pickup_locations = [];
|
||||
$formatted_pickup_locations = array();
|
||||
foreach ( $locations as $location ) {
|
||||
$formatted_pickup_locations[] = [
|
||||
$formatted_pickup_locations[] = array(
|
||||
'name' => $location['name'],
|
||||
'address' => $location['address'],
|
||||
'details' => $location['details'],
|
||||
'enabled' => wc_string_to_bool( $location['enabled'] ),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
$has_legacy_pickup = false;
|
||||
@@ -303,7 +292,7 @@ class ShippingController {
|
||||
}
|
||||
|
||||
$settings = array(
|
||||
'pickupLocationSettings' => get_option( 'woocommerce_pickup_location_settings', [] ),
|
||||
'pickupLocationSettings' => LocalPickupUtils::get_local_pickup_settings(),
|
||||
'pickupLocations' => $formatted_pickup_locations,
|
||||
'readonlySettings' => array(
|
||||
'hasLegacyPickup' => $has_legacy_pickup,
|
||||
@@ -325,7 +314,7 @@ class ShippingController {
|
||||
* Load admin scripts.
|
||||
*/
|
||||
public function admin_scripts() {
|
||||
$this->asset_api->register_script( 'wc-shipping-method-pickup-location', 'assets/client/blocks/wc-shipping-method-pickup-location.js', [], true );
|
||||
$this->asset_api->register_script( 'wc-shipping-method-pickup-location', 'assets/client/blocks/wc-shipping-method-pickup-location.js', array(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,8 +376,8 @@ class ShippingController {
|
||||
|
||||
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
if ( $chosen_method_id && true === apply_filters( 'woocommerce_apply_base_tax_for_local_pickup', true ) && in_array( $chosen_method_id, LocalPickupUtils::get_local_pickup_method_ids(), true ) ) {
|
||||
$pickup_locations = get_option( 'pickup_location_pickup_locations', [] );
|
||||
$pickup_location = $pickup_locations[ $chosen_method_instance ] ?? [];
|
||||
$pickup_locations = get_option( 'pickup_location_pickup_locations', array() );
|
||||
$pickup_location = $pickup_locations[ $chosen_method_instance ] ?? array();
|
||||
|
||||
if ( isset( $pickup_location['address'], $pickup_location['address']['country'] ) && ! empty( $pickup_location['address']['country'] ) ) {
|
||||
$address = array(
|
||||
@@ -417,8 +406,8 @@ class ShippingController {
|
||||
// Check all packages for an instance of a collectable shipping method.
|
||||
$valid_packages = array_filter(
|
||||
$packages,
|
||||
function( $package ) {
|
||||
$shipping_method_ids = ArrayUtil::select( $package['rates'] ?? [], 'get_method_id', ArrayUtil::SELECT_BY_OBJECT_METHOD );
|
||||
function ( $package ) {
|
||||
$shipping_method_ids = ArrayUtil::select( $package['rates'] ?? array(), 'get_method_id', ArrayUtil::SELECT_BY_OBJECT_METHOD );
|
||||
return ! empty( array_intersect( LocalPickupUtils::get_local_pickup_method_ids(), $shipping_method_ids ) );
|
||||
}
|
||||
);
|
||||
@@ -426,14 +415,14 @@ class ShippingController {
|
||||
// Remove pickup location from rates arrays.
|
||||
if ( count( $valid_packages ) !== count( $packages ) ) {
|
||||
$packages = array_map(
|
||||
function( $package ) {
|
||||
function ( $package ) {
|
||||
if ( ! is_array( $package['rates'] ) ) {
|
||||
$package['rates'] = [];
|
||||
$package['rates'] = array();
|
||||
return $package;
|
||||
}
|
||||
$package['rates'] = array_filter(
|
||||
$package['rates'],
|
||||
function( $rate ) {
|
||||
function ( $rate ) {
|
||||
return ! in_array( $rate->get_method_id(), LocalPickupUtils::get_local_pickup_method_ids(), true );
|
||||
}
|
||||
);
|
||||
@@ -478,7 +467,7 @@ class ShippingController {
|
||||
'pickup_locations_enabled' => count(
|
||||
array_filter(
|
||||
$locations,
|
||||
function( $location ) {
|
||||
function ( $location ) {
|
||||
return $location['enabled']; }
|
||||
)
|
||||
),
|
||||
@@ -488,4 +477,29 @@ class ShippingController {
|
||||
|
||||
return $served;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if legacy local pickup is activated in any of the shipping zones or in the Rest of the World zone.
|
||||
*
|
||||
* @since 8.8.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_legacy_local_pickup_active() {
|
||||
$rest_of_the_world = \WC_Shipping_Zones::get_zone_by( 'zone_id', 0 );
|
||||
$shipping_zones = \WC_Shipping_Zones::get_zones();
|
||||
$rest_of_the_world_data = $rest_of_the_world->get_data();
|
||||
$rest_of_the_world_data['shipping_methods'] = $rest_of_the_world->get_shipping_methods();
|
||||
array_unshift( $shipping_zones, $rest_of_the_world_data );
|
||||
|
||||
foreach ( $shipping_zones as $zone ) {
|
||||
foreach ( $zone['shipping_methods'] as $method ) {
|
||||
if ( 'local_pickup' === $method->id && $method->is_enabled() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,31 +8,15 @@ namespace Automattic\WooCommerce\Blocks\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();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractPageTemplate extends AbstractTemplate {
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
public function init() {
|
||||
add_filter( 'page_template_hierarchy', array( $this, 'page_template_hierarchy' ), 1 );
|
||||
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.
|
||||
*
|
||||
@@ -47,15 +31,6 @@ abstract class AbstractPageTemplate {
|
||||
*/
|
||||
abstract protected function is_active_template();
|
||||
|
||||
/**
|
||||
* Should return the title of the page, or an empty string if the page title should not be changed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_template_title() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* When the page should be displaying the template, add it to the hierarchy.
|
||||
*
|
||||
@@ -67,7 +42,7 @@ abstract class AbstractPageTemplate {
|
||||
*/
|
||||
public function page_template_hierarchy( $templates ) {
|
||||
if ( $this->is_active_template() ) {
|
||||
array_unshift( $templates, $this->get_slug() );
|
||||
array_unshift( $templates, static::SLUG );
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* AbstractTemplate class.
|
||||
*
|
||||
* Shared logic for templates.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = '';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
abstract public function init();
|
||||
|
||||
/**
|
||||
* Should return the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_template_title();
|
||||
|
||||
/**
|
||||
* Should return the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_template_description();
|
||||
}
|
||||
@@ -136,6 +136,7 @@ abstract class AbstractTemplateCompatibility {
|
||||
remove_action( $hook, $callback, $priority );
|
||||
}
|
||||
}
|
||||
$class_name = basename( str_replace( '\\', '/', get_class( $this ) ) );
|
||||
|
||||
/**
|
||||
* When extensions implement their equivalent blocks of the template
|
||||
@@ -161,8 +162,10 @@ abstract class AbstractTemplateCompatibility {
|
||||
*
|
||||
* @since 9.5.0
|
||||
* @param array $data Additional hooked data. Default to empty
|
||||
* @param string $class_name Class name within which the hook is called.
|
||||
* Either ArchiveProductTemplatesCompatibility or SingleProductTemplateCompatibility.
|
||||
*/
|
||||
$additional_hook_data = apply_filters( 'woocommerce_blocks_hook_compatibility_additional_data', array() );
|
||||
$additional_hook_data = apply_filters( 'woocommerce_blocks_hook_compatibility_additional_data', array(), $class_name );
|
||||
|
||||
if ( empty( $additional_hook_data ) || ! is_array( $additional_hook_data ) ) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* AbstractTemplatePart class.
|
||||
*
|
||||
* Shared logic for templates parts.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractTemplatePart extends AbstractTemplate {
|
||||
/**
|
||||
* The template part area where the template part belongs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template_area;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
|
||||
$block_hooks = array_filter(
|
||||
$this->hook_data,
|
||||
function( $hook ) use ( $block_name ) {
|
||||
function ( $hook ) use ( $block_name ) {
|
||||
return in_array( $block_name, $hook['block_names'], true );
|
||||
}
|
||||
);
|
||||
@@ -98,9 +98,9 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
}
|
||||
|
||||
$supported_blocks = array_merge(
|
||||
[],
|
||||
array(),
|
||||
...array_map(
|
||||
function( $hook ) {
|
||||
function ( $hook ) {
|
||||
return $hook['block_names'];
|
||||
},
|
||||
array_values( $this->hook_data )
|
||||
@@ -312,7 +312,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility
|
||||
continue;
|
||||
}
|
||||
foreach ( $data['hooked'] as $callback => $priority ) {
|
||||
if ( ! in_array( $callback, $data['permanently_removed_actions'] ?? [], true ) ) {
|
||||
if ( ! in_array( $callback, $data['permanently_removed_actions'] ?? array(), true ) ) {
|
||||
add_action( $hook, $callback, $priority );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,50 @@ namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
* @internal
|
||||
*/
|
||||
class CartTemplate extends AbstractPageTemplate {
|
||||
|
||||
/**
|
||||
* Template slug.
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'page-cart';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'page-cart';
|
||||
public function get_template_title() {
|
||||
return _x( 'Page: Cart', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'The Cart template displays the items selected by the user for purchase, including quantities, prices, and discounts. It allows users to review their choices before proceeding to checkout.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if (
|
||||
! is_embed() && is_cart()
|
||||
) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +85,7 @@ class CartTemplate extends AbstractPageTemplate {
|
||||
*/
|
||||
public function page_template_hierarchy( $templates ) {
|
||||
if ( $this->is_active_template() ) {
|
||||
array_unshift( $templates, $this->get_slug() );
|
||||
array_unshift( $templates, self::SLUG );
|
||||
array_unshift( $templates, 'cart' );
|
||||
}
|
||||
return $templates;
|
||||
|
||||
@@ -6,8 +6,42 @@ namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CheckoutHeaderTemplate {
|
||||
class CheckoutHeaderTemplate extends AbstractTemplatePart {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'checkout-header';
|
||||
|
||||
/**
|
||||
* The template part area where the template part belongs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template_area = 'header';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Checkout Header', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Template used to display the simplified Checkout header.', 'woocommerce' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,50 @@ namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
* @internal
|
||||
*/
|
||||
class CheckoutTemplate extends AbstractPageTemplate {
|
||||
|
||||
/**
|
||||
* Template slug.
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'page-checkout';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'page-checkout';
|
||||
public function get_template_title() {
|
||||
return _x( 'Page: Checkout', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'The Checkout template guides users through the final steps of the purchase process. It enables users to enter shipping and billing information, select a payment method, and review order details.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if (
|
||||
! is_embed() && is_checkout()
|
||||
) {
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +85,7 @@ class CheckoutTemplate extends AbstractPageTemplate {
|
||||
*/
|
||||
public function page_template_hierarchy( $templates ) {
|
||||
if ( $this->is_active_template() ) {
|
||||
array_unshift( $templates, $this->get_slug() );
|
||||
array_unshift( $templates, self::SLUG );
|
||||
array_unshift( $templates, 'checkout' );
|
||||
}
|
||||
return $templates;
|
||||
|
||||
@@ -32,7 +32,7 @@ class ClassicTemplatesCompatibility {
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
protected function init() { // phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingPublic
|
||||
if ( ! wc_current_theme_is_fse_theme() ) {
|
||||
add_action( 'template_redirect', array( $this, 'set_classic_template_data' ) );
|
||||
// We need to set this data on the widgets screen so the filters render previews.
|
||||
@@ -60,7 +60,7 @@ class ClassicTemplatesCompatibility {
|
||||
global $pagenow;
|
||||
|
||||
if ( is_shop() || is_product_taxonomy() || 'widgets.php' === $pagenow ) {
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true, true );
|
||||
$this->asset_data_registry->add( 'hasFilterableProducts', true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class ClassicTemplatesCompatibility {
|
||||
*/
|
||||
public function set_php_template_data() {
|
||||
if ( is_shop() || is_product_taxonomy() ) {
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true, true );
|
||||
$this->asset_data_registry->add( 'isRenderingPhpTemplate', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
/**
|
||||
* ComingSoonTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ComingSoonTemplate extends AbstractPageTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'coming-soon';
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Page: Coming soon', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Page template for Coming soon page.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page object assigned to this template/page.
|
||||
*
|
||||
* @return \WP_Post|null Post object or null.
|
||||
*/
|
||||
protected function get_placeholder_page() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when viewing the coming soon page.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_active_template() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,61 @@ namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MiniCartTemplate {
|
||||
class MiniCartTemplate extends AbstractTemplatePart {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'mini-cart';
|
||||
|
||||
/**
|
||||
* The template part area where the template part belongs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template_area = 'mini-cart';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'default_wp_template_part_areas', array( $this, 'register_mini_cart_template_part_area' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Mini-Cart', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Template used to display the Mini-Cart drawer.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = array(
|
||||
'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, array( $mini_cart_template_part_area ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,39 @@ namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
* @internal
|
||||
*/
|
||||
class OrderConfirmationTemplate extends AbstractPageTemplate {
|
||||
|
||||
/**
|
||||
* Template slug.
|
||||
* The slug of the template.
|
||||
*
|
||||
* @return string
|
||||
* @var string
|
||||
*/
|
||||
public static function get_slug() {
|
||||
return 'order-confirmation';
|
||||
}
|
||||
const SLUG = 'order-confirmation';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
parent::init();
|
||||
public function init() {
|
||||
add_action( 'wp_before_admin_bar_render', array( $this, 'remove_edit_page_link' ) );
|
||||
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Order Confirmation', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'The Order Confirmation template serves as a receipt and confirmation of a successful purchase. It includes a summary of the ordered items, shipping, billing, and totals.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,13 +69,4 @@ class OrderConfirmationTemplate extends AbstractPageTemplate {
|
||||
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' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,75 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductAttributeTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductAttributeTemplate {
|
||||
class ProductAttributeTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'taxonomy-product_attribute';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The template used as a fallback if that one is customized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->init();
|
||||
}
|
||||
public $fallback_template = ProductCatalogTemplate::SLUG;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
add_filter( 'taxonomy_template_hierarchy', array( $this, 'update_taxonomy_template_hierarchy' ), 1, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Products by Attribute', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays products filtered by an attribute.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
$queried_object = get_queried_object();
|
||||
if ( is_null( $queried_object ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $queried_object->taxonomy ) && taxonomy_is_product_attribute( $queried_object->taxonomy ) ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Product by Attribute template for product attributes taxonomy pages.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductCatalogTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductCatalogTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'archive-product';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
add_filter( 'current_theme_supports-block-templates', array( $this, 'remove_block_template_support_for_shop_page' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Product Catalog', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays your products.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( ! is_embed() && ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the template panel from the Sidebar of the Shop page because
|
||||
* the Site Editor handles it.
|
||||
*
|
||||
* @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/6278
|
||||
*
|
||||
* @param bool $is_support Whether the active theme supports block templates.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function remove_block_template_support_for_shop_page( $is_support ) {
|
||||
global $pagenow, $post;
|
||||
|
||||
if (
|
||||
is_admin() &&
|
||||
'post.php' === $pagenow &&
|
||||
function_exists( 'wc_get_page_id' ) &&
|
||||
is_a( $post, 'WP_Post' ) &&
|
||||
wc_get_page_id( 'shop' ) === $post->ID
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $is_support;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductCategoryTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductCategoryTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'taxonomy-product_cat';
|
||||
|
||||
/**
|
||||
* The template used as a fallback if that one is customized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $fallback_template = ProductCatalogTemplate::SLUG;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Products by Category', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays products filtered by a category.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( ! is_embed() && is_product_taxonomy() && is_tax( 'product_cat' ) ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,70 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductSearchResultsTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductSearchResultsTemplate {
|
||||
class ProductSearchResultsTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'product-search-results';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The template used as a fallback if that one is customized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->init();
|
||||
}
|
||||
public $fallback_template = ProductCatalogTemplate::SLUG;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
protected function init() {
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
add_filter( 'search_template_hierarchy', array( $this, 'update_search_template_hierarchy' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Product Search Results', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays search results for your store.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( ! is_embed() && is_post_type_archive( 'product' ) && is_search() ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the search is for products and a block theme is active, render the Product Search Template.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductTagTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductTagTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'taxonomy-product_tag';
|
||||
|
||||
/**
|
||||
* The template used as a fallback if that one is customized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $fallback_template = ProductCatalogTemplate::SLUG;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Products by Tag', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays products filtered by a tag.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( ! is_embed() && is_product_taxonomy() && is_tax( 'product_tag' ) ) {
|
||||
$templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) );
|
||||
|
||||
if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) {
|
||||
add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,116 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* SingleProductTemplae class.
|
||||
* SingleProductTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SingleProductTemplate {
|
||||
class SingleProductTemplate extends AbstractTemplate {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'single-product';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', array( $this, 'render_block_template' ) );
|
||||
add_filter( 'get_block_templates', array( $this, 'update_single_product_content' ), 11, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Single Product', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __( 'Displays a single product.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default block template from Woo Blocks if no theme templates exist.
|
||||
*/
|
||||
public function render_block_template() {
|
||||
if ( ! is_embed() && is_singular( 'product' ) ) {
|
||||
global $post;
|
||||
|
||||
$valid_slugs = array( self::SLUG );
|
||||
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' );
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_has_block_template', '__return_true', 10, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the block template objects to be used.
|
||||
*
|
||||
* @param array $query_result Array of template objects.
|
||||
* @param array $query Optional. Arguments to retrieve templates.
|
||||
* @param string $template_type wp_template or wp_template_part.
|
||||
* @return array
|
||||
*/
|
||||
public function update_single_product_content( $query_result, $query, $template_type ) {
|
||||
$query_result = array_map(
|
||||
function ( $template ) {
|
||||
if ( str_contains( $template->slug, self::SLUG ) ) {
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( post_password_required() ) {
|
||||
$template->content = $this->add_password_form( $template->content );
|
||||
} else {
|
||||
$template->content = SingleProductTemplateCompatibility::add_compatibility_layer( $template->content );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $template;
|
||||
},
|
||||
$query_result
|
||||
);
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the first single product template block with the password form. Remove all other single product template blocks.
|
||||
@@ -21,7 +125,7 @@ class SingleProductTemplate {
|
||||
$single_product_template_blocks = array( 'woocommerce/product-image-gallery', 'woocommerce/product-details', 'woocommerce/add-to-cart-form', 'woocommerce/product-meta', 'woocommerce/product-rating', 'woocommerce/product-price', 'woocommerce/related-products' );
|
||||
return array_reduce(
|
||||
$parsed_blocks,
|
||||
function( $carry, $block ) use ( $single_product_template_blocks ) {
|
||||
function ( $carry, $block ) use ( $single_product_template_blocks ) {
|
||||
if ( in_array( $block['blockName'], $single_product_template_blocks, true ) ) {
|
||||
if ( $carry['is_already_replaced'] ) {
|
||||
return array(
|
||||
|
||||
@@ -31,7 +31,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
|
||||
$block_hooks = array_filter(
|
||||
$this->hook_data,
|
||||
function( $hook ) use ( $block_name ) {
|
||||
function ( $hook ) use ( $block_name ) {
|
||||
return in_array( $block_name, $hook['block_names'], true );
|
||||
}
|
||||
);
|
||||
@@ -279,7 +279,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
$grouped_blocks = self::group_blocks( $parsed_blocks );
|
||||
|
||||
$wrapped_blocks = array_map(
|
||||
function( $blocks ) {
|
||||
function ( $blocks ) {
|
||||
if ( 'core/template-part' === $blocks[0]['blockName'] ) {
|
||||
return $blocks;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
private static function inject_custom_attributes_to_first_and_last_block_single_product_template( $wrapped_blocks ) {
|
||||
$template_with_custom_attributes = array_reduce(
|
||||
$wrapped_blocks,
|
||||
function( $carry, $item ) {
|
||||
function ( $carry, $item ) {
|
||||
|
||||
$index = $carry['index'];
|
||||
$carry['index'] = $carry['index'] + 1;
|
||||
@@ -379,7 +379,6 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
$new_block['innerBlocks'] = $blocks;
|
||||
|
||||
return $new_block;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,7 +420,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
private static function group_blocks( $parsed_blocks ) {
|
||||
return array_reduce(
|
||||
$parsed_blocks,
|
||||
function( array $carry, array $block ) {
|
||||
function ( array $carry, array $block ) {
|
||||
if ( 'core/template-part' === $block['blockName'] ) {
|
||||
$carry[] = array( $block );
|
||||
return $carry;
|
||||
@@ -480,7 +479,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
private static function serialize_blocks( $parsed_blocks ) {
|
||||
return array_reduce(
|
||||
$parsed_blocks,
|
||||
function( $carry, $item ) {
|
||||
function ( $carry, $item ) {
|
||||
if ( is_array( $item ) ) {
|
||||
return $carry . serialize_blocks( $item );
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ trait BlockHooksTrait {
|
||||
/**
|
||||
* Callback for `hooked_block_types` to auto-inject the mini-cart block into headers after navigation.
|
||||
*
|
||||
* @param array $hooked_blocks An array of block slugs hooked into a given context.
|
||||
* @param string $position Position of the block insertion point.
|
||||
* @param string $anchor_block The block acting as the anchor for the inserted block.
|
||||
* @param \WP_Block_Template|array $context Where the block is embedded.
|
||||
* @since $VID:$
|
||||
* @param array $hooked_blocks An array of block slugs hooked into a given context.
|
||||
* @param string $position Position of the block insertion point.
|
||||
* @param string $anchor_block The block acting as the anchor for the inserted block.
|
||||
* @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded.
|
||||
* @since 8.5.0
|
||||
* @return array An array of block slugs hooked into a given context.
|
||||
*/
|
||||
public function register_hooked_block( $hooked_blocks, $position, $anchor_block, $context ) {
|
||||
@@ -32,36 +32,43 @@ trait BlockHooksTrait {
|
||||
$active_theme_name = wp_get_theme()->get( 'Name' );
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of pattern slugs to exclude from auto-insert (useful when
|
||||
* there are patterns that have a very specific location for the block)
|
||||
*
|
||||
* @since $VID:$
|
||||
*/
|
||||
$pattern_exclude_list = apply_filters( 'woocommerce_hooked_blocks_pattern_exclude_list', array( 'twentytwentytwo/header-centered-logo', 'twentytwentytwo/header-stacked' ) );
|
||||
|
||||
/**
|
||||
* A list of theme slugs to execute this with. This is a temporary
|
||||
* measure until improvements to the Block Hooks API allow for exposing
|
||||
* to all block themes.
|
||||
*
|
||||
* @since $VID:$
|
||||
* @since 8.4.0
|
||||
*/
|
||||
$theme_include_list = apply_filters( 'woocommerce_hooked_blocks_theme_include_list', array( 'Twenty Twenty-Four', 'Twenty Twenty-Three', 'Twenty Twenty-Two', 'Tsubaki', 'Zaino', 'Thriving Artist', 'Amulet', 'Tazza' ) );
|
||||
|
||||
if ( $context && in_array( $active_theme_name, $theme_include_list, true ) ) {
|
||||
foreach ( $this->hooked_block_placements as $placement ) {
|
||||
if (
|
||||
$placement['position'] === $position &&
|
||||
$placement['anchor'] === $anchor_block &&
|
||||
(
|
||||
|
||||
if ( $placement['position'] === $position && $placement['anchor'] === $anchor_block ) {
|
||||
// If an area has been specified for this placement.
|
||||
if (
|
||||
isset( $placement['area'] ) &&
|
||||
$this->is_template_part_or_pattern( $context, $placement['area'] )
|
||||
) &&
|
||||
! $this->pattern_is_excluded( $context, $pattern_exclude_list ) &&
|
||||
! $this->has_block_in_content( $context )
|
||||
) {
|
||||
$hooked_blocks[] = $this->namespace . '/' . $this->block_name;
|
||||
! $this->has_block_in_content( $context )
|
||||
&& $this->is_target_area( $context, $placement['area'] )
|
||||
) {
|
||||
$hooked_blocks[] = $this->namespace . '/' . $this->block_name;
|
||||
}
|
||||
|
||||
// If no area has been specified for this placement just insert the block.
|
||||
// This is likely to be the case when we're inserting into the navigation block
|
||||
// where we don't have a specific area to target.
|
||||
if ( ! isset( $placement['area'] ) ) {
|
||||
$hooked_blocks[] = $this->namespace . '/' . $this->block_name;
|
||||
}
|
||||
|
||||
// If a callback has been specified for this placement, call it. This allows for custom block-specific logic to be run.
|
||||
$callback = isset( $placement['callback'] ) && is_callable( array( $this, $placement['callback'] ) ) ? array( $this, $placement['callback'] ) : null;
|
||||
if ( null !== $callback ) {
|
||||
$modified_hooked_blocks = $callback( $hooked_blocks, $position, $anchor_block, $context );
|
||||
if ( is_array( $modified_hooked_blocks ) ) {
|
||||
$hooked_blocks = $modified_hooked_blocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,21 +80,33 @@ trait BlockHooksTrait {
|
||||
* Checks if the provided context contains a the block already.
|
||||
*
|
||||
* @param array|\WP_Block_Template $context Where the block is embedded.
|
||||
* @since $VID:$
|
||||
* @return boolean
|
||||
*/
|
||||
protected function has_block_in_content( $context ) {
|
||||
$content = $this->get_context_content( $context );
|
||||
return strpos( $content, 'wp:' . $this->namespace . '/' . $this->block_name ) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a provided context, returns the content of the context.
|
||||
*
|
||||
* @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded.
|
||||
* @since 8.5.0
|
||||
* @return string
|
||||
*/
|
||||
protected function get_context_content( $context ) {
|
||||
$content = is_array( $context ) && isset( $context['content'] ) ? $context['content'] : '';
|
||||
$content = '' === $content && $context instanceof \WP_Block_Template ? $context->content : $content;
|
||||
return strpos( $content, 'wp:' . $this->namespace . '/' . $this->block_name ) !== false;
|
||||
$content = '' === $content && $context instanceof \WP_Post ? $context->post_content : $content;
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a provided context, returns whether the context refers to header content.
|
||||
*
|
||||
* @param array|\WP_Block_Template $context Where the block is embedded.
|
||||
* @param string $area The area to check against before inserting.
|
||||
* @since $VID:$
|
||||
* @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded.
|
||||
* @param string $area The area to check against before inserting.
|
||||
* @since 8.5.0
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_template_part_or_pattern( $context, $area ) {
|
||||
@@ -101,14 +120,41 @@ trait BlockHooksTrait {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the pattern is excluded or not
|
||||
* Given a provided context, returns whether the context refers to the target area and isn't marked as excluded.
|
||||
*
|
||||
* @param array|\WP_Block_Template $context Where the block is embedded.
|
||||
* @param array $pattern_exclude_list List of pattern slugs to exclude.
|
||||
* @since $VID:$
|
||||
* @param array|\WP_Post|\WP_Block_Template $context the context to check.
|
||||
* @param string $area The area to check against before inserting.
|
||||
* @since 8.5.0
|
||||
* @return boolean
|
||||
*/
|
||||
protected function pattern_is_excluded( $context, $pattern_exclude_list = array() ) {
|
||||
protected function is_target_area( $context, $area ) {
|
||||
if ( $this->is_template_part_or_pattern( $context, $area ) && ! $this->pattern_is_excluded( $context ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the pattern is excluded or not
|
||||
*
|
||||
* @since 8.5.0
|
||||
*
|
||||
* @param array|\WP_Block_Template $context Where the block is embedded.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function pattern_is_excluded( $context ) {
|
||||
/**
|
||||
* A list of pattern slugs to exclude from auto-insert (useful when there are patterns that have a very specific location for the block)
|
||||
* Note: The patterns that are currently excluded are the ones that don't work well with the mini-cart block or customer-account block.
|
||||
*
|
||||
* @since 8.5.0
|
||||
*/
|
||||
$pattern_exclude_list = apply_filters(
|
||||
'woocommerce_hooked_blocks_pattern_exclude_list',
|
||||
array_unique( array_merge( isset( $this->hooked_block_excluded_patterns ) ? $this->hooked_block_excluded_patterns : array(), array( 'twentytwentytwo/header-centered-logo', 'twentytwentytwo/header-stacked' ) ) )
|
||||
);
|
||||
|
||||
$pattern_slug = is_array( $context ) && isset( $context['slug'] ) ? $context['slug'] : '';
|
||||
if ( ! $pattern_slug ) {
|
||||
/**
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Utils;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\FeatureGating;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Blocks\Options;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutHeaderTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\CheckoutTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\MiniCartTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\OrderConfirmationTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\BlockTemplatesRegistry;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
|
||||
|
||||
/**
|
||||
* Utility methods used for serving block templates from WooCommerce Blocks.
|
||||
* {@internal This class and its methods should only be used within the BlockTemplateController.php and is not intended for public use.}
|
||||
*/
|
||||
class BlockTemplateUtils {
|
||||
const ELIGIBLE_FOR_ARCHIVE_PRODUCT_FALLBACK = array( 'taxonomy-product_cat', 'taxonomy-product_tag', ProductAttributeTemplate::SLUG );
|
||||
/**
|
||||
* Directory names for block templates
|
||||
*
|
||||
@@ -59,6 +54,18 @@ class BlockTemplateUtils {
|
||||
*/
|
||||
const DEPRECATED_PLUGIN_SLUG = 'woocommerce';
|
||||
|
||||
/**
|
||||
* Returns the template matching the slug
|
||||
*
|
||||
* @param string $template_slug Slug of the template to retrieve.
|
||||
*
|
||||
* @return AbstractTemplate|AbstractTemplatePart|null
|
||||
*/
|
||||
public static function get_template( $template_slug ) {
|
||||
$block_templates_registry = Package::container()->get( BlockTemplatesRegistry::class );
|
||||
return $block_templates_registry->get_template( $template_slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the references of
|
||||
* the passed blocks and their inner blocks.
|
||||
@@ -178,6 +185,19 @@ class BlockTemplateUtils {
|
||||
$template->origin = 'plugin';
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the block hooks algorithm introduced in WP 6.4 on the template content.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -207,7 +227,7 @@ class BlockTemplateUtils {
|
||||
$template->content = self::inject_theme_attribute_in_content( $template_content );
|
||||
// Remove the term description block from the archive-product template
|
||||
// as the Product Catalog/Shop page doesn't have a description.
|
||||
if ( 'archive-product' === $template_file->slug ) {
|
||||
if ( ProductCatalogTemplate::SLUG === $template_file->slug ) {
|
||||
$template->content = str_replace( '<!-- wp:term-description {"align":"wide"} /-->', '', $template->content );
|
||||
}
|
||||
// Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
|
||||
@@ -221,20 +241,23 @@ class BlockTemplateUtils {
|
||||
$template->origin = $template_file->source;
|
||||
$template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
|
||||
$template->post_types = array(); // Don't appear in any Edit Post template selector dropdown.
|
||||
$template->area = 'uncategorized';
|
||||
$template->area = self::get_block_template_area( $template->slug, $template_type );
|
||||
|
||||
// Force the Mini-Cart template part to be in the Mini-Cart template part area.
|
||||
// @todo When this class is refactored, move title, description, and area definition to the template classes (CheckoutHeaderTemplate, MiniCartTemplate, etc).
|
||||
if ( 'wp_template_part' === $template_type ) {
|
||||
switch ( $template_file->slug ) {
|
||||
case 'mini-cart':
|
||||
$template->area = 'mini-cart';
|
||||
break;
|
||||
case 'checkout-header':
|
||||
$template->area = 'header';
|
||||
break;
|
||||
/*
|
||||
* Run the block hooks algorithm introduced in WP 6.4 on the template content.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -270,18 +293,45 @@ class BlockTemplateUtils {
|
||||
/**
|
||||
* Finds all nested template part file paths in a theme's directory.
|
||||
*
|
||||
* @param string $base_directory The theme's file path.
|
||||
* @param string $template_type wp_template or wp_template_part.
|
||||
* @return array $path_list A list of paths to all template part files.
|
||||
*/
|
||||
public static function get_template_paths( $base_directory ) {
|
||||
$path_list = array();
|
||||
if ( file_exists( $base_directory ) ) {
|
||||
$nested_files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $base_directory ) );
|
||||
$nested_html_files = new \RegexIterator( $nested_files, '/^.+\.html$/i', \RecursiveRegexIterator::GET_MATCH );
|
||||
foreach ( $nested_html_files as $path => $file ) {
|
||||
$path_list[] = $path;
|
||||
}
|
||||
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',
|
||||
);
|
||||
|
||||
/*
|
||||
* This may return the blockified directory for wp_templates.
|
||||
* At the moment every template file has a corresponding blockified file.
|
||||
* If we decide to add a new template file that doesn't, we will need to update this logic.
|
||||
*/
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -309,15 +359,15 @@ class BlockTemplateUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns template titles.
|
||||
* Returns template title.
|
||||
*
|
||||
* @param string $template_slug The templates slug (e.g. single-product).
|
||||
* @param string $template_slug The template slug (e.g. single-product).
|
||||
* @return string Human friendly title.
|
||||
*/
|
||||
public static function get_block_template_title( $template_slug ) {
|
||||
$plugin_template_types = self::get_plugin_block_template_types();
|
||||
if ( isset( $plugin_template_types[ $template_slug ] ) ) {
|
||||
return $plugin_template_types[ $template_slug ]['title'];
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
if ( isset( $registered_template ) ) {
|
||||
return $registered_template->get_template_title();
|
||||
} else {
|
||||
// Human friendly title converted from the slug.
|
||||
return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
|
||||
@@ -325,72 +375,34 @@ class BlockTemplateUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns template descriptions.
|
||||
* Returns template description.
|
||||
*
|
||||
* @param string $template_slug The templates slug (e.g. single-product).
|
||||
* @param string $template_slug The template slug (e.g. single-product).
|
||||
* @return string Template description.
|
||||
*/
|
||||
public static function get_block_template_description( $template_slug ) {
|
||||
$plugin_template_types = self::get_plugin_block_template_types();
|
||||
if ( isset( $plugin_template_types[ $template_slug ] ) ) {
|
||||
return $plugin_template_types[ $template_slug ]['description'];
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
if ( isset( $registered_template ) ) {
|
||||
return $registered_template->get_template_description();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filtered list of plugin template types, containing their
|
||||
* localized titles and descriptions.
|
||||
* Returns area for template parts.
|
||||
*
|
||||
* @return array The plugin template types.
|
||||
* @param string $template_slug The template part slug (e.g. mini-cart).
|
||||
* @param string $template_type Either `wp_template` or `wp_template_part`.
|
||||
* @return string Template part area.
|
||||
*/
|
||||
public static function get_plugin_block_template_types() {
|
||||
return array(
|
||||
'single-product' => array(
|
||||
'title' => _x( 'Single Product', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays a single product.', 'woocommerce' ),
|
||||
),
|
||||
'archive-product' => array(
|
||||
'title' => _x( 'Product Catalog', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays your products.', 'woocommerce' ),
|
||||
),
|
||||
'taxonomy-product_cat' => array(
|
||||
'title' => _x( 'Products by Category', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays products filtered by a category.', 'woocommerce' ),
|
||||
),
|
||||
'taxonomy-product_tag' => array(
|
||||
'title' => _x( 'Products by Tag', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays products filtered by a tag.', 'woocommerce' ),
|
||||
),
|
||||
ProductAttributeTemplate::SLUG => array(
|
||||
'title' => _x( 'Products by Attribute', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays products filtered by an attribute.', 'woocommerce' ),
|
||||
),
|
||||
ProductSearchResultsTemplate::SLUG => array(
|
||||
'title' => _x( 'Product Search Results', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Displays search results for your store.', 'woocommerce' ),
|
||||
),
|
||||
MiniCartTemplate::SLUG => array(
|
||||
'title' => _x( 'Mini-Cart', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Template used to display the Mini-Cart drawer.', 'woocommerce' ),
|
||||
),
|
||||
CartTemplate::get_slug() => array(
|
||||
'title' => _x( 'Page: Cart', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'The Cart template displays the items selected by the user for purchase, including quantities, prices, and discounts. It allows users to review their choices before proceeding to checkout.', 'woocommerce' ),
|
||||
),
|
||||
CheckoutTemplate::get_slug() => array(
|
||||
'title' => _x( 'Page: Checkout', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'The Checkout template guides users through the final steps of the purchase process. It enables users to enter shipping and billing information, select a payment method, and review order details.', 'woocommerce' ),
|
||||
),
|
||||
CheckoutHeaderTemplate::SLUG => array(
|
||||
'title' => _x( 'Checkout Header', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'Template used to display the simplified Checkout header.', 'woocommerce' ),
|
||||
),
|
||||
OrderConfirmationTemplate::get_slug() => array(
|
||||
'title' => _x( 'Order Confirmation', 'Template name', 'woocommerce' ),
|
||||
'description' => __( 'The Order Confirmation template serves as a receipt and confirmation of a successful purchase. It includes a summary of the ordered items, shipping, billing, and totals.', 'woocommerce' ),
|
||||
),
|
||||
);
|
||||
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';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,7 +447,7 @@ class BlockTemplateUtils {
|
||||
// or the stylesheet directory for child themes.
|
||||
$possible_paths = array_reduce(
|
||||
$possible_templates_dir,
|
||||
function( $carry, $item ) use ( $template_filename ) {
|
||||
function ( $carry, $item ) use ( $template_filename ) {
|
||||
$filepath = DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $template_filename;
|
||||
|
||||
$carry[] = get_stylesheet_directory() . $filepath;
|
||||
@@ -502,7 +514,11 @@ class BlockTemplateUtils {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function template_is_eligible_for_product_archive_fallback( $template_slug ) {
|
||||
return in_array( $template_slug, self::ELIGIBLE_FOR_ARCHIVE_PRODUCT_FALLBACK, true );
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
|
||||
return ProductCatalogTemplate::SLUG === $registered_template->fallback_template;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,7 +537,7 @@ class BlockTemplateUtils {
|
||||
$array_filter = array_filter(
|
||||
$db_templates,
|
||||
function ( $template ) use ( $template_slug ) {
|
||||
return 'archive-product' === $template->slug;
|
||||
return ProductCatalogTemplate::SLUG === $template->slug;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -542,7 +558,7 @@ class BlockTemplateUtils {
|
||||
}
|
||||
|
||||
foreach ( $db_templates as $template ) {
|
||||
if ( 'archive-product' === $template->slug ) {
|
||||
if ( ProductCatalogTemplate::SLUG === $template->slug ) {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
@@ -562,7 +578,7 @@ class BlockTemplateUtils {
|
||||
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( 'archive-product' );
|
||||
&& self::theme_has_template( ProductCatalogTemplate::SLUG );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,13 +626,13 @@ class BlockTemplateUtils {
|
||||
|
||||
// Get the slugs of all templates that have been customised and saved in the database.
|
||||
$customised_template_slugs = array_map(
|
||||
function( $template ) {
|
||||
function ( $template ) {
|
||||
return $template->slug;
|
||||
},
|
||||
array_values(
|
||||
array_filter(
|
||||
$templates,
|
||||
function( $template ) {
|
||||
function ( $template ) {
|
||||
// This template has been customised and saved as a post.
|
||||
return 'custom' === $template->source;
|
||||
}
|
||||
@@ -631,7 +647,7 @@ class BlockTemplateUtils {
|
||||
return array_values(
|
||||
array_filter(
|
||||
$templates,
|
||||
function( $template ) use ( $customised_template_slugs ) {
|
||||
function ( $template ) use ( $customised_template_slugs ) {
|
||||
// This template has been customised and saved as a post, so return it.
|
||||
return ! ( 'theme' === $template->source && in_array( $template->slug, $customised_template_slugs, true ) );
|
||||
}
|
||||
@@ -651,7 +667,7 @@ class BlockTemplateUtils {
|
||||
public static function remove_duplicate_customized_templates( $templates, $theme_slug ) {
|
||||
$filtered_templates = array_filter(
|
||||
$templates,
|
||||
function( $template ) use ( $templates, $theme_slug ) {
|
||||
function ( $template ) use ( $templates, $theme_slug ) {
|
||||
if ( $template->theme === $theme_slug ) {
|
||||
// This is a customized template based on the theme template, so it should be returned.
|
||||
return true;
|
||||
@@ -660,7 +676,7 @@ class BlockTemplateUtils {
|
||||
// Only return it if there isn't a customized version of the theme template.
|
||||
$is_there_a_customized_theme_template = array_filter(
|
||||
$templates,
|
||||
function( $theme_template ) use ( $template, $theme_slug ) {
|
||||
function ( $theme_template ) use ( $template, $theme_slug ) {
|
||||
return $theme_template->slug === $template->slug && $theme_template->theme === $theme_slug;
|
||||
}
|
||||
);
|
||||
@@ -690,16 +706,6 @@ class BlockTemplateUtils {
|
||||
return wc_string_to_bool( $use_blockified_templates );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the passed `$template` has a title, and it's different from the slug.
|
||||
*
|
||||
* @param object $template The template object.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function template_has_title( $template ) {
|
||||
return ! empty( $template->title ) && $template->title !== $template->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the passed `$template` has the legacy template block.
|
||||
*
|
||||
@@ -710,6 +716,37 @@ class BlockTemplateUtils {
|
||||
return has_block( 'woocommerce/legacy-template', $template->content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the title, description and area of a template to the correct values and to make them more user-friendly.
|
||||
* For example, instead of:
|
||||
* - Title: `Tag (product_tag)`
|
||||
* - Description: `Displays taxonomy: Tag.`
|
||||
* we display:
|
||||
* - Title: `Products by Tag`
|
||||
* - Description: `Displays products filtered by a tag.`.
|
||||
*
|
||||
* @param WP_Block_Template $template The template object.
|
||||
* @param string $template_type wp_template or wp_template_part.
|
||||
*
|
||||
* @return WP_Block_Template
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the templates saved in the database.
|
||||
*
|
||||
@@ -740,7 +777,7 @@ class BlockTemplateUtils {
|
||||
$saved_woo_templates = $check_query->posts;
|
||||
|
||||
return array_map(
|
||||
function( $saved_woo_template ) {
|
||||
function ( $saved_woo_template ) {
|
||||
return self::build_template_result_from_post( $saved_woo_template );
|
||||
},
|
||||
$saved_woo_templates
|
||||
|
||||
@@ -15,12 +15,6 @@ class CartCheckoutUtils {
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
// Ignore the pages and check the templates.
|
||||
$templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'cart' ), 'wp_template' );
|
||||
|
||||
// If there is no template file, we're using default which does use the block.
|
||||
if ( empty( $templates_from_db ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( $templates_from_db as $template ) {
|
||||
if ( has_block( 'woocommerce/cart', $template->content ) ) {
|
||||
return true;
|
||||
@@ -40,12 +34,6 @@ class CartCheckoutUtils {
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
// Ignore the pages and check the templates.
|
||||
$templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'checkout' ), 'wp_template' );
|
||||
|
||||
// If there is no template file, we're using default which does use the block.
|
||||
if ( empty( $templates_from_db ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ( $templates_from_db as $template ) {
|
||||
if ( has_block( 'woocommerce/checkout', $template->content ) ) {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user