hooked_block_placements ) || empty( $this->hooked_block_placements ) ) { return $hooked_blocks; } // Cache the block hooks version. static $block_hooks_version = null; if ( defined( 'WP_RUN_CORE_TESTS' ) || is_null( $block_hooks_version ) ) { $block_hooks_version = get_option( 'woocommerce_hooked_blocks_version' ); } // If block hooks are disabled or the version is not set, return early. if ( 'no' === $block_hooks_version || false === $block_hooks_version ) { return $hooked_blocks; } // Valid placements are those that have no version specified, // or have a version that is less than or equal to version specified in the woocommerce_hooked_blocks_version option. $valid_placements = array_filter( $this->hooked_block_placements, function ( $placement ) use ( $block_hooks_version ) { $placement_version = isset( $placement['version'] ) ? $placement['version'] : null; return is_null( $placement_version ) || ! is_null( $placement_version ) && version_compare( $block_hooks_version, $placement_version, '>=' ); } ); if ( $context && ! empty( $valid_placements ) ) { foreach ( $valid_placements as $placement ) { if ( $placement['position'] === $position && $placement['anchor'] === $anchor_block ) { // If an area has been specified for this placement. if ( isset( $placement['area'] ) && ! $this->has_block_in_content( $context ) && $this->is_target_area( $context, $placement['area'] ) ) { $hooked_blocks[] = $this->namespace . '/' . $this->block_name; } // If no area has been specified for this placement just insert the block. // This is likely to be the case when we're inserting into the navigation block // where we don't have a specific area to target. if ( ! isset( $placement['area'] ) ) { $hooked_blocks[] = $this->namespace . '/' . $this->block_name; } // If a callback has been specified for this placement, call it. This allows for custom block-specific logic to be run. $callback = isset( $placement['callback'] ) && is_callable( array( $this, $placement['callback'] ) ) ? array( $this, $placement['callback'] ) : null; if ( null !== $callback ) { $modified_hooked_blocks = $callback( $hooked_blocks, $position, $anchor_block, $context ); if ( is_array( $modified_hooked_blocks ) ) { $hooked_blocks = $modified_hooked_blocks; } } } } } return $hooked_blocks; } /** * Checks if the provided context contains a the block already. * * @param array|\WP_Block_Template $context Where the block is embedded. * @return boolean */ protected function has_block_in_content( $context ) { $content = $this->get_context_content( $context ); return strpos( $content, 'wp:' . $this->namespace . '/' . $this->block_name ) !== false; } /** * Given a provided context, returns the content of the context. * * @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded. * @since 8.5.0 * @return string */ protected function get_context_content( $context ) { $content = is_array( $context ) && isset( $context['content'] ) ? $context['content'] : ''; $content = '' === $content && $context instanceof \WP_Block_Template ? $context->content : $content; $content = '' === $content && $context instanceof \WP_Post ? $context->post_content : $content; return $content; } /** * Given a provided context, returns whether the context refers to header content. * * @param array|\WP_Post|\WP_Block_Template $context Where the block is embedded. * @param string $area The area to check against before inserting. * @since 8.5.0 * @return boolean */ protected function is_template_part_or_pattern( $context, $area ) { $is_pattern = is_array( $context ) && ( ( isset( $context['blockTypes'] ) && in_array( 'core/template-part/' . $area, $context['blockTypes'], true ) ) || ( isset( $context['categories'] ) && in_array( $area, $context['categories'], true ) ) ); $is_template_part = $context instanceof \WP_Block_Template && $area === $context->area; return ( $is_pattern || $is_template_part ); } /** * Given a provided context, returns whether the context refers to the target area and isn't marked as excluded. * * @param array|\WP_Post|\WP_Block_Template $context the context to check. * @param string $area The area to check against before inserting. * @since 8.5.0 * @return boolean */ protected function is_target_area( $context, $area ) { if ( $this->is_template_part_or_pattern( $context, $area ) && ! $this->pattern_is_excluded( $context ) ) { return true; } return false; } /** * Returns whether the pattern is excluded or not * * @since 8.5.0 * * @param array|\WP_Block_Template $context Where the block is embedded. * @return boolean */ protected function pattern_is_excluded( $context ) { /** * A list of pattern slugs to exclude from auto-insert (useful when there are patterns that have a very specific location for the block) * Note: The patterns that are currently excluded are the ones that don't work well with the mini-cart block or customer-account block. * * @since 8.5.0 */ $pattern_exclude_list = apply_filters( 'woocommerce_hooked_blocks_pattern_exclude_list', array_unique( array_merge( isset( $this->hooked_block_excluded_patterns ) ? $this->hooked_block_excluded_patterns : array(), array( 'twentytwentytwo/header-centered-logo', 'twentytwentytwo/header-stacked' ) ) ) ); $pattern_slug = is_array( $context ) && isset( $context['slug'] ) ? $context['slug'] : ''; if ( ! $pattern_slug ) { /** * Woo patterns have a slug property in $context, but core/theme patterns dont. * In that case, we fallback to the name property, as they're the same. */ $pattern_slug = is_array( $context ) && isset( $context['name'] ) ? $context['name'] : ''; } return in_array( $pattern_slug, $pattern_exclude_list, true ); } }