plugin updates
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' ),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -332,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'];
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ComingSoonTemplate;
|
||||
|
||||
/**
|
||||
* BlockTypesController class.
|
||||
@@ -26,7 +27,6 @@ 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( '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_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 );
|
||||
@@ -36,13 +36,13 @@ class BlockTemplatesController {
|
||||
// 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;
|
||||
},
|
||||
@@ -54,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.
|
||||
@@ -92,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; }' );
|
||||
}
|
||||
},
|
||||
@@ -202,7 +202,7 @@ class BlockTemplatesController {
|
||||
|
||||
$templates_eligible_for_fallback = array_filter(
|
||||
$template_slugs,
|
||||
function( $template_slug ) {
|
||||
function ( $template_slug ) {
|
||||
return BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug );
|
||||
}
|
||||
);
|
||||
@@ -307,19 +307,7 @@ class BlockTemplatesController {
|
||||
* @return WP_Block_Template|null
|
||||
*/
|
||||
public function add_block_template_details( $block_template, $id, $template_type ) {
|
||||
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 );
|
||||
}
|
||||
if ( ! $block_template->area || 'uncategorized' === $block_template->area ) {
|
||||
$block_template->area = BlockTemplateUtils::get_block_template_area( $block_template->slug, $template_type );
|
||||
}
|
||||
return $block_template;
|
||||
return BlockTemplateUtils::update_template_data( $block_template, $template_type );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,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();
|
||||
|
||||
@@ -398,18 +387,8 @@ class BlockTemplatesController {
|
||||
* templates that aren't listed in theme.json.
|
||||
*/
|
||||
$query_result = array_map(
|
||||
function( $template ) use ( $template_type ) {
|
||||
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 );
|
||||
}
|
||||
if ( ! $template->area || 'uncategorized' === $template->area ) {
|
||||
$template->area = BlockTemplateUtils::get_block_template_area( $template->slug, $template_type );
|
||||
}
|
||||
|
||||
return $template;
|
||||
function ( $template ) use ( $template_type ) {
|
||||
return BlockTemplateUtils::update_template_data( $template, $template_type );
|
||||
},
|
||||
$query_result
|
||||
);
|
||||
@@ -441,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;
|
||||
@@ -582,30 +555,4 @@ class BlockTemplatesController {
|
||||
|
||||
return $post_type_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?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;
|
||||
@@ -8,6 +9,9 @@ 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;
|
||||
@@ -49,6 +53,9 @@ class BlockTemplatesRegistry {
|
||||
} 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(),
|
||||
|
||||
@@ -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(
|
||||
[
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
|
||||
|
||||
/**
|
||||
* Cart class.
|
||||
@@ -245,9 +246,8 @@ class Cart extends AbstractBlock {
|
||||
$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 = get_option( 'woocommerce_pickup_location_settings', [] );
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', wc_string_to_bool( $pickup_location_settings['enabled'] ?? 'no' ) );
|
||||
$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,7 +321,20 @@ class Checkout extends AbstractBlock {
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data() );
|
||||
$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',
|
||||
@@ -279,8 +361,9 @@ class Checkout extends AbstractBlock {
|
||||
$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() );
|
||||
|
||||
$pickup_location_settings = get_option( 'woocommerce_pickup_location_settings', [] );
|
||||
$this->asset_data_registry->add( 'localPickupEnabled', wc_string_to_bool( $pickup_location_settings['enabled'] ?? 'no' ) );
|
||||
$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();
|
||||
|
||||
@@ -487,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';
|
||||
}
|
||||
@@ -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' );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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' ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ] );
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -19,7 +19,7 @@ if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_fie
|
||||
add_action(
|
||||
'woocommerce_blocks_loaded',
|
||||
function () use ( $options ) {
|
||||
__experimental_woocommerce_blocks_register_checkout_field( $options );
|
||||
woocommerce_register_additional_checkout_field( $options );
|
||||
}
|
||||
);
|
||||
return;
|
||||
@@ -32,6 +32,20 @@ if ( ! function_exists( '__experimental_woocommerce_blocks_register_checkout_fie
|
||||
}
|
||||
}
|
||||
|
||||
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' ) ) {
|
||||
/**
|
||||
|
||||
@@ -55,7 +55,7 @@ class ShippingController {
|
||||
if ( is_admin() ) {
|
||||
$this->asset_data_registry->add(
|
||||
'countryStates',
|
||||
function() {
|
||||
function () {
|
||||
return WC()->countries->get_states();
|
||||
}
|
||||
);
|
||||
@@ -63,9 +63,9 @@ class ShippingController {
|
||||
|
||||
$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 );
|
||||
@@ -74,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
|
||||
@@ -98,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 );
|
||||
@@ -129,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() );
|
||||
@@ -138,7 +124,7 @@ class ShippingController {
|
||||
$address = $shipping_method->get_meta( 'pickup_address' );
|
||||
|
||||
if ( ! $address ) {
|
||||
return $return;
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
@@ -160,7 +146,7 @@ class ShippingController {
|
||||
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
|
||||
$settings[ $index ]['desc'] = sprintf(
|
||||
/* translators: %s: URL to the documentation. */
|
||||
__( 'Not available when using the <a href="%s">Local pickup options powered by the Checkout block</a>.', 'woocommerce' ),
|
||||
__( '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;
|
||||
@@ -180,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',
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -267,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;
|
||||
@@ -306,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,
|
||||
@@ -328,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 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,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(
|
||||
@@ -420,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 ) );
|
||||
}
|
||||
);
|
||||
@@ -429,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 );
|
||||
}
|
||||
);
|
||||
@@ -481,7 +467,7 @@ class ShippingController {
|
||||
'pickup_locations_enabled' => count(
|
||||
array_filter(
|
||||
$locations,
|
||||
function( $location ) {
|
||||
function ( $location ) {
|
||||
return $location['enabled']; }
|
||||
)
|
||||
),
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* CartTemplate class.
|
||||
*
|
||||
@@ -33,7 +31,6 @@ class CartTemplate extends AbstractPageTemplate {
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Page: Cart', 'Template name', 'woocommerce' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +40,6 @@ class CartTemplate extends AbstractPageTemplate {
|
||||
*/
|
||||
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' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* CheckoutTemplate class.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -54,13 +54,13 @@ class MiniCartTemplate extends AbstractTemplatePart {
|
||||
* @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 = [
|
||||
$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, [ $mini_cart_template_part_area ] );
|
||||
);
|
||||
return array_merge( $default_area_definitions, array( $mini_cart_template_part_area ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class ProductCatalogTemplate extends AbstractTemplate {
|
||||
*/
|
||||
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' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,4 +58,30 @@ class ProductCatalogTemplate extends AbstractTemplate {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* SingleProductTemplae class.
|
||||
* SingleProductTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@@ -51,7 +51,7 @@ class SingleProductTemplate extends AbstractTemplate {
|
||||
if ( ! is_embed() && is_singular( 'product' ) ) {
|
||||
global $post;
|
||||
|
||||
$valid_slugs = [ self::SLUG ];
|
||||
$valid_slugs = array( self::SLUG );
|
||||
if ( 'product' === $post->post_type && $post->post_name ) {
|
||||
$valid_slugs[] = 'single-product-' . $post->post_name;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class SingleProductTemplate extends AbstractTemplate {
|
||||
*/
|
||||
public function update_single_product_content( $query_result, $query, $template_type ) {
|
||||
$query_result = array_map(
|
||||
function( $template ) {
|
||||
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.
|
||||
@@ -84,7 +84,7 @@ class SingleProductTemplate extends AbstractTemplate {
|
||||
// Add the product class to the body. We should move this to a more appropriate place.
|
||||
add_filter(
|
||||
'body_class',
|
||||
function( $classes ) {
|
||||
function ( $classes ) {
|
||||
return array_merge( $classes, wc_get_product_class() );
|
||||
}
|
||||
);
|
||||
@@ -125,7 +125,7 @@ class SingleProductTemplate extends AbstractTemplate {
|
||||
$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 );
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\Utils;
|
||||
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Blocks\Options;
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\BlockTemplatesRegistry;
|
||||
@@ -292,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;
|
||||
}
|
||||
|
||||
@@ -678,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.
|
||||
*
|
||||
@@ -698,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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user