PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Blocks / BlockTypesController.php
woocommerce / src / Blocks Last commit date
AI 1 year ago AIContent 2 weeks ago Assets 1 month ago BlockTypes 2 weeks ago Domain 2 weeks ago Images 1 year ago Integrations 2 years ago Patterns 2 weeks ago Payments 2 weeks ago Registry 2 years ago SharedStores 3 months ago Shipping 2 weeks ago Templates 3 weeks ago Utils 1 week ago Assets.php 2 years ago AssetsController.php 2 weeks ago BlockPatterns.php 2 weeks ago BlockTemplatesController.php 9 months ago BlockTemplatesRegistry.php 2 weeks ago BlockTypesController.php 2 weeks ago CoreBreadcrumbsCompatibility.php 2 weeks ago DependencyDetection.php 2 weeks ago InboxNotifications.php 2 years ago Installer.php 3 months ago Library.php 2 years ago Options.php 2 years ago Package.php 1 year ago QueryFilters.php 1 month ago TemplateOptions.php 1 year ago
BlockTypesController.php
703 lines
1 <?php
2 declare(strict_types=1);
3
4 namespace Automattic\WooCommerce\Blocks;
5
6 use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
7 use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
8 use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
9 use Automattic\WooCommerce\Blocks\BlockTypes\Cart;
10 use Automattic\WooCommerce\Blocks\BlockTypes\Checkout;
11 use Automattic\WooCommerce\Blocks\BlockTypes\MiniCartContents;
12 use Automattic\WooCommerce\Internal\Features\BlockEditorUnifiedAssets;
13 use Automattic\WooCommerce\Internal\ShopperLists\ShopperListsController;
14
15 /**
16 * BlockTypesController class.
17 *
18 * @since 5.0.0
19 * @internal
20 */
21 final class BlockTypesController {
22
23 /**
24 * Instance of the asset API.
25 *
26 * @var AssetApi
27 */
28 protected $asset_api;
29
30 /**
31 * Instance of the asset data registry.
32 *
33 * @var AssetDataRegistry
34 */
35 protected $asset_data_registry;
36
37 /**
38 * Holds the registered blocks that have WooCommerce blocks as their parents.
39 *
40 * @var array List of registered blocks.
41 */
42 private $registered_blocks_with_woocommerce_parents;
43
44 /**
45 * Whether register_blocks() has run in this request.
46 *
47 * Static because it mirrors the WordPress block-type registry, which is a process-global singleton: once
48 * any controller has registered the blocks they are registered for the whole request, regardless of which
49 * container instance owns the controller. Only tracks the AbstractBlock-based block types registered by
50 * register_blocks(); blocks registered through other paths are not reflected here.
51 *
52 * @var bool
53 */
54 private static $register_blocks_has_run = false;
55
56 /**
57 * Constructor.
58 *
59 * @param AssetApi $asset_api Instance of the asset API.
60 * @param AssetDataRegistry $asset_data_registry Instance of the asset data registry.
61 */
62 public function __construct( AssetApi $asset_api, AssetDataRegistry $asset_data_registry ) {
63 $this->asset_api = $asset_api;
64 $this->asset_data_registry = $asset_data_registry;
65 $this->init();
66 }
67
68 /**
69 * Initialize class features.
70 */
71 protected function init() { // phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingPublic
72 add_action( 'init', array( $this, 'register_blocks' ) );
73 add_action( 'wp_loaded', array( $this, 'register_block_patterns' ) );
74 add_filter( 'block_categories_all', array( $this, 'register_block_categories' ), 10, 2 );
75 add_filter( 'render_block', array( $this, 'add_data_attributes' ), 10, 2 );
76 add_action( 'woocommerce_login_form_end', array( $this, 'redirect_to_field' ) );
77 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_legacy_widgets_with_block_equivalent' ) );
78 add_filter( 'block_type_metadata_settings', array( $this, 'use_single_block_editor_style' ), 10, 2 );
79 add_filter( 'register_block_type_args', array( $this, 'enqueue_block_style_for_classic_themes' ), 10, 2 );
80 add_filter( 'block_core_breadcrumbs_post_type_settings', array( $this, 'set_product_breadcrumbs_preferred_taxonomy' ), 10, 3 );
81 add_filter( 'block_core_breadcrumbs_items', array( $this, 'apply_woocommerce_breadcrumb_filters' ), 10, 1 );
82 }
83
84 /**
85 * Get registered blocks that have WooCommerce blocks as their parents. Adds the value to the
86 * `registered_blocks_with_woocommerce_parents` cache if `init` has been fired.
87 *
88 * @return array Registered blocks with WooCommerce blocks as parents.
89 */
90 public function get_registered_blocks_with_woocommerce_parent() {
91 // If init has run and the cache is already set, return it.
92 if ( did_action( 'init' ) && ! empty( $this->registered_blocks_with_woocommerce_parents ) ) {
93 return $this->registered_blocks_with_woocommerce_parents;
94 }
95
96 $registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
97
98 if ( ! is_array( $registered_blocks ) ) {
99 return array();
100 }
101
102 $this->registered_blocks_with_woocommerce_parents = array_filter(
103 $registered_blocks,
104 function ( $block ) {
105 if ( empty( $block->parent ) ) {
106 return false;
107 }
108 if ( ! is_array( $block->parent ) ) {
109 $block->parent = array( $block->parent );
110 }
111 $woocommerce_blocks = array_filter(
112 $block->parent,
113 function ( $parent_block_name ) {
114 return 'woocommerce' === strtok( $parent_block_name, '/' );
115 }
116 );
117 return ! empty( $woocommerce_blocks );
118 }
119 );
120 return $this->registered_blocks_with_woocommerce_parents;
121 }
122
123 /**
124 * Register blocks, hooking up assets and render functions as needed.
125 */
126 public function register_blocks() {
127 // Set before registering rather than after: it guards against re-entry through the on-demand
128 // registration in Bootstrap, and a registration failure must not be retried on later filter fires.
129 self::$register_blocks_has_run = true;
130 $this->register_block_metadata();
131 $block_types = $this->get_block_types();
132
133 foreach ( $block_types as $block_type ) {
134 $block_type_class = __NAMESPACE__ . '\\BlockTypes\\' . $block_type;
135
136 new $block_type_class( $this->asset_api, $this->asset_data_registry, new IntegrationRegistry() );
137 }
138 }
139
140 /**
141 * Whether register_blocks() has run in this request.
142 *
143 * Covers only the AbstractBlock-based block types that register_blocks() registers — blocks registered
144 * through other paths are not tracked. Used by the on-demand registration on the
145 * woocommerce_short_description filter (see Bootstrap::maybe_register_blocks_from_content) to avoid
146 * re-registering the block set when eager registration already ran on init.
147 *
148 * @since 11.1.0
149 *
150 * @return bool True if register_blocks() has already run.
151 */
152 public function register_blocks_has_run() {
153 return self::$register_blocks_has_run;
154 }
155
156 /**
157 * Register block metadata collections for WooCommerce blocks.
158 *
159 * This method handles the registration of block metadata by using WordPress's block metadata
160 * collection registration system. It includes a temporary workaround for WordPress 6.7's
161 * strict path validation that might fail for sites using symlinked plugins.
162 *
163 * If the registration fails due to path validation, blocks will fall back to regular
164 * registration without affecting functionality.
165 */
166 public function register_block_metadata() {
167 $meta_file_path = WC_ABSPATH . 'assets/client/blocks/blocks-json.php';
168 if ( function_exists( 'wp_register_block_metadata_collection' ) && file_exists( $meta_file_path ) ) {
169 add_filter( 'doing_it_wrong_trigger_error', array( __CLASS__, 'bypass_block_metadata_doing_it_wrong' ), 10, 4 );
170 wp_register_block_metadata_collection(
171 WC_ABSPATH . 'assets/client/blocks/',
172 $meta_file_path
173 );
174 remove_filter( 'doing_it_wrong_trigger_error', array( __CLASS__, 'bypass_block_metadata_doing_it_wrong' ), 10 );
175 }
176 }
177
178 /**
179 * Temporarily bypasses _doing_it_wrong() notices for block metadata collection registration.
180 *
181 * WordPress 6.7 introduced block metadata collections (with strict path validation).
182 * Any sites using symlinks for plugins will fail the validation which causes the metadata
183 * collection to not be registered. However, the blocks will still fall back to the regular
184 * registration and no functionality is affected.
185 * While this validation is being discussed in WordPress Core (#62140),
186 * this method allows registration to proceed by temporarily disabling
187 * the relevant notice.
188 *
189 * @param bool $trigger Whether to trigger the error.
190 * @param string $function The function that was called.
191 * @param string $message A message explaining what was done incorrectly.
192 * @param string $version The version of WordPress where the message was added.
193 * @return bool Whether to trigger the error.
194 */
195 public static function bypass_block_metadata_doing_it_wrong( $trigger, $function, $message, $version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable,Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed,Universal.NamingConventions.NoReservedKeywordParameterNames.functionFound
196 if ( 'WP_Block_Metadata_Registry::register_collection' === $function ) {
197 return false;
198 }
199 return $trigger;
200 }
201
202 /**
203 * Register block patterns
204 */
205 public function register_block_patterns() {
206 register_block_pattern(
207 'woocommerce/order-confirmation-totals-heading',
208 array(
209 'title' => '',
210 'inserter' => false,
211 'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Order details', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
212 )
213 );
214 register_block_pattern(
215 'woocommerce/order-confirmation-downloads-heading',
216 array(
217 'title' => '',
218 'inserter' => false,
219 'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Downloads', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
220 )
221 );
222 register_block_pattern(
223 'woocommerce/order-confirmation-shipping-heading',
224 array(
225 'title' => '',
226 'inserter' => false,
227 'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Shipping address', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
228 )
229 );
230 register_block_pattern(
231 'woocommerce/order-confirmation-billing-heading',
232 array(
233 'title' => '',
234 'inserter' => false,
235 'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Billing address', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
236 )
237 );
238 register_block_pattern(
239 'woocommerce/order-confirmation-additional-fields-heading',
240 array(
241 'title' => '',
242 'inserter' => false,
243 'content' => '<!-- wp:heading {"level":2,"style":{"typography":{"fontSize":"24px"}}} --><h2 class="wp-block-heading" style="font-size:24px">' . esc_html__( 'Additional information', 'woocommerce' ) . '</h2><!-- /wp:heading -->',
244 )
245 );
246 }
247
248 /**
249 * Register block categories
250 *
251 * Used in combination with the `block_categories_all` filter, to append
252 * WooCommerce Blocks related categories to the Gutenberg editor.
253 *
254 * @param array $categories The array of already registered categories.
255 */
256 public function register_block_categories( $categories ) {
257 $woocommerce_block_categories = array(
258 array(
259 'slug' => 'woocommerce',
260 'title' => __( 'WooCommerce', 'woocommerce' ),
261 ),
262 array(
263 'slug' => 'woocommerce-product-elements',
264 'title' => __( 'WooCommerce Product Elements', 'woocommerce' ),
265 ),
266 );
267
268 return array_merge( $categories, $woocommerce_block_categories );
269 }
270
271 /**
272 * Check if a block should have data attributes appended on render. If it's in an allowed namespace, or the block
273 * has explicitly been added to the allowed block list, or if one of the block's parents is in the WooCommerce
274 * namespace it can have data attributes.
275 *
276 * @param string $block_name Name of the block to check.
277 *
278 * @return boolean
279 */
280 public function block_should_have_data_attributes( $block_name ) {
281 $block_namespace = strtok( $block_name ?? '', '/' );
282
283 /**
284 * Filters the list of allowed block namespaces.
285 *
286 * This hook defines which block namespaces should have block name and attribute `data-` attributes appended on render.
287 *
288 * @since 5.9.0
289 *
290 * @param array $allowed_namespaces List of namespaces.
291 */
292 $allowed_namespaces = array_merge( array( 'woocommerce', 'woocommerce-checkout' ), (array) apply_filters( '__experimental_woocommerce_blocks_add_data_attributes_to_namespace', array() ) );
293
294 /**
295 * Filters the list of allowed Block Names
296 *
297 * This hook defines which block names should have block name and attribute data- attributes appended on render.
298 *
299 * @since 5.9.0
300 *
301 * @param array $allowed_namespaces List of namespaces.
302 */
303 $allowed_blocks = (array) apply_filters( '__experimental_woocommerce_blocks_add_data_attributes_to_block', array() );
304
305 $blocks_with_woo_parents = $this->get_registered_blocks_with_woocommerce_parent();
306 $block_has_woo_parent = in_array( $block_name, array_keys( $blocks_with_woo_parents ), true );
307 $in_allowed_namespace_list = in_array( $block_namespace, $allowed_namespaces, true );
308 $in_allowed_block_list = in_array( $block_name, $allowed_blocks, true );
309
310 return $block_has_woo_parent || $in_allowed_block_list || $in_allowed_namespace_list;
311 }
312
313 /**
314 * Add data- attributes to blocks when rendered if the block is under the woocommerce/ namespace.
315 *
316 * @param string $content Block content.
317 * @param array $block Parsed block data.
318 * @return string
319 */
320 public function add_data_attributes( $content, $block ) {
321
322 if ( ! is_string( $content ) || ! $this->block_should_have_data_attributes( $block['blockName'] ) ) {
323 return $content;
324 }
325
326 $attributes = (array) $block['attrs'];
327 $exclude_attributes = array( 'className', 'align' );
328
329 $processor = new \WP_HTML_Tag_Processor( $content );
330
331 if (
332 false === $processor->next_tag() || $processor->is_tag_closer()
333 ) {
334
335 return $content;
336 }
337
338 foreach ( $attributes as $key => $value ) {
339 if ( ! is_string( $key ) || in_array( $key, $exclude_attributes, true ) ) {
340 continue;
341 }
342 if ( is_bool( $value ) ) {
343 $value = $value ? 'true' : 'false';
344 }
345 if ( ! is_scalar( $value ) ) {
346 $value = wp_json_encode( $value );
347 }
348
349 // For output consistency, we convert camelCase to kebab-case and output in lowercase.
350 $key = strtolower( preg_replace( '/(?<!^|\ )[A-Z]/', '-$0', $key ) );
351
352 $processor->set_attribute( "data-{$key}", $value );
353 }
354
355 // Set this last to prevent user-input from overriding it.
356 $processor->set_attribute( 'data-block-name', $block['blockName'] );
357 return $processor->get_updated_html();
358 }
359
360 /**
361 * Adds a redirect field to the login form so blocks can redirect users after login.
362 */
363 public function redirect_to_field() {
364 // phpcs:ignore WordPress.Security.NonceVerification
365 if ( empty( $_GET['redirect_to'] ) ) {
366 return;
367 }
368 echo '<input type="hidden" name="redirect" value="' . esc_attr( esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ) . '" />'; // phpcs:ignore WordPress.Security.NonceVerification
369 }
370
371 /**
372 * Hide legacy widgets with a feature complete block equivalent in the inserter
373 * and prevent them from showing as an option in the Legacy Widget block.
374 *
375 * @param array $widget_types An array of widgets hidden in core.
376 * @return array $widget_types An array including the WooCommerce widgets to hide.
377 */
378 public function hide_legacy_widgets_with_block_equivalent( $widget_types ) {
379 array_push(
380 $widget_types,
381 'woocommerce_product_search',
382 'woocommerce_product_categories',
383 'woocommerce_recent_reviews',
384 'woocommerce_product_tag_cloud',
385 'woocommerce_price_filter',
386 'woocommerce_layered_nav',
387 'woocommerce_layered_nav_filters',
388 'woocommerce_rating_filter'
389 );
390
391 return $widget_types;
392 }
393
394 /**
395 * Delete product transients when a product is deleted.
396 *
397 * @deprecated since 10.6.0
398 * @return void
399 */
400 public function delete_product_transients() {
401 wc_deprecated_function( __METHOD__, '10.6.0' );
402 }
403
404 /**
405 * Get list of block types.
406 *
407 * @return array
408 */
409 protected function get_block_types() {
410 $block_types = array(
411 'ActiveFilters',
412 'AddToCartForm',
413 'AllProducts',
414 'AllReviews',
415 'AttributeFilter',
416 'Breadcrumbs',
417 'CartLink',
418 'CatalogSorting',
419 'CategoryTitle',
420 'CategoryDescription',
421 'ClassicTemplate',
422 'ClassicShortcode',
423 'ComingSoon',
424 'CouponCode',
425 'CustomerAccount',
426 'Dropdown',
427 'EmailContent',
428 'FeaturedCategory',
429 'FeaturedProduct',
430 'FilterWrapper',
431 'HandpickedProducts',
432 'MiniCart',
433 'NextPreviousButtons',
434 'StoreNotices',
435 'PaymentMethodIcons',
436 'PriceFilter',
437 'ProductBestSellers',
438 'ProductButton',
439 'ProductCategories',
440 'ProductCategory',
441 'ProductCollection\Controller',
442 'ProductCollection\NoResults',
443 'ProductFilters',
444 'ProductFilterStatus',
445 'ProductFilterPrice',
446 'ProductFilterPriceSlider',
447 'ProductFilterAttribute',
448 'ProductFilterRating',
449 'ProductFilterActive',
450 'ProductFilterRemovableChips',
451 'ProductFilterClearButton',
452 'ProductFilterCheckboxList',
453 'ProductFilterChips',
454 'ProductFilterTaxonomy',
455 'ProductGallery',
456 'ProductGalleryLargeImage',
457 'ProductGalleryThumbnails',
458 'ProductImage',
459 'ProductImageGallery',
460 'ProductMeta',
461 'ProductNew',
462 'ProductOnSale',
463 'ProductPrice',
464 'ProductTemplate',
465 'ProductQuery',
466 'ProductAverageRating',
467 'ProductRating',
468 'ProductRatingCounter',
469 'ProductRatingStars',
470 'ProductResultsCount',
471 'ProductSaleBadge',
472 'ProductSearch',
473 'ProductSKU',
474 'ProductStockIndicator',
475 'ProductSummary',
476 'ProductTag',
477 'ProductTitle',
478 'ProductTopRated',
479 'ProductsByAttribute',
480 'RatingFilter',
481 'ReviewsByCategory',
482 'ReviewsByProduct',
483 'RelatedProducts',
484 'SingleProduct',
485 'StockFilter',
486 'PageContentWrapper',
487 'OrderConfirmation\Status',
488 'OrderConfirmation\Summary',
489 'OrderConfirmation\Totals',
490 'OrderConfirmation\TotalsWrapper',
491 'OrderConfirmation\Downloads',
492 'OrderConfirmation\DownloadsWrapper',
493 'OrderConfirmation\BillingAddress',
494 'OrderConfirmation\ShippingAddress',
495 'OrderConfirmation\BillingWrapper',
496 'OrderConfirmation\ShippingWrapper',
497 'OrderConfirmation\AdditionalInformation',
498 'OrderConfirmation\AdditionalFieldsWrapper',
499 'OrderConfirmation\AdditionalFields',
500 'OrderConfirmation\CreateAccount',
501 'ProductDetails',
502 'ProductDescription',
503 'ProductSpecifications',
504 // Generic blocks that will be pushed upstream.
505 'Accordion\AccordionGroup',
506 'Accordion\AccordionItem',
507 'Accordion\AccordionPanel',
508 'Accordion\AccordionHeader',
509 // End: generic blocks that will be pushed upstream.
510 'Reviews\ProductReviews',
511 'Reviews\ProductReviewRating',
512 'Reviews\ProductReviewsTitle',
513 'Reviews\ProductReviewForm',
514 'Reviews\ProductReviewDate',
515 'Reviews\ProductReviewContent',
516 'Reviews\ProductReviewAuthorName',
517 'Reviews\ProductReviewsPagination',
518 'Reviews\ProductReviewsPaginationNext',
519 'Reviews\ProductReviewsPaginationPrevious',
520 'Reviews\ProductReviewsPaginationNumbers',
521 'Reviews\ProductReviewTemplate',
522 );
523
524 $block_types = array_merge(
525 $block_types,
526 Cart::get_cart_block_types(),
527 Checkout::get_checkout_block_types(),
528 MiniCartContents::get_mini_cart_block_types()
529 );
530
531 if ( wc_get_container()->get( ShopperListsController::class )->is_enabled( 'saved-for-later' ) ) {
532 $block_types[] = 'SavedForLater';
533 }
534
535 if ( wc_get_container()->get( ShopperListsController::class )->is_enabled( 'wishlist' ) ) {
536 $block_types[] = 'Wishlist';
537 $block_types[] = 'AddToWishlistButton';
538 }
539
540 if ( wp_is_block_theme() ) {
541 $block_types[] = 'AddToCartWithOptions\AddToCartWithOptions';
542 $block_types[] = 'AddToCartWithOptions\QuantitySelector';
543 $block_types[] = 'AddToCartWithOptions\VariationDescription';
544 $block_types[] = 'AddToCartWithOptions\VariationSelector';
545 $block_types[] = 'AddToCartWithOptions\VariationSelectorAttribute';
546 $block_types[] = 'AddToCartWithOptions\VariationSelectorAttributeName';
547 $block_types[] = 'AddToCartWithOptions\GroupedProductSelector';
548 $block_types[] = 'AddToCartWithOptions\GroupedProductItem';
549 $block_types[] = 'AddToCartWithOptions\GroupedProductItemSelector';
550 $block_types[] = 'AddToCartWithOptions\GroupedProductItemLabel';
551 }
552
553 /**
554 * Filters the list of allowed block types.
555 *
556 * @since 9.0.0
557 *
558 * @param array $block_types List of block types.
559 */
560 return apply_filters( 'woocommerce_get_block_types', $block_types );
561 }
562
563 /**
564 * By default, when the classic theme is used, block style is always
565 * enqueued even if the block is not used on the page. We want WooCommerce
566 * store to always performant so we have to manually enqueue the block style
567 * on-demand for classic themes.
568 *
569 * @internal
570 *
571 * @param array $args Block metadata.
572 * @param string $block_name Block name.
573 *
574 * @return array Block metadata.
575 */
576 public function enqueue_block_style_for_classic_themes( $args, $block_name ) {
577
578 // Repeatedly checking the theme is expensive. So statically cache this logic result and remove the filter if not needed.
579 static $should_enqueue_block_style_for_classic_themes = null;
580 if ( null === $should_enqueue_block_style_for_classic_themes ) {
581 $should_enqueue_block_style_for_classic_themes = ! (
582 is_admin() ||
583 wp_is_block_theme() ||
584 ( function_exists( 'wp_should_load_block_assets_on_demand' ) && wp_should_load_block_assets_on_demand() ) ||
585 wp_should_load_separate_core_block_assets()
586 );
587 }
588 if ( ! $should_enqueue_block_style_for_classic_themes ) {
589 remove_filter( 'register_block_type_args', array( $this, 'enqueue_block_style_for_classic_themes' ), 10 );
590
591 return $args;
592 }
593
594 if (
595 false === strpos( $block_name, 'woocommerce/' ) ||
596 ( empty( $args['style_handles'] ) && empty( $args['style'] )
597 )
598 ) {
599 return $args;
600 }
601
602 $style_handlers = $args['style_handles'] ?? $args['style'];
603
604 add_filter(
605 'render_block_' . $block_name,
606 static function ( $html ) use ( $style_handlers ) {
607 array_map( 'wp_enqueue_style', $style_handlers );
608
609 return $html;
610 },
611 10
612 );
613
614 $args['style_handles'] = array();
615 $args['style'] = array();
616
617 return $args;
618 }
619
620 /**
621 * Use one shared editor stylesheet for WooCommerce blocks.
622 *
623 * WordPress loads `style` handles in both the frontend and editor. WooCommerce
624 * keeps those per-block handles for frontend performance, but removes them in
625 * admin so the block editor loads the combined stylesheet only.
626 *
627 * @internal
628 *
629 * @param array $settings Block settings.
630 * @param array $metadata Block metadata.
631 *
632 * @return array Block settings.
633 */
634 public function use_single_block_editor_style( $settings, $metadata ) {
635 if (
636 ! BlockEditorUnifiedAssets::is_enabled() ||
637 ! is_admin() ||
638 ! $this->is_woocommerce_block_metadata( $metadata ) ) {
639 return $settings;
640 }
641
642 $settings['style_handles'] = array();
643 $settings['style'] = array();
644 $settings['editor_style_handles'] = array( 'wc-block-library-style' );
645 $settings['editor_style'] = array( 'wc-block-library-style' );
646
647 return $settings;
648 }
649
650 /**
651 * Check whether block metadata belongs to a block bundled with WooCommerce.
652 *
653 * @param array $metadata Block metadata.
654 *
655 * @return bool Whether the metadata file is in the WooCommerce blocks directory.
656 */
657 private function is_woocommerce_block_metadata( $metadata ) {
658 static $blocks_path = null;
659
660 if ( null === $blocks_path ) {
661 $resolved_path = realpath( WC_ABSPATH . 'assets/client/blocks' );
662 $blocks_path = false === $resolved_path
663 ? ''
664 : trailingslashit( wp_normalize_path( $resolved_path ) );
665 }
666
667 if ( '' === $blocks_path || empty( $metadata['file'] ) ) {
668 return false;
669 }
670
671 return str_starts_with(
672 wp_normalize_path( $metadata['file'] ),
673 $blocks_path
674 );
675 }
676
677 /**
678 * Set the preferred taxonomy and term for the breadcrumbs block on the product post type.
679 *
680 * @internal
681 *
682 * @param array $settings The settings for the breadcrumbs block.
683 * @param string $post_type The post type.
684 * @param int $post_id The current post ID.
685 * @return array The settings for the breadcrumbs block.
686 */
687 public function set_product_breadcrumbs_preferred_taxonomy( $settings, $post_type, $post_id = 0 ) {
688 return Package::container()->get( CoreBreadcrumbsCompatibility::class )->set_product_breadcrumbs_preferred_taxonomy( $settings, $post_type, $post_id );
689 }
690
691 /**
692 * Apply WooCommerce compatibility behavior to Core breadcrumb items.
693 *
694 * @internal
695 *
696 * @param array $items Array of breadcrumb items from Core.
697 * @return array Modified breadcrumb items.
698 */
699 public function apply_woocommerce_breadcrumb_filters( $items ) {
700 return Package::container()->get( CoreBreadcrumbsCompatibility::class )->apply_woocommerce_breadcrumb_filters( $items );
701 }
702 }
703