Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Product Block Editor: Make it compatible with WooCommerce 8.6 #2249

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/php-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ jobs:
uses: woocommerce/grow/get-plugin-releases@actions-v1
with:
slug: woocommerce
# Temporarily use the RC version as version 8.6.0 has not been released yet.
# Will be reverted before merging PR.
includeRC: true

UnitTests:
name: PHP unit tests - PHP ${{ matrix.php }}, WP ${{ matrix.wp-version || 'latest' }}, WC ${{ matrix.wc-versions || 'latest' }}
Expand Down
6 changes: 3 additions & 3 deletions js/src/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ The "derived value" refers to the computation of a value based on another state

References:

- At [ProductPage](https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce-admin/client/products/product-page.tsx#L77-L79) and [ProductVariationPage](https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce-admin/client/products/product-variation-page.tsx#L83-L85) layers, they won't render the actual blocks before the product data is fetched
- The above product data is obtained from [useProductEntityRecord](https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce-admin/client/products/hooks/use-product-entity-record.ts#L19-L23) or [useProductVariationEntityRecord](https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce-admin/client/products/hooks/use-product-variation-entity-record.ts#L16-L22) hook, and both hooks use the `getEntityRecord` selector.
- This extension obtains product data from [useProductEntityProp](https://github.com/woocommerce/woocommerce/blob/8.3.0/packages/js/product-editor/src/hooks/use-product-entity-prop.ts#L24-L33), which uses the `useEntityProp` hook internally.
- At [ProductPage](https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce-admin/client/products/product-page.tsx#L77-L79) and [ProductVariationPage](https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce-admin/client/products/product-variation-page.tsx#L83-L85) layers, they won't render the actual blocks before the product data is fetched
- The above product data is obtained from [useProductEntityRecord](https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce-admin/client/products/hooks/use-product-entity-record.ts#L19-L23) or [useProductVariationEntityRecord](https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce-admin/client/products/hooks/use-product-variation-entity-record.ts#L16-L22) hook, and both hooks use the `getEntityRecord` selector.
- This extension obtains product data from [useProductEntityProp](https://github.com/woocommerce/woocommerce/blob/8.6.0/packages/js/product-editor/src/hooks/use-product-entity-prop.ts#L24-L34), which uses the `useEntityProp` hook internally.
- The [useEntityProp](https://github.com/WordPress/gutenberg/blob/wp/6.0/packages/core-data/src/entity-provider.js#L102-L133) hook also uses the `getEntityRecord` selector.

### Infrastructure adjustments
Expand Down
6 changes: 3 additions & 3 deletions src/Admin/Product/ChannelVisibilityBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ public function register(): void {
return;
}

// https://github.com/woocommerce/woocommerce/blob/8.5.0/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php#L182-L192
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php#L182-L192
add_filter( 'woocommerce_rest_prepare_product_object', [ $this, 'prepare_data' ], 10, 2 );

// https://github.com/woocommerce/woocommerce/blob/8.5.0/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php#L200-L207
// https://github.com/woocommerce/woocommerce/blob/8.5.0/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php#L247-L254
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php#L200-L207
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php#L247-L254
add_action( 'woocommerce_rest_insert_product_object', [ $this, 'update_data' ], 10, 2 );
}

Expand Down
209 changes: 111 additions & 98 deletions src/Admin/ProductBlocksService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterService;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Product\Attributes\AttributeManager;
use Automattic\WooCommerce\GoogleListingsAndAds\Product\ProductSyncer;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\BuiltScriptDependencyArray;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockInterface;
use Automattic\WooCommerce\Admin\Features\ProductBlockEditor\BlockRegistry;
Expand Down Expand Up @@ -53,11 +54,6 @@
*/
protected $channel_visibility_block;

/**
* @var BlockRegistry
*/
protected $block_registry;

/**
* @var string[]
*/
Expand Down Expand Up @@ -90,120 +86,146 @@
* @return bool Whether this service is needed to be registered.
*/
public static function is_needed(): bool {
// compatibility-code "WC >= 8.5" -- The Block Template API used requires at least WooCommerce 8.5
return version_compare( WC_VERSION, '8.5', '>=' ) && PageController::is_admin_page();
// compatibility-code "WC >= 8.6" -- The Block Template API used requires at least WooCommerce 8.6
return version_compare( WC_VERSION, '8.6', '>=' );
}

/**
* Register a service.
*/
public function register(): void {
add_action(
'init',
function () {
$build_path = "{$this->get_root_dir()}/js/build";
$uri = 'js/build/blocks';
$this->set_block_registry( BlockRegistry::get_instance() );
$this->register_custom_blocks( $build_path, $uri, self::CUSTOM_BLOCKS );
}
);
if ( PageController::is_admin_page() ) {
add_action( 'init', [ $this, 'hook_init' ] );
}

// https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php#L16
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/AbstractProductFormTemplate.php#L19
$template_area = 'product-form';

// https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php#L18
// https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductTemplates/ProductVariationTemplate.php#L18
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php#L19
// https://github.com/woocommerce/woocommerce/blob/8.6.0/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/ProductVariationTemplate.php#L19
$block_id = 'general';

add_action(
"woocommerce_block_template_area_{$template_area}_after_add_block_{$block_id}",
function ( BlockInterface $general_group ) {
/** @var Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates\ProductFormTemplateInterface */
$template = $general_group->get_root_template();
[ $this, 'hook_block_template' ]
);
}

/**
* Action hanlder for the 'init' hook.
*/
public function hook_init(): void {
$build_path = "{$this->get_root_dir()}/js/build";
$uri = 'js/build/blocks';

Check warning on line 119 in src/Admin/ProductBlocksService.php

View check run for this annotation

Codecov / codecov/patch

src/Admin/ProductBlocksService.php#L117-L119

Added lines #L117 - L119 were not covered by tests

$is_variation_template = $this->is_variation_template( $general_group );
$this->register_custom_blocks( BlockRegistry::get_instance(), $build_path, $uri, self::CUSTOM_BLOCKS );

Check warning on line 121 in src/Admin/ProductBlocksService.php

View check run for this annotation

Codecov / codecov/patch

src/Admin/ProductBlocksService.php#L121

Added line #L121 was not covered by tests
}

// Please note that the simple and variable product types use the same product block template 'simple-product'.
if ( 'simple-product' !== $template->get_id() && ! $is_variation_template ) {
return;
}
/**
* Action hanlder for the "woocommerce_block_template_area_{$template_area}_after_add_block_{$block_id}" hook.
*
* @param BlockInterface $block The block just added to get its root template to add this extension's group and blocks.
*/
public function hook_block_template( BlockInterface $block ): void {
/** @var Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates\ProductFormTemplateInterface */
$template = $block->get_root_template();

/** @var Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates\GroupInterface */
$group = $template->add_group(
[
'id' => 'google-listings-and-ads-group',
'order' => 100,
'attributes' => [
'title' => __( 'Google Listings & Ads', 'google-listings-and-ads' ),
],
]
);

if ( ! $this->merchant_center->is_setup_complete() ) {
$group->add_block(
[
'id' => 'google-listings-and-ads-product-onboarding-prompt',
'blockName' => 'google-listings-and-ads/product-onboarding-prompt',
'attributes' => [
'startUrl' => $this->get_start_url(),
],
]
);

return;
}
$is_variation_template = $this->is_variation_template( $block );

/** @var SectionInterface */
$channel_visibility_section = $group->add_section(
[
'id' => 'google-listings-and-ads-channel-visibility-section',
'order' => 1,
'attributes' => [
'title' => __( 'Channel visibility', 'google-listings-and-ads' ),
],
]
);

if ( ! $is_variation_template ) {
$this->add_channel_visibility_block( $channel_visibility_section );
}
// Please note that the simple, variable, grouped, and external product types
// use the same product block template 'simple-product'. Their dynamic hidden
// conditions are added below.
if ( 'simple-product' !== $template->get_id() && ! $is_variation_template ) {
return;
}

// Add the hidden condition to the channel visibility section because it only has one block.
$visible_product_types = $this->channel_visibility_block->get_visible_product_types();
$channel_visibility_section->add_hide_condition( $this->get_hide_condition( $visible_product_types ) );

/** @var SectionInterface */
$product_attributes_section = $group->add_section(
[
'id' => 'google-listings-and-ads-product-attributes-section',
'order' => 2,
'attributes' => [
'title' => __( 'Product attributes', 'google-listings-and-ads' ),
],
]
);

$this->add_product_attribute_blocks( $product_attributes_section );
/** @var Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplates\GroupInterface */
$group = $template->add_group(
[
'id' => 'google-listings-and-ads-group',
'order' => 100,
'attributes' => [
'title' => __( 'Google Listings & Ads', 'google-listings-and-ads' ),
],
]
);

$visible_product_types = ProductSyncer::get_supported_product_types();

if ( $is_variation_template ) {
// The property of `editedProduct.type` doesn't exist in the variation product.
// The condition returned from `get_hide_condition` won't work, so it uses 'true' directly.
if ( ! in_array( 'variation', $visible_product_types, true ) ) {
$group->add_hide_condition( 'true' );
}
} else {
$group->add_hide_condition( $this->get_hide_condition( $visible_product_types ) );
}

if ( ! $this->merchant_center->is_setup_complete() ) {
$group->add_block(
[
'id' => 'google-listings-and-ads-product-onboarding-prompt',
'blockName' => 'google-listings-and-ads/product-onboarding-prompt',
'attributes' => [
'startUrl' => $this->get_start_url(),
],
]
);

return;
}

/** @var SectionInterface */
$channel_visibility_section = $group->add_section(
[
'id' => 'google-listings-and-ads-channel-visibility-section',
'order' => 1,
'attributes' => [
'title' => __( 'Channel visibility', 'google-listings-and-ads' ),
],
]
);

if ( ! $is_variation_template ) {
$this->add_channel_visibility_block( $channel_visibility_section );
}

// Add the hidden condition to the channel visibility section because it only has one block.
$visible_product_types = $this->channel_visibility_block->get_visible_product_types();
$channel_visibility_section->add_hide_condition( $this->get_hide_condition( $visible_product_types ) );

/** @var SectionInterface */
$product_attributes_section = $group->add_section(
[
'id' => 'google-listings-and-ads-product-attributes-section',
'order' => 2,
'attributes' => [
'title' => __( 'Product attributes', 'google-listings-and-ads' ),
],
]
);

$this->add_product_attribute_blocks( $product_attributes_section );
}

/**
* Register the custom blocks and their assets.
*
* @param string $build_path The absolute path to the build directory of the assets.
* @param string $uri The script URI of the custom blocks.
* @param string[] $custom_blocks The directory names of each custom block under the build path.
* @param BlockRegistry $block_registry BlockRegistry instance getting from Woo Core for registering custom blocks.
* @param string $build_path The absolute path to the build directory of the assets.
* @param string $uri The script URI of the custom blocks.
* @param string[] $custom_blocks The directory names of each custom block under the build path.
*/
public function register_custom_blocks( string $build_path, string $uri, array $custom_blocks ): void {
public function register_custom_blocks( BlockRegistry $block_registry, string $build_path, string $uri, array $custom_blocks ): void {
foreach ( $custom_blocks as $custom_block ) {
$block_json_file = "{$build_path}/{$custom_block}/block.json";

if ( ! file_exists( $block_json_file ) ) {
continue;
}

$this->block_registry->register_block_type_from_metadata( $block_json_file );
$block_registry->register_block_type_from_metadata( $block_json_file );
}

$assets[] = new AdminScriptWithBuiltDependenciesAsset(
Expand Down Expand Up @@ -264,25 +286,16 @@
} else {
$visible_product_types = AttributesForm::get_attribute_product_types( $attribute_type )['visible'];

// When editing a simple or variable product, its product type on the frontend side can be
// changed dynamically. So, it needs to use the ProductTemplates API `add_hide_condition`
// to conditionally hide attributes.
// When editing a simple, variable, grouped, or external product, its product type on the
// frontend side can be changed dynamically. So, it needs to use the ProductTemplates API
// `add_hide_condition` to conditionally hide attributes.
/** @var BlockInterface */
$block = $section->add_block( $input->get_block_config() );
$block->add_hide_condition( $this->get_hide_condition( $visible_product_types ) );
}
}
}

/**
* Set the block registry for registering custom blocks.
*
* @param BlockRegistry $block_registry
*/
public function set_block_registry( BlockRegistry $block_registry ): void {
$this->block_registry = $block_registry;
}

/**
* Determine if the product block template of the given block is the variation template.
*
Expand Down
Loading
Loading