plugin updates
This commit is contained in:
@@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Blocks\Assets;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\Hydration;
|
||||
use Automattic\WooCommerce\Internal\Logging\RemoteLogger;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
|
||||
@@ -89,6 +90,7 @@ class AssetDataRegistry {
|
||||
'dateFormat' => wc_date_format(),
|
||||
'homeUrl' => esc_url( home_url( '/' ) ),
|
||||
'locale' => $this->get_locale_data(),
|
||||
'isRemoteLoggingEnabled' => wc_get_container()->get( RemoteLogger::class )->is_remote_logging_allowed(),
|
||||
'dashboardUrl' => wc_get_account_endpoint_url( 'dashboard' ),
|
||||
'orderStatuses' => $this->get_order_statuses(),
|
||||
'placeholderImgSrc' => wc_placeholder_img_src(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonTemplate;
|
||||
|
||||
@@ -27,7 +26,7 @@ class BlockTemplatesController {
|
||||
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, 3 );
|
||||
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
|
||||
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 );
|
||||
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_fallback_template_to_hierarchy' ), 10, 1 );
|
||||
add_filter( 'block_type_metadata_settings', array( $this, 'add_plugin_templates_parts_support' ), 10, 2 );
|
||||
add_filter( 'block_type_metadata_settings', array( $this, 'prevent_shortcodes_html_breakage' ), 10, 2 );
|
||||
add_action( 'current_screen', array( $this, 'hide_template_selector_in_cart_checkout_pages' ), 10 );
|
||||
@@ -54,6 +53,10 @@ class BlockTemplatesController {
|
||||
* This function is used on the `pre_get_block_template` hook to return the fallback template from the db in case
|
||||
* the template is eligible for it.
|
||||
*
|
||||
* Currently, the Products by Category, Products by Tag and Products by Attribute templates fall back to the
|
||||
* Product Catalog template. That means that if there are customizations in the Product Catalog template,
|
||||
* they are also reflected in the other templates as long as they haven't been customized as well.
|
||||
*
|
||||
* @param \WP_Block_Template|null $template Block template object to short-circuit the default query,
|
||||
* or null to allow WP to run its normal queries.
|
||||
* @param string $id Template unique identifier (example: theme_slug//template_slug).
|
||||
@@ -76,13 +79,14 @@ class BlockTemplatesController {
|
||||
$template_name_parts = explode( '//', $id );
|
||||
$theme = $template_name_parts[0] ?? '';
|
||||
$slug = $template_name_parts[1] ?? '';
|
||||
$registered_template = BlockTemplateUtils::get_template( $slug );
|
||||
|
||||
if ( empty( $theme ) || empty( $slug ) || ! BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $slug ) ) {
|
||||
if ( empty( $theme ) || empty( $slug ) || ! $registered_template || ! isset( $registered_template->fallback_template ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$wp_query_args = array(
|
||||
'post_name__in' => array( ProductCatalogTemplate::SLUG, $slug ),
|
||||
'post_name__in' => array( $registered_template->fallback_template, $slug ),
|
||||
'post_type' => $template_type,
|
||||
'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
|
||||
'no_found_rows' => true,
|
||||
@@ -98,12 +102,12 @@ class BlockTemplatesController {
|
||||
$posts = $template_query->posts;
|
||||
|
||||
// If we have more than one result from the query, it means that the current template is present in the db (has
|
||||
// been customized by the user) and we should not return the `archive-product` template.
|
||||
// been customized by the user) and we should not return the fallback template.
|
||||
if ( count( $posts ) > 1 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( count( $posts ) > 0 && ProductCatalogTemplate::SLUG === $posts[0]->post_name ) {
|
||||
if ( count( $posts ) > 0 && $registered_template->fallback_template === $posts[0]->post_name ) {
|
||||
$template = _build_block_template_result_from_post( $posts[0] );
|
||||
|
||||
if ( ! is_wp_error( $template ) ) {
|
||||
@@ -121,26 +125,21 @@ class BlockTemplatesController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the `archive-product` template to the `taxonomy-product_cat`, `taxonomy-product_tag`, `taxonomy-attribute`
|
||||
* templates to be able to fall back to it.
|
||||
* Adds the fallback template to the template hierarchy.
|
||||
*
|
||||
* @param array $template_hierarchy A list of template candidates, in descending order of priority.
|
||||
*/
|
||||
public function add_archive_product_to_eligible_for_fallback_templates( $template_hierarchy ) {
|
||||
public function add_fallback_template_to_hierarchy( $template_hierarchy ) {
|
||||
$template_slugs = array_map(
|
||||
'_strip_template_file_suffix',
|
||||
$template_hierarchy
|
||||
);
|
||||
|
||||
$templates_eligible_for_fallback = array_filter(
|
||||
$template_slugs,
|
||||
function ( $template_slug ) {
|
||||
return BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug );
|
||||
foreach ( $template_slugs as $template_slug ) {
|
||||
$template = BlockTemplateUtils::get_template( $template_slug );
|
||||
if ( $template && isset( $template->fallback_template ) ) {
|
||||
$template_hierarchy[] = $template->fallback_template;
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $templates_eligible_for_fallback ) > 0 ) {
|
||||
$template_hierarchy[] = ProductCatalogTemplate::SLUG;
|
||||
}
|
||||
|
||||
return $template_hierarchy;
|
||||
@@ -233,10 +232,12 @@ class BlockTemplatesController {
|
||||
|
||||
list( $template_id, $template_slug ) = $template_name_parts;
|
||||
|
||||
// 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( ProductCatalogTemplate::SLUG );
|
||||
$template_object = BlockTemplateUtils::create_new_block_template_object( $template_path, $template_type, $template_slug, true );
|
||||
// If the template is not present in the theme but its fallback template is,
|
||||
// let's use the theme's fallback template.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_theme( $template_slug ) ) {
|
||||
$registered_template = BlockTemplateUtils::get_template( $template_slug );
|
||||
$template_path = BlockTemplateUtils::get_theme_template_path( $registered_template->fallback_template );
|
||||
$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 );
|
||||
}
|
||||
|
||||
@@ -441,7 +442,7 @@ class BlockTemplatesController {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_db( $template_slug, $already_found_templates ) ) {
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_db( $template_slug, $already_found_templates ) ) {
|
||||
$template = clone BlockTemplateUtils::get_fallback_template_from_db( $template_slug, $already_found_templates );
|
||||
$template_id = explode( '//', $template->id );
|
||||
$template->id = $template_id[0] . '//' . $template_slug;
|
||||
@@ -452,10 +453,12 @@ class BlockTemplatesController {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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( ProductCatalogTemplate::SLUG );
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
|
||||
// If the template is not present in the theme but its fallback template is,
|
||||
// let's use the theme's fallback template.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_theme( $template_slug ) ) {
|
||||
$registered_template = BlockTemplateUtils::get_template( $template_slug );
|
||||
$template_file = BlockTemplateUtils::get_theme_template_path( $registered_template->fallback_template );
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ 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;
|
||||
|
||||
@@ -450,14 +450,16 @@ abstract class AbstractBlock {
|
||||
'wordCountType' => _x( 'words', 'Word count type. Do not translate!', 'woocommerce' ),
|
||||
];
|
||||
if ( is_admin() && ! WC()->is_rest_api_request() ) {
|
||||
$wc_blocks_config = array_merge(
|
||||
$product_counts = wp_count_posts( 'product' );
|
||||
$published_products = isset( $product_counts->publish ) ? $product_counts->publish : 0;
|
||||
$wc_blocks_config = array_merge(
|
||||
$wc_blocks_config,
|
||||
[
|
||||
// Note that while we don't have a consolidated way of doing feature-flagging
|
||||
// we are borrowing from the WC Admin Features implementation. Also note we cannot
|
||||
// use the wcAdminFeatures global because it's not always enqueued in the context of blocks.
|
||||
'experimentalBlocksEnabled' => Features::is_enabled( 'experimental-blocks' ),
|
||||
'productCount' => array_sum( (array) wp_count_posts( 'product' ) ),
|
||||
'productCount' => $published_products,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class AddToCartForm extends AbstractBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* It isn't necessary register block assets because it is a server side block.
|
||||
* It isn't necessary to register block assets because it is a server side block.
|
||||
*/
|
||||
protected function register_block_type_assets() {
|
||||
return null;
|
||||
|
||||
@@ -245,7 +245,6 @@ class Cart extends AbstractBlock {
|
||||
$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() );
|
||||
$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'] );
|
||||
|
||||
|
||||
@@ -156,35 +156,82 @@ class ProductCollection extends AbstractBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the Product Collection block with client-side pagination.
|
||||
* Check if next tag is a PC block.
|
||||
*
|
||||
* This function identifies Product Collection blocks and adds necessary data attributes
|
||||
* to enable client-side navigation and animation effects. It also enqueues the Interactivity API runtime.
|
||||
* @param WP_HTML_Tag_processor $p Initial tag processor.
|
||||
*
|
||||
* @return bool Answer if PC block is available.
|
||||
*/
|
||||
private function is_next_tag_product_collection( $p ) {
|
||||
return $p->next_tag( array( 'class_name' => 'wp-block-woocommerce-product-collection' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set PC block namespace for Interactivity API.
|
||||
*
|
||||
* @param WP_HTML_Tag_processor $p Initial tag processor.
|
||||
*/
|
||||
private function set_product_collection_namespace( $p ) {
|
||||
$p->set_attribute( 'data-wc-interactive', wp_json_encode( array( 'namespace' => 'woocommerce/product-collection' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the init directive to Product Collection block to call
|
||||
* the onRender callback.
|
||||
*
|
||||
* @param string $block_content The HTML content of the block.
|
||||
* @param array $block Block details, including its attributes.
|
||||
* @param string $collection Collection type.
|
||||
*
|
||||
* @return string Updated block content with added interactivity attributes.
|
||||
* @return string Updated HTML content.
|
||||
*/
|
||||
public function enhance_product_collection_with_interactivity( $block_content, $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' );
|
||||
private function add_rendering_callback( $block_content, $collection ) {
|
||||
$p = new \WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
$p = new \WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
// Add `data-wc-navigation-id to the product collection block.
|
||||
if ( $p->next_tag( array( 'class_name' => 'wp-block-woocommerce-product-collection' ) ) ) {
|
||||
$p->set_attribute(
|
||||
'data-wc-navigation-id',
|
||||
'wc-product-collection-' . $this->parsed_block['attrs']['queryId']
|
||||
);
|
||||
$p->set_attribute( 'data-wc-interactive', wp_json_encode( array( 'namespace' => 'woocommerce/product-collection' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ) );
|
||||
// Add `data-init to the product collection block so we trigger JS event on render.
|
||||
if ( $this->is_next_tag_product_collection( $p ) ) {
|
||||
$p->set_attribute(
|
||||
'data-wc-init',
|
||||
'callbacks.onRender'
|
||||
);
|
||||
if ( $collection ) {
|
||||
$p->set_attribute(
|
||||
'data-wc-context',
|
||||
wp_json_encode(
|
||||
array(
|
||||
'collection' => $collection,
|
||||
),
|
||||
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $p->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach all the Interactivity API directives responsible
|
||||
* for client-side navigation.
|
||||
*
|
||||
* @param string $block_content The HTML content of the block.
|
||||
*
|
||||
* @return string Updated HTML content.
|
||||
*/
|
||||
private function enable_client_side_navigation( $block_content ) {
|
||||
$p = new \WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
// Add `data-wc-navigation-id to the product collection block.
|
||||
if ( $this->is_next_tag_product_collection( $p ) ) {
|
||||
$p->set_attribute(
|
||||
'data-wc-navigation-id',
|
||||
'wc-product-collection-' . $this->parsed_block['attrs']['queryId']
|
||||
);
|
||||
$current_context = json_decode( $p->get_attribute( 'data-wc-context' ) ?? '{}', true );
|
||||
$p->set_attribute(
|
||||
'data-wc-context',
|
||||
wp_json_encode(
|
||||
array_merge(
|
||||
$current_context,
|
||||
array(
|
||||
// The message to be announced by the screen reader when the page is loading or loaded.
|
||||
'accessibilityLoadingMessage' => __( 'Loading page, please wait.', 'woocommerce' ),
|
||||
@@ -193,19 +240,20 @@ class ProductCollection extends AbstractBlock {
|
||||
// This way we avoid prefetching when the page loads.
|
||||
'isPrefetchNextOrPreviousLink' => false,
|
||||
),
|
||||
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
|
||||
)
|
||||
);
|
||||
$block_content = $p->get_updated_html();
|
||||
}
|
||||
),
|
||||
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
|
||||
)
|
||||
);
|
||||
$block_content = $p->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two div's:
|
||||
* 1. Pagination animation for visual users.
|
||||
* 2. Accessibility div for screen readers, to announce page load states.
|
||||
*/
|
||||
$last_tag_position = strripos( $block_content, '</div>' );
|
||||
$accessibility_and_animation_html = '
|
||||
/**
|
||||
* Add two div's:
|
||||
* 1. Pagination animation for visual users.
|
||||
* 2. Accessibility div for screen readers, to announce page load states.
|
||||
*/
|
||||
$last_tag_position = strripos( $block_content, '</div>' );
|
||||
$accessibility_and_animation_html = '
|
||||
<div
|
||||
data-wc-interactive="{"namespace":"woocommerce/product-collection"}"
|
||||
class="wc-block-product-collection__pagination-animation"
|
||||
@@ -219,12 +267,44 @@ class ProductCollection extends AbstractBlock {
|
||||
data-wc-text="context.accessibilityMessage">
|
||||
</div>
|
||||
';
|
||||
$block_content = substr_replace(
|
||||
$block_content,
|
||||
$accessibility_and_animation_html,
|
||||
$last_tag_position,
|
||||
0
|
||||
);
|
||||
return substr_replace(
|
||||
$block_content,
|
||||
$accessibility_and_animation_html,
|
||||
$last_tag_position,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the Product Collection block with client-side pagination.
|
||||
*
|
||||
* This function identifies Product Collection blocks and adds necessary data attributes
|
||||
* to enable client-side navigation and animation effects. It also enqueues the Interactivity API runtime.
|
||||
*
|
||||
* @param string $block_content The HTML content of the block.
|
||||
* @param array $block Block details, including its attributes.
|
||||
*
|
||||
* @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 ) {
|
||||
// Enqueue the Interactivity API runtime and set the namespace.
|
||||
wp_enqueue_script( 'wc-interactivity' );
|
||||
$p = new \WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $this->is_next_tag_product_collection( $p ) ) {
|
||||
$this->set_product_collection_namespace( $p );
|
||||
}
|
||||
$block_content = $p->get_updated_html();
|
||||
|
||||
$collection = $block['attrs']['collection'] ?? '';
|
||||
$block_content = $this->add_rendering_callback( $block_content, $collection );
|
||||
|
||||
$is_enhanced_pagination_enabled = ! ( $block['attrs']['forcePageReload'] ?? false );
|
||||
if ( $is_enhanced_pagination_enabled ) {
|
||||
$block_content = $this->enable_client_side_navigation( $block_content );
|
||||
}
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
@@ -295,7 +375,7 @@ class ProductCollection extends AbstractBlock {
|
||||
'class_name' => $class_name,
|
||||
)
|
||||
) ) {
|
||||
$processor->set_attribute( 'data-wc-interactive', wp_json_encode( array( 'namespace' => 'woocommerce/product-collection' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ) );
|
||||
$this->set_product_collection_namespace( $processor );
|
||||
$processor->set_attribute( 'data-wc-on--click', 'actions.navigate' );
|
||||
$processor->set_attribute( 'data-wc-key', $key_prefix . '--' . esc_attr( wp_rand() ) );
|
||||
|
||||
@@ -314,9 +394,11 @@ class ProductCollection extends AbstractBlock {
|
||||
*/
|
||||
private function is_block_compatible( $block_name ) {
|
||||
// Check for explicitly unsupported blocks.
|
||||
if ( 'core/post-content' === $block_name ||
|
||||
if (
|
||||
'core/post-content' === $block_name ||
|
||||
'woocommerce/mini-cart' === $block_name ||
|
||||
'woocommerce/featured-product' === $block_name ) {
|
||||
'woocommerce/featured-product' === $block_name
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -835,7 +917,7 @@ class ProductCollection extends AbstractBlock {
|
||||
if ( ! isset( $base[ $key ] ) ) {
|
||||
$base[ $key ] = array();
|
||||
}
|
||||
$base[ $key ] = $this->array_merge_recursive_replace_non_array_properties( $base[ $key ], $value );
|
||||
$base[ $key ] = $this->array_merge_recursive_replace_non_array_properties( $base[ $key ], $value );
|
||||
} else {
|
||||
$base[ $key ] = $value;
|
||||
}
|
||||
@@ -1092,7 +1174,7 @@ class ProductCollection extends AbstractBlock {
|
||||
$max_price_query = empty( $max_price ) ? array() : array(
|
||||
'key' => '_price',
|
||||
'value' => $max_price,
|
||||
'compare' => '<',
|
||||
'compare' => '<=',
|
||||
'type' => 'numeric',
|
||||
);
|
||||
|
||||
|
||||
@@ -30,6 +30,34 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
|
||||
add_filter( 'collection_filter_query_param_keys', array( $this, 'get_filter_query_param_keys' ), 10, 2 );
|
||||
add_filter( 'collection_active_filters_data', array( $this, 'register_active_filters_data' ), 10, 2 );
|
||||
add_action( 'deleted_transient', array( $this, 'delete_default_attribute_id_transient' ) );
|
||||
add_action( 'wp_loaded', array( $this, 'register_block_patterns' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data passed through from server to client for block.
|
||||
*
|
||||
* @param array $attributes Any attributes that currently are available from the block.
|
||||
* Note, this will be empty in the editor context when the block is
|
||||
* not in the post content on editor load.
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = array() ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
if ( is_admin() ) {
|
||||
$this->asset_data_registry->add( 'defaultProductFilterAttribute', $this->get_default_product_attribute() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the default attribute id transient when the attribute taxonomies are deleted.
|
||||
*
|
||||
* @param string $transient The transient name.
|
||||
*/
|
||||
public function delete_default_attribute_id_transient( $transient ) {
|
||||
if ( 'wc_attribute_taxonomies' === $transient ) {
|
||||
delete_transient( 'wc_block_product_filter_attribute_default_attribute' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +71,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
public function get_filter_query_param_keys( $filter_param_keys, $url_param_keys ) {
|
||||
$attribute_param_keys = array_filter(
|
||||
$url_param_keys,
|
||||
function( $param ) {
|
||||
function ( $param ) {
|
||||
return strpos( $param, 'filter_' ) === 0 || strpos( $param, 'query_type_' ) === 0;
|
||||
}
|
||||
);
|
||||
@@ -64,7 +92,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
public function register_active_filters_data( $data, $params ) {
|
||||
$product_attributes_map = array_reduce(
|
||||
wc_get_attribute_taxonomies(),
|
||||
function( $acc, $attribute_object ) {
|
||||
function ( $acc, $attribute_object ) {
|
||||
$acc[ $attribute_object->attribute_name ] = $attribute_object->attribute_label;
|
||||
return $acc;
|
||||
},
|
||||
@@ -73,7 +101,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
|
||||
$active_product_attributes = array_reduce(
|
||||
array_keys( $params ),
|
||||
function( $acc, $attribute ) {
|
||||
function ( $acc, $attribute ) {
|
||||
if ( strpos( $attribute, 'filter_' ) === 0 ) {
|
||||
$acc[] = str_replace( 'filter_', '', $attribute );
|
||||
}
|
||||
@@ -84,7 +112,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
|
||||
$active_product_attributes = array_filter(
|
||||
$active_product_attributes,
|
||||
function( $item ) use ( $product_attributes_map ) {
|
||||
function ( $item ) use ( $product_attributes_map ) {
|
||||
return in_array( $item, array_keys( $product_attributes_map ), true );
|
||||
}
|
||||
);
|
||||
@@ -96,7 +124,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
|
||||
// Get attribute term by slug.
|
||||
$terms = array_map(
|
||||
function( $term ) use ( $product_attribute, $action_namespace ) {
|
||||
function ( $term ) use ( $product_attribute, $action_namespace ) {
|
||||
$term_object = get_term_by( 'slug', $term, "pa_{$product_attribute}" );
|
||||
return array(
|
||||
'title' => $term_object->name,
|
||||
@@ -134,6 +162,11 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
* @return string Rendered block type output.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
if ( empty( $attributes['attributeId'] ) ) {
|
||||
$default_product_attribute = $this->get_default_product_attribute();
|
||||
$attributes['attributeId'] = $default_product_attribute->attribute_id;
|
||||
}
|
||||
|
||||
// don't render if its admin, or ajax in progress.
|
||||
if ( is_admin() || wp_doing_ajax() || empty( $attributes['attributeId'] ) ) {
|
||||
return '';
|
||||
@@ -169,7 +202,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
);
|
||||
|
||||
$attribute_options = array_map(
|
||||
function( $term ) use ( $attribute_counts, $selected_terms ) {
|
||||
function ( $term ) use ( $attribute_counts, $selected_terms ) {
|
||||
$term = (array) $term;
|
||||
$term['count'] = $attribute_counts[ $term['term_id'] ];
|
||||
$term['selected'] = in_array( $term['slug'], $selected_terms, true );
|
||||
@@ -180,7 +213,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
|
||||
$filtered_options = array_filter(
|
||||
$attribute_options,
|
||||
function( $option ) {
|
||||
function ( $option ) {
|
||||
return $option['count'] > 0;
|
||||
}
|
||||
);
|
||||
@@ -192,7 +225,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
$context = array(
|
||||
'attributeSlug' => str_replace( 'pa_', '', $product_attribute->slug ),
|
||||
'queryType' => $attributes['queryType'],
|
||||
'selectType' => $attributes['selectType'],
|
||||
'selectType' => 'multiple',
|
||||
);
|
||||
|
||||
return sprintf(
|
||||
@@ -243,7 +276,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
'items' => $list_items,
|
||||
'action' => "{$this->get_full_block_name()}::actions.navigate",
|
||||
'selected_items' => $selected_items,
|
||||
'select_type' => $attributes['selectType'] ?? 'multiple',
|
||||
'select_type' => 'multiple',
|
||||
// translators: %s is a product attribute name.
|
||||
'placeholder' => sprintf( __( 'Select %s', 'woocommerce' ), $product_attribute->name ),
|
||||
)
|
||||
@@ -265,7 +298,7 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
$show_counts = $attributes['showCounts'] ?? false;
|
||||
|
||||
$list_options = array_map(
|
||||
function( $option ) use ( $show_counts ) {
|
||||
function ( $option ) use ( $show_counts ) {
|
||||
return array(
|
||||
'id' => $option['slug'] . '-' . $option['term_id'],
|
||||
'checked' => $option['selected'],
|
||||
@@ -325,9 +358,128 @@ final class ProductFilterAttribute extends AbstractBlock {
|
||||
$acc[ $count['term'] ] = $count['count'];
|
||||
return $acc;
|
||||
},
|
||||
[]
|
||||
array()
|
||||
);
|
||||
|
||||
return $attribute_counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribute if with most term but closest to 30 terms.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
private function get_default_product_attribute() {
|
||||
// Cache this variable in memory to prevent repeated database queries to check
|
||||
// for transient in the same request.
|
||||
static $cached = null;
|
||||
|
||||
if ( $cached ) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$cached = get_transient( 'wc_block_product_filter_attribute_default_attribute' );
|
||||
|
||||
if ( $cached ) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$attributes = wc_get_attribute_taxonomies();
|
||||
|
||||
$attributes_count = array_map(
|
||||
function ( $attribute ) {
|
||||
return intval(
|
||||
wp_count_terms(
|
||||
array(
|
||||
'taxonomy' => 'pa_' . $attribute->attribute_name,
|
||||
'hide_empty' => false,
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
$attributes
|
||||
);
|
||||
|
||||
asort( $attributes_count );
|
||||
|
||||
$search = 30;
|
||||
$closest = null;
|
||||
$attribute_id = null;
|
||||
|
||||
foreach ( $attributes_count as $id => $count ) {
|
||||
if ( null === $closest || abs( $search - $closest ) > abs( $count - $search ) ) {
|
||||
$closest = $count;
|
||||
$attribute_id = $id;
|
||||
}
|
||||
|
||||
if ( $closest && $count >= $search ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$default_attribute = (object) array(
|
||||
'attribute_id' => '0',
|
||||
'attribute_name' => 'attribute',
|
||||
'attribute_label' => __( 'Attribute', 'woocommerce' ),
|
||||
'attribute_type' => 'select',
|
||||
'attribute_orderby' => 'menu_order',
|
||||
'attribute_public' => 0,
|
||||
);
|
||||
|
||||
if ( $attribute_id ) {
|
||||
$default_attribute = $attributes[ $attribute_id ];
|
||||
}
|
||||
|
||||
set_transient( 'wc_block_product_filter_attribute_default_attribute', $default_attribute );
|
||||
|
||||
return $default_attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register pattern for default product attribute.
|
||||
*/
|
||||
public function register_block_patterns() {
|
||||
$default_attribute = $this->get_default_product_attribute();
|
||||
register_block_pattern(
|
||||
'woocommerce/default-attribute-filter',
|
||||
array(
|
||||
'title' => '',
|
||||
'inserter' => false,
|
||||
'content' => strtr(
|
||||
'
|
||||
<!-- wp:woocommerce/product-filter {"filterType":"attribute-filter","attributeId":{{attribute_id}}} -->
|
||||
<!-- wp:group {"metadata":{"name":"Header"},"style":{"spacing":{"blockGap":"0"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
|
||||
<div class="wp-block-group">
|
||||
<!-- wp:heading {"level":3} -->
|
||||
<h3 class="wp-block-heading">{{attribute_label}}</h3>
|
||||
<!-- /wp:heading -->
|
||||
|
||||
<!-- wp:woocommerce/product-filter-clear-button {"lock":{"remove":true,"move":false}} -->
|
||||
<!-- wp:buttons {"layout":{"type":"flex"}} -->
|
||||
<div class="wp-block-buttons">
|
||||
<!-- wp:button {"className":"wc-block-product-filter-clear-button is-style-outline","style":{"border":{"width":"0px","style":"none"},"typography":{"textDecoration":"underline"},"outline":"none","fontSize":"medium"}} -->
|
||||
<div
|
||||
class="wp-block-button wc-block-product-filter-clear-button is-style-outline"
|
||||
style="text-decoration: underline"
|
||||
>
|
||||
<a class="wp-block-button__link wp-element-button" style="border-style: none; border-width: 0px">Clear</a>
|
||||
</div>
|
||||
<!-- /wp:button -->
|
||||
</div>
|
||||
<!-- /wp:buttons -->
|
||||
<!-- /wp:woocommerce/product-filter-clear-button -->
|
||||
</div>
|
||||
<!-- /wp:group -->
|
||||
|
||||
<!-- wp:woocommerce/product-filter-attribute {"attributeId":{{attribute_id}},"lock":{"remove":true}} /-->
|
||||
<!-- /wp:woocommerce/product-filter -->
|
||||
',
|
||||
array(
|
||||
'{{attribute_id}}' => intval( $default_attribute->attribute_id ),
|
||||
'{{attribute_label}}' => esc_html( $default_attribute->attribute_label ),
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,8 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
type="text"
|
||||
value="%s"
|
||||
data-wc-bind--value="state.formattedMinPrice"
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
data-wc-on--input="actions.updateProducts"
|
||||
data-wc-on--focus="actions.selectInputContent"
|
||||
pattern=""
|
||||
/>',
|
||||
wp_strip_all_tags( $formatted_min_price )
|
||||
@@ -189,7 +190,8 @@ final class ProductFilterPrice extends AbstractBlock {
|
||||
type="text"
|
||||
value="%s"
|
||||
data-wc-bind--value="state.formattedMaxPrice"
|
||||
data-wc-on--change="actions.updateProducts"
|
||||
data-wc-on--input="actions.updateProducts"
|
||||
data-wc-on--focus="actions.selectInputContent"
|
||||
/>',
|
||||
wp_strip_all_tags( $formatted_max_price )
|
||||
) : sprintf(
|
||||
|
||||
@@ -11,4 +11,87 @@ class ProductFiltersOverlayNavigation extends AbstractBlock {
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'product-filters-overlay-navigation';
|
||||
|
||||
/**
|
||||
* Register the context
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_block_type_uses_context() {
|
||||
return [ 'woocommerce/product-filters/overlay' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend script handle for this block type.
|
||||
*
|
||||
* @see $this->register_block_type()
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return array|string|null
|
||||
*/
|
||||
protected function get_block_type_script( $key = null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include and render the block.
|
||||
*
|
||||
* @param array $attributes Block attributes. Default empty array.
|
||||
* @param string $content Block content. Default empty string.
|
||||
* @param WP_Block $block Block instance.
|
||||
* @return string Rendered block type output.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
$wrapper_attributes = get_block_wrapper_attributes(
|
||||
array(
|
||||
'class' => 'wc-block-product-filters-overlay-navigation',
|
||||
)
|
||||
);
|
||||
$overlay_mode = $block->context['woocommerce/product-filters/overlay'];
|
||||
|
||||
if ( 'never' === $overlay_mode || ( ! wp_is_mobile() && 'mobile' === $overlay_mode ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$html_content = strtr(
|
||||
'<div {{wrapper_attributes}}>
|
||||
{{primary_content}}
|
||||
{{secondary_content}}
|
||||
</div>',
|
||||
array(
|
||||
'{{wrapper_attributes}}' => $wrapper_attributes,
|
||||
'{{primary_content}}' => 'open-overlay' === $attributes['triggerType'] ? $this->render_icon( $attributes ) : $this->render_label( $attributes ),
|
||||
'{{secondary_content}}' => 'open-overlay' === $attributes['triggerType'] ? $this->render_label( $attributes ) : $this->render_icon( $attributes ),
|
||||
)
|
||||
);
|
||||
return $html_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon to render depending on the triggerType attribute.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*
|
||||
* @return string Label to render on the block
|
||||
*/
|
||||
private function render_icon( $attributes ) {
|
||||
if ( 'open-overlay' === $attributes['triggerType'] ) {
|
||||
return '<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" style="width: 16px; height: 16px;"><path d="M10 17.5H14V16H10V17.5ZM6 6V7.5H18V6H6ZM8 12.5H16V11H8V12.5Z" fill="currentColor"></path></svg>';
|
||||
}
|
||||
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true" focusable="false" style="width: 16px; height: 16px;"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label to render depending on the triggerType.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
*
|
||||
* @return string Label to render on the block
|
||||
*/
|
||||
private function render_label( $attributes ) {
|
||||
return sprintf(
|
||||
'<span>%s</span>',
|
||||
'open-overlay' === $attributes['triggerType'] ? __( 'Filters', 'woocommerce' ) : __( 'Close', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ class ProductGalleryLargeImageNextPrevious extends AbstractBlock {
|
||||
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product instanceof \WC_Product ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$product_gallery = $product->get_gallery_image_ids();
|
||||
|
||||
if ( empty( $product_gallery ) ) {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* ProductMeta class.
|
||||
*/
|
||||
class ProductMeta extends AbstractBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'product-meta';
|
||||
|
||||
/**
|
||||
* Get the editor script data for this block type.
|
||||
*
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return null
|
||||
*/
|
||||
protected function get_block_type_editor_script( $key = null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the editor style handle for this block type.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function get_block_type_editor_style() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend script handle for this block type.
|
||||
*
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return null
|
||||
*/
|
||||
protected function get_block_type_script( $key = null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend style handle for this block type.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function get_block_type_style() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -619,7 +619,7 @@ class ProductQuery extends AbstractBlock {
|
||||
$max_price_query = empty( $max_price ) ? array() : [
|
||||
'key' => '_price',
|
||||
'value' => $max_price,
|
||||
'compare' => '<',
|
||||
'compare' => '<=',
|
||||
'type' => 'numeric',
|
||||
];
|
||||
|
||||
|
||||
@@ -17,6 +17,18 @@ class ProductTemplate extends AbstractBlock {
|
||||
*/
|
||||
protected $block_name = 'product-template';
|
||||
|
||||
/**
|
||||
* Initialize this block type.
|
||||
*
|
||||
* - Hook into WP lifecycle.
|
||||
* - Register the block with WordPress.
|
||||
* - Hook into pre_render_block to update the query.
|
||||
*/
|
||||
protected function initialize() {
|
||||
add_filter( 'block_type_metadata_settings', array( $this, 'add_block_type_metadata_settings' ), 10, 2 );
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend script handle for this block type.
|
||||
*
|
||||
@@ -134,4 +146,19 @@ class ProductTemplate extends AbstractBlock {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Product Template renders inner blocks manually so we need to skip default
|
||||
* rendering routine for its inner blocks
|
||||
*
|
||||
* @param array $settings Array of determined settings for registering a block type.
|
||||
* @param array $metadata Metadata provided for registering a block type.
|
||||
* @return array
|
||||
*/
|
||||
public function add_block_type_metadata_settings( $settings, $metadata ) {
|
||||
if ( ! empty( $metadata['name'] ) && 'woocommerce/product-template' === $metadata['name'] ) {
|
||||
$settings['skip_inner_blocks'] = true;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +350,7 @@ final class BlockTypesController {
|
||||
'ProductGalleryThumbnails',
|
||||
'ProductImage',
|
||||
'ProductImageGallery',
|
||||
'ProductMeta',
|
||||
'ProductNew',
|
||||
'ProductOnSale',
|
||||
'ProductPrice',
|
||||
@@ -437,7 +438,6 @@ final class BlockTypesController {
|
||||
$block_types = array_diff(
|
||||
$block_types,
|
||||
array(
|
||||
'AddToCartForm',
|
||||
'Breadcrumbs',
|
||||
'CatalogSorting',
|
||||
'ClassicTemplate',
|
||||
|
||||
@@ -508,6 +508,7 @@ class CheckoutFields {
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'email',
|
||||
'autocapitalize' => 'none',
|
||||
'type' => 'email',
|
||||
'index' => 0,
|
||||
],
|
||||
'country' => [
|
||||
@@ -780,7 +781,7 @@ class CheckoutFields {
|
||||
* @return mixed
|
||||
*/
|
||||
public function update_default_locale_with_fields( $locale ) {
|
||||
foreach ( $this->fields_locations['address'] as $field_id => $additional_field ) {
|
||||
foreach ( $this->get_fields_for_location( 'address' ) as $field_id => $additional_field ) {
|
||||
if ( empty( $locale[ $field_id ] ) ) {
|
||||
$locale[ $field_id ] = $additional_field;
|
||||
}
|
||||
|
||||
@@ -28,50 +28,82 @@ class CheckboxList {
|
||||
$items = $props['items'] ?? array();
|
||||
$checkbox_list_context = array( 'items' => $items );
|
||||
$on_change = $props['on_change'] ?? '';
|
||||
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-checkbox-list' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP );
|
||||
|
||||
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-checkbox-list' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP );
|
||||
|
||||
$checked_items = array_filter(
|
||||
$items,
|
||||
function ( $item ) {
|
||||
return $item['checked'];
|
||||
}
|
||||
);
|
||||
$show_initially = $props['show_initially'] ?? 15;
|
||||
$remaining_initial_unchecked = count( $checked_items ) > $show_initially ? count( $checked_items ) : $show_initially - count( $checked_items );
|
||||
$count = 0;
|
||||
ob_start();
|
||||
?>
|
||||
<div data-wc-interactive='<?php echo esc_attr( $namespace ); ?>'>
|
||||
<div data-wc-context='<?php echo wp_json_encode( $checkbox_list_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); ?>' >
|
||||
<div class="wc-block-stock-filter style-list">
|
||||
<ul class="wc-block-components-checkbox-list">
|
||||
<?php foreach ( $items as $item ) { ?>
|
||||
<?php
|
||||
$item['id'] = $item['id'] ?? uniqid( 'checkbox-' );
|
||||
// translators: %s: checkbox label.
|
||||
$i18n_label = sprintf( __( 'Checkbox: %s', 'woocommerce' ), $item['aria_label'] ?? '' );
|
||||
?>
|
||||
<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"
|
||||
aria-invalid="false"
|
||||
aria-label="<?php echo esc_attr( $i18n_label ); ?>"
|
||||
data-wc-on--change--select-item="actions.selectCheckboxItem"
|
||||
data-wc-on--change--parent-action="<?php echo esc_attr( $on_change ); ?>"
|
||||
value="<?php echo esc_attr( $item['value'] ); ?>"
|
||||
<?php checked( $item['checked'], 1 ); ?>
|
||||
>
|
||||
<svg class="wc-block-components-checkbox__mark" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 20">
|
||||
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path>
|
||||
</svg>
|
||||
<span class="wc-block-components-checkbox__label">
|
||||
<?php // The label can be HTML, so we don't want to escape it. ?>
|
||||
<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php echo $item['label']; ?>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="wc-block-interactivity-components-checkbox-list"
|
||||
data-wc-interactive='<?php echo esc_attr( $namespace ); ?>'
|
||||
data-wc-context='<?php echo wp_json_encode( $checkbox_list_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); ?>'
|
||||
>
|
||||
<ul class="wc-block-interactivity-components-checkbox-list__list">
|
||||
<?php foreach ( $items as $item ) { ?>
|
||||
<?php
|
||||
$item['id'] = $item['id'] ?? uniqid( 'checkbox-' );
|
||||
// translators: %s: checkbox label.
|
||||
$i18n_label = sprintf( __( 'Checkbox: %s', 'woocommerce' ), $item['aria_label'] ?? '' );
|
||||
?>
|
||||
<li
|
||||
data-wc-key="<?php echo esc_attr( $item['id'] ); ?>"
|
||||
<?php
|
||||
if ( ! $item['checked'] ) :
|
||||
if ( $count >= $remaining_initial_unchecked ) :
|
||||
?>
|
||||
class="wc-block-interactivity-components-checkbox-list__item hidden"
|
||||
data-wc-class--hidden="!context.showAll"
|
||||
<?php else : ?>
|
||||
<?php ++$count; ?>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
class="wc-block-interactivity-components-checkbox-list__item"
|
||||
>
|
||||
<label
|
||||
class="wc-block-interactivity-components-checkbox-list__label"
|
||||
for="<?php echo esc_attr( $item['id'] ); ?>"
|
||||
>
|
||||
<span class="wc-block-interactivity-components-checkbox-list__input-wrapper">
|
||||
<input
|
||||
id="<?php echo esc_attr( $item['id'] ); ?>"
|
||||
class="wc-block-interactivity-components-checkbox-list__input"
|
||||
type="checkbox"
|
||||
aria-invalid="false"
|
||||
aria-label="<?php echo esc_attr( $i18n_label ); ?>"
|
||||
data-wc-on--change--select-item="actions.selectCheckboxItem"
|
||||
data-wc-on--change--parent-action="<?php echo esc_attr( $on_change ); ?>"
|
||||
value="<?php echo esc_attr( $item['value'] ); ?>"
|
||||
<?php checked( $item['checked'], 1 ); ?>
|
||||
>
|
||||
<svg class="wc-block-interactivity-components-checkbox-list__mark" viewBox="0 0 10 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.25 1.19922L3.75 6.69922L1 3.94922" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="wc-block-interactivity-components-checkbox-list__text">
|
||||
<?php echo wp_kses_post( $item['label'] ); ?>
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<?php if ( count( $items ) > $show_initially ) : ?>
|
||||
<span
|
||||
role="button"
|
||||
class="wc-block-interactivity-components-checkbox-list__show-more"
|
||||
data-wc-class--hidden="context.showAll"
|
||||
data-wc-on--click="actions.showAllItems"
|
||||
>
|
||||
<small role="presentation"><?php echo esc_html__( 'Show more...', 'woocommerce' ); ?></small>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
|
||||
@@ -14,7 +14,6 @@ abstract class AbstractPageTemplate extends AbstractTemplate {
|
||||
*/
|
||||
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' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +47,10 @@ abstract class AbstractPageTemplate extends AbstractTemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the page title when the template is active.
|
||||
* Forces the page title to match the template title when this template is active.
|
||||
*
|
||||
* Only applies when hooked into `pre_get_document_title`. Most templates used for pages will not require this because
|
||||
* the page title should be used instead.
|
||||
*
|
||||
* @param string $title Page title.
|
||||
* @return string
|
||||
|
||||
@@ -20,7 +20,7 @@ class OrderConfirmationTemplate extends AbstractPageTemplate {
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'wp_before_admin_bar_render', array( $this, 'remove_edit_page_link' ) );
|
||||
|
||||
add_filter( 'pre_get_document_title', array( $this, 'page_template_title' ) );
|
||||
parent::init();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
* Since that there is a custom logic for the first and last block, we have to inject the hooks manually.
|
||||
* The first block supports the following hooks:
|
||||
* woocommerce_before_single_product
|
||||
* woocommerce_before_single_product_summary
|
||||
*
|
||||
* The last block supports the following hooks:
|
||||
* woocommerce_after_single_product
|
||||
@@ -69,6 +70,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
'before' => array(
|
||||
'woocommerce_before_main_content' => $this->hook_data['woocommerce_before_main_content'],
|
||||
'woocommerce_before_single_product' => $this->hook_data['woocommerce_before_single_product'],
|
||||
'woocommerce_before_single_product_summary' => $this->hook_data['woocommerce_before_single_product_summary'],
|
||||
),
|
||||
'after' => array(),
|
||||
);
|
||||
@@ -195,7 +197,7 @@ class SingleProductTemplateCompatibility extends AbstractTemplateCompatibility {
|
||||
),
|
||||
),
|
||||
'woocommerce_before_single_product_summary' => array(
|
||||
'block_names' => array( 'core/post-excerpt' ),
|
||||
'block_names' => array(),
|
||||
'position' => 'before',
|
||||
'hooked' => array(
|
||||
'woocommerce_show_product_sale_flash' => 10,
|
||||
|
||||
@@ -509,23 +509,6 @@ class BlockTemplateUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we can fall back to the `archive-product` template for a given slug.
|
||||
*
|
||||
* `taxonomy-product_cat`, `taxonomy-product_tag`, `taxonomy-product_attribute` templates can
|
||||
* generally use the `archive-product` as a fallback if there are no specific overrides.
|
||||
*
|
||||
* @param string $template_slug Slug to check for fallbacks.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function template_is_eligible_for_product_archive_fallback( $template_slug ) {
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
|
||||
return ProductCatalogTemplate::SLUG === $registered_template->fallback_template;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we can fall back to an `archive-product` template stored on the db for a given slug.
|
||||
*
|
||||
@@ -533,20 +516,21 @@ class BlockTemplateUtils {
|
||||
* @param array $db_templates Templates that have already been found on the db.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function template_is_eligible_for_product_archive_fallback_from_db( $template_slug, $db_templates ) {
|
||||
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
|
||||
if ( ! $eligible_for_fallback ) {
|
||||
return false;
|
||||
public static function template_is_eligible_for_fallback_from_db( $template_slug, $db_templates ) {
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
|
||||
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
|
||||
$array_filter = array_filter(
|
||||
$db_templates,
|
||||
function ( $template ) use ( $registered_template ) {
|
||||
return isset( $registered_template->fallback_template ) && $registered_template->fallback_template === $template->slug;
|
||||
}
|
||||
);
|
||||
|
||||
return count( $array_filter ) > 0;
|
||||
}
|
||||
|
||||
$array_filter = array_filter(
|
||||
$db_templates,
|
||||
function ( $template ) use ( $template_slug ) {
|
||||
return ProductCatalogTemplate::SLUG === $template->slug;
|
||||
}
|
||||
);
|
||||
|
||||
return count( $array_filter ) > 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -557,14 +541,13 @@ class BlockTemplateUtils {
|
||||
* @return boolean|object
|
||||
*/
|
||||
public static function get_fallback_template_from_db( $template_slug, $db_templates ) {
|
||||
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
|
||||
if ( ! $eligible_for_fallback ) {
|
||||
return false;
|
||||
}
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
|
||||
foreach ( $db_templates as $template ) {
|
||||
if ( ProductCatalogTemplate::SLUG === $template->slug ) {
|
||||
return $template;
|
||||
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
|
||||
foreach ( $db_templates as $template ) {
|
||||
if ( $registered_template->fallback_template === $template->slug ) {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,10 +563,12 @@ class BlockTemplateUtils {
|
||||
* @param string $template_slug Slug to check for fallbacks.
|
||||
* @return boolean
|
||||
*/
|
||||
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 )
|
||||
public static function template_is_eligible_for_fallback_from_theme( $template_slug ) {
|
||||
$registered_template = self::get_template( $template_slug );
|
||||
|
||||
return $registered_template && isset( $registered_template->fallback_template )
|
||||
&& ! self::theme_has_template( $template_slug )
|
||||
&& self::theme_has_template( ProductCatalogTemplate::SLUG );
|
||||
&& self::theme_has_template( $registered_template->fallback_template );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,7 +594,7 @@ class BlockTemplateUtils {
|
||||
$query_result_template->slug === $template->slug
|
||||
&& $query_result_template->theme === $template->theme
|
||||
) {
|
||||
if ( self::template_is_eligible_for_product_archive_fallback_from_theme( $template->slug ) ) {
|
||||
if ( self::template_is_eligible_for_fallback_from_theme( $template->slug ) ) {
|
||||
$query_result_template->has_theme_file = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user