Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/Utils.tar
Назад
ProductGalleryUtils.php 0000777 00000013252 15252146202 0011247 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; /** * Utility methods used for the Product Gallery block. * {@internal This class and its methods are not intended for public use.} */ class ProductGalleryUtils { /** * Get all image IDs for the product. * * @param \WC_Product $product The product object. * @return array An array of image IDs. */ public static function get_all_image_ids( $product ) { if ( ! $product instanceof \WC_Product ) { wc_doing_it_wrong( __FUNCTION__, __( 'Invalid product object.', 'woocommerce' ), '9.8.0' ); return array(); } $gallery_image_ids = self::get_product_gallery_image_ids( $product ); $product_variation_image_ids = self::get_product_variation_image_ids( $product ); $all_image_ids = array_values( array_map( 'intval', array_unique( array_merge( $gallery_image_ids, $product_variation_image_ids ) ) ) ); if ( empty( $all_image_ids ) ) { return array(); } return $all_image_ids; } /** * Get the product gallery image data. * * @param \WC_Product $product The product object to retrieve the gallery images for. * @param string $size The size of the image to retrieve. * @return array An array of image data for the product gallery. */ public static function get_product_gallery_image_data( $product, $size ) { $all_image_ids = self::get_all_image_ids( $product ); return self::get_image_src_data( $all_image_ids, $size, $product->get_title() ); } /** * Get the product gallery image count. * * @param \WC_Product $product The product object to retrieve the gallery images for. * @return int The number of images in the product gallery. */ public static function get_product_gallery_image_count( $product ) { $all_image_ids = self::get_all_image_ids( $product ); return count( $all_image_ids ); } /** * Get the image source data. * * @param array $image_ids The image IDs to retrieve the source data for. * @param string $size The size of the image to retrieve. * @param string $product_title The title of the product used for alt fallback. * @return array An array of image source data. */ public static function get_image_src_data( $image_ids, $size, $product_title = '' ) { $image_src_data = array(); foreach ( $image_ids as $index => $image_id ) { if ( 0 === $image_id ) { // Handle placeholder image. $image_src_data[] = array( 'id' => 0, 'src' => wc_placeholder_img_src(), 'srcset' => '', 'sizes' => '', 'alt' => '', ); continue; } // Get the image source. $full_src = wp_get_attachment_image_src( $image_id, $size ); // Get srcset and sizes. $srcset = wp_get_attachment_image_srcset( $image_id, $size ); $sizes = wp_get_attachment_image_sizes( $image_id, $size ); $alt = get_post_meta( $image_id, '_wp_attachment_image_alt', true ); $image_src_data[] = array( 'id' => $image_id, 'src' => $full_src ? $full_src[0] : '', 'srcset' => $srcset ? $srcset : '', 'sizes' => $sizes ? $sizes : '', 'alt' => $alt ? $alt : sprintf( /* translators: 1: Product title 2: Image number */ __( '%1$s - Image %2$d', 'woocommerce' ), $product_title, $index + 1 ), ); } return $image_src_data; } /** * Get the product variation image data. * * @param \WC_Product $product The product object to retrieve the variation images for. * @return array An array of image data for the product variation images. */ public static function get_product_variation_image_ids( $product ) { $variation_image_ids = array(); if ( ! $product instanceof \WC_Product ) { wc_doing_it_wrong( __FUNCTION__, __( 'Invalid product object.', 'woocommerce' ), '9.8.0' ); return $variation_image_ids; } try { if ( $product->is_type( 'variable' ) ) { $variations = $product->get_children(); foreach ( $variations as $variation_id ) { $variation = wc_get_product( $variation_id ); if ( $variation ) { $variation_image_id = $variation->get_image_id(); if ( ! empty( $variation_image_id ) && ! in_array( strval( $variation_image_id ), $variation_image_ids, true ) ) { $variation_image_ids[] = strval( $variation_image_id ); } } } } } catch ( \Exception $e ) { // Log the error but continue execution. error_log( 'Error getting product variation image IDs: ' . $e->getMessage() ); } return $variation_image_ids; } /** * Get the product gallery image IDs. * * @param \WC_Product $product The product object to retrieve the gallery images for. * @return array An array of unique image IDs for the product gallery. */ public static function get_product_gallery_image_ids( $product ) { $product_image_ids = array(); // Main product featured image. $featured_image_id = $product->get_image_id(); if ( $featured_image_id ) { $product_image_ids[] = $featured_image_id; } // All other product gallery images. $product_gallery_image_ids = $product->get_gallery_image_ids(); if ( ! empty( $product_gallery_image_ids ) ) { // We don't want to show the same image twice, so we have to remove the featured image from the gallery if it's there. $product_image_ids = array_unique( array_merge( $product_image_ids, $product_gallery_image_ids ) ); } // If the Product image is not set and there are no gallery images, we need to set it to a placeholder image. if ( ! $featured_image_id && empty( $product_gallery_image_ids ) ) { $product_image_ids[] = '0'; } foreach ( $product_image_ids as $key => $image_id ) { $product_image_ids[ $key ] = strval( $image_id ); } // Reindex array. $product_image_ids = array_values( $product_image_ids ); return $product_image_ids; } } BlockHooksTrait.php 0000777 00000015623 15252146202 0010334 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; /** * BlockHooksTrait * * Shared functionality for using the Block Hooks API with WooCommerce Blocks. */ trait BlockHooksTrait { /** * Callback for `hooked_block_types` to auto-inject the mini-cart block into headers after navigation. * * @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. * @since 8.5.0 * @return array An array of block slugs hooked into a given context. */ public function register_hooked_block( $hooked_blocks, $position, $anchor_block, $context ) { // If the block has no hook placements, return early. if ( ! isset( $this->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 ); } } Utils.php 0000777 00000002330 15252146202 0006361 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; /** * Utils class */ class Utils { /** * Compare the current WordPress version with a given version. It's a wrapper around `version-compare` * that additionally takes into account the suffix (like `-RC1`). * For example: version 6.3 is considered lower than 6.3-RC2, so you can do * wp_version_compare( '6.3', '>=' ) and that will return true for 6.3-RC2. * * @param string $version The version to compare against. * @param string|null $operator Optional. The comparison operator. Defaults to null. * @return bool|int Returns true if the current WordPress version satisfies the comparison, false otherwise. */ public static function wp_version_compare( $version, $operator = null ) { $current_wp_version = get_bloginfo( 'version' ); if ( preg_match( '/^([0-9]+\.[0-9]+)/', $current_wp_version, $matches ) ) { $current_wp_version = (float) $matches[1]; } // Replace non-alphanumeric characters with a dot. $current_wp_version = preg_replace( '/[^0-9a-zA-Z\.]+/i', '.', $current_wp_version ); $version = preg_replace( '/[^0-9a-zA-Z\.]+/i', '.', $version ); return version_compare( $current_wp_version, $version, $operator ); } } BlocksSharedState.php 0000777 00000012326 15252146202 0010634 0 ustar 00 <?php declare(strict_types=1); namespace Automattic\WooCommerce\Blocks\Utils; use InvalidArgumentException; use Automattic\WooCommerce\Blocks\Package; use Automattic\WooCommerce\Blocks\Domain\Services\Hydration; /** * Manages the registration of interactivity config and state that is commonly shared by WooCommerce blocks. * Initialization only happens on the first call to load_store_config. * * This is a private API and may change in future versions. */ class BlocksSharedState { /** * The consent statement for using private APIs of this class. * * @var string */ private static string $consent_statement = 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WooCommerce'; /** * The namespace for the config. * * @var string */ private static string $settings_namespace = 'woocommerce'; /** * Whether the core config has been registered. * * @var bool */ private static bool $core_config_registered = false; /** * Cart state. * * @var array|null */ private static ?array $blocks_shared_cart_state = null; /** * Prevent caching on certain pages. * * @return void */ private static function prevent_cache(): void { \WC_Cache_Helper::set_nocache_constants(); nocache_headers(); } /** * Check that the consent statement was passed. * * @param string $consent_statement The consent statement string. * @return true * @throws InvalidArgumentException If the statement does not match. */ private static function check_consent( string $consent_statement ): bool { if ( $consent_statement !== self::$consent_statement ) { throw new InvalidArgumentException( 'This method cannot be called without consenting the API may change.' ); } return true; } /** * Load store config (currency, locale, core data) into interactivity config. * * @param string $consent_statement The consent statement string. * @return void * @throws InvalidArgumentException If consent statement doesn't match. */ public static function load_store_config( string $consent_statement ): void { self::check_consent( $consent_statement ); if ( self::$core_config_registered ) { return; } self::$core_config_registered = true; wp_interactivity_config( self::$settings_namespace, self::get_currency_data() ); wp_interactivity_config( self::$settings_namespace, self::get_locale_data() ); wp_interactivity_config( self::$settings_namespace, self::get_core_data() ); } /** * Load cart state into interactivity state. * * @param string $consent_statement The consent statement string. * @return void * @throws InvalidArgumentException If consent statement doesn't match. */ public static function load_cart_state( string $consent_statement ): void { self::check_consent( $consent_statement ); if ( null === self::$blocks_shared_cart_state ) { $cart_exists = isset( WC()->cart ); $cart_has_contents = $cart_exists && ! WC()->cart->is_empty(); if ( $cart_exists ) { $cart_response = Package::container()->get( Hydration::class )->get_rest_api_response_data( '/wc/store/v1/cart' ); self::$blocks_shared_cart_state = $cart_response['body'] ?? array(); } else { self::$blocks_shared_cart_state = array(); } if ( $cart_has_contents ) { self::prevent_cache(); } wp_interactivity_state( 'woocommerce', array( 'cart' => self::$blocks_shared_cart_state, 'nonce' => wp_create_nonce( 'wc_store_api' ), 'noticeId' => '', 'restUrl' => get_rest_url(), ) ); } } /** * Get core data to include in settings. * * @return array */ private static function get_core_data(): array { return array( 'isBlockTheme' => wp_is_block_theme(), ); } /** * Get currency data to include in settings. * * @return array */ private static function get_currency_data(): array { $currency = get_woocommerce_currency(); return array( 'currency' => array( 'code' => $currency, 'precision' => wc_get_price_decimals(), 'symbol' => html_entity_decode( get_woocommerce_currency_symbol( $currency ) ), 'symbolPosition' => get_option( 'woocommerce_currency_pos' ), 'decimalSeparator' => wc_get_price_decimal_separator(), 'thousandSeparator' => wc_get_price_thousand_separator(), 'priceFormat' => html_entity_decode( get_woocommerce_price_format() ), ), ); } /** * Get locale data to include in settings. * * @return array */ private static function get_locale_data(): array { global $wp_locale; return array( 'locale' => array( 'siteLocale' => get_locale(), 'userLocale' => get_user_locale(), 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), ), ); } /** * Load placeholder image into interactivity config. * * @param string $consent_statement The consent statement string. * @return void * @throws InvalidArgumentException If consent statement doesn't match. */ public static function load_placeholder_image( string $consent_statement ): void { self::check_consent( $consent_statement ); wp_interactivity_config( self::$settings_namespace, array( 'placeholderImgSrc' => wc_placeholder_img_src() ) ); } } BlockTemplateUtils.php 0000777 00000070422 15252146202 0011037 0 ustar 00 <?php declare( strict_types=1 ); namespace Automattic\WooCommerce\Blocks\Utils; use WP_Block_Patterns_Registry; use Automattic\WooCommerce\Admin\Features\Features; use Automattic\WooCommerce\Blocks\Options; use Automattic\WooCommerce\Blocks\Package; use Automattic\WooCommerce\Blocks\BlockTemplatesRegistry; use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate; /** * Utility methods used for serving block templates from WooCommerce Blocks. * {@internal This class and its methods should only be used within the BlockTemplateController.php and is not intended for public use.} */ class BlockTemplateUtils { /** * Directory names for block templates * * Directory names conventions for block templates have changed with Gutenberg 12.1.0, * however, for backwards-compatibility, we also keep the older conventions, prefixed * with `DEPRECATED_`. * * @var array { * @var string DEPRECATED_TEMPLATES Old directory name of the block templates directory. * @var string DEPRECATED_TEMPLATE_PARTS Old directory name of the block template parts directory. * @var string TEMPLATES_DIR_NAME Directory name of the block templates directory. * @var string TEMPLATE_PARTS_DIR_NAME Directory name of the block template parts directory. * } */ const DIRECTORY_NAMES = array( 'DEPRECATED_TEMPLATES' => 'block-templates', 'DEPRECATED_TEMPLATE_PARTS' => 'block-template-parts', 'TEMPLATES' => 'templates', 'TEMPLATE_PARTS' => 'parts', ); const TEMPLATES_ROOT_DIR = 'templates'; /** * WooCommerce plugin slug * * This is used to save templates to the DB which are stored against this value in the wp_terms table. * * @var string */ const PLUGIN_SLUG = 'woocommerce/woocommerce'; /** * Deprecated WooCommerce plugin slug * * For supporting users who have customized templates under the incorrect plugin slug during the first release. * More context found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423. * * @var string */ const DEPRECATED_PLUGIN_SLUG = 'woocommerce'; /** * Returns the template matching the slug * * @param string $template_slug Slug of the template to retrieve. * * @return AbstractTemplate|AbstractTemplatePart|null */ public static function get_template( $template_slug ) { $block_templates_registry = Package::container()->get( BlockTemplatesRegistry::class ); return $block_templates_registry->get_template( $template_slug ); } /** * Returns an array containing the references of * the passed blocks and their inner blocks. * * @param array $blocks array of blocks. * * @return array block references to the passed blocks and their inner blocks. */ public static function flatten_blocks( &$blocks ) { $all_blocks = array(); $queue = array(); foreach ( $blocks as &$block ) { $queue[] = &$block; } $queue_count = count( $queue ); while ( $queue_count > 0 ) { $block = &$queue[0]; array_shift( $queue ); $all_blocks[] = &$block; if ( ! empty( $block['innerBlocks'] ) ) { foreach ( $block['innerBlocks'] as &$inner_block ) { $queue[] = &$inner_block; } } $queue_count = count( $queue ); } return $all_blocks; } /** * Parses wp_template content and injects the current theme's * stylesheet as a theme attribute into each wp_template_part * * @param string $template_content serialized wp_template content. * * @return string Updated wp_template content. */ public static function inject_theme_attribute_in_content( $template_content ) { $has_updated_content = false; $new_content = ''; $template_blocks = parse_blocks( $template_content ); $blocks = self::flatten_blocks( $template_blocks ); foreach ( $blocks as &$block ) { if ( 'core/template-part' === $block['blockName'] && ! isset( $block['attrs']['theme'] ) ) { $block['attrs']['theme'] = wp_get_theme()->get_stylesheet(); $has_updated_content = true; } } if ( $has_updated_content ) { foreach ( $template_blocks as &$block ) { $new_content .= serialize_block( $block ); } return $new_content; } return $template_content; } /** * Build a unified template object based a post Object. * Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes. * * @param \WP_Post $post Template post. * * @return \WP_Block_Template|\WP_Error Template. */ public static function build_template_result_from_post( $post ) { $terms = get_the_terms( $post, 'wp_theme' ); if ( is_wp_error( $terms ) ) { return $terms; } if ( ! $terms ) { return new \WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'woocommerce' ) ); } $theme = $terms[0]->name; $has_theme_file = true; $template = new \WP_Block_Template(); $template->wp_id = $post->ID; $template->id = $theme . '//' . $post->post_name; $template->theme = $theme; $template->content = $post->post_content; $template->slug = $post->post_name; $template->source = 'custom'; $template->type = $post->post_type; $template->description = $post->post_excerpt; $template->title = $post->post_title; $template->status = $post->post_status; $template->has_theme_file = $has_theme_file; $template->is_custom = false; $template->post_types = array(); // Don't appear in any Edit Post template selector dropdown. if ( 'wp_template_part' === $post->post_type ) { $type_terms = get_the_terms( $post, 'wp_template_part_area' ); if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) { $template->area = $type_terms[0]->name; } } // We are checking 'woocommerce' to maintain classic templates which are saved to the DB, // prior to updating to use the correct slug. // More information found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423. if ( self::PLUGIN_SLUG === $theme || self::DEPRECATED_PLUGIN_SLUG === strtolower( $theme ) ) { $template->origin = 'plugin'; } /* * Run the block hooks algorithm introduced in WP 6.4 on the template content. */ if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) { $hooked_blocks = get_hooked_blocks(); if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template ); $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template ); $blocks = parse_blocks( $template->content ); $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); } } return $template; } /** * Build a unified template object based on a theme file. * * @internal Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes. * * @param array|object $template_file Theme file. * @param string $template_type wp_template or wp_template_part. * * @return \WP_Block_Template Template. */ public static function build_template_result_from_file( $template_file, $template_type ) { $template_file = (object) $template_file; // If the theme has an archive-products.html template but does not have product taxonomy templates // then we will load in the archive-product.html template from the theme to use for product taxonomies on the frontend. $template_is_from_theme = 'theme' === $template_file->source; $theme_name = wp_get_theme()->get( 'TextDomain' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $template_content = file_get_contents( $template_file->path ); $template = new \WP_Block_Template(); $template->id = $template_is_from_theme ? $theme_name . '//' . $template_file->slug : self::PLUGIN_SLUG . '//' . $template_file->slug; $template->theme = $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG; $template->content = self::inject_theme_attribute_in_content( $template_content ); // Remove the term description block from the archive-product template // as the Product Catalog/Shop page doesn't have a description. if ( ProductCatalogTemplate::SLUG === $template_file->slug ) { $template->content = str_replace( '<!-- wp:term-description {"align":"wide"} /-->', '', $template->content ); } // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909. $template->source = $template_file->source ? $template_file->source : 'plugin'; $template->slug = $template_file->slug; $template->type = $template_type; $template->title = ! empty( $template_file->title ) ? $template_file->title : self::get_block_template_title( $template_file->slug ); $template->description = ! empty( $template_file->description ) ? $template_file->description : self::get_block_template_description( $template_file->slug ); $template->status = 'publish'; $template->has_theme_file = true; $template->origin = $template_file->source; $template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are. $template->post_types = array(); // Don't appear in any Edit Post template selector dropdown. $template->area = self::get_block_template_area( $template->slug, $template_type ); /* * Run the block hooks algorithm introduced in WP 6.4 on the template content. */ if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) { $before_block_visitor = '_inject_theme_attribute_in_template_part_block'; $after_block_visitor = null; $hooked_blocks = get_hooked_blocks(); if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) { $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template ); $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template ); } $blocks = parse_blocks( $template->content ); $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor ); } return $template; } /** * Build a new template object so that we can make Woo Blocks default templates available in the current theme should they not have any. * * @param string $template_file Block template file path. * @param string $template_type wp_template or wp_template_part. * @param string $template_slug Block template slug e.g. single-product. * @param bool $template_is_from_theme If the block template file is being loaded from the current theme instead of Woo Blocks. * * @return object Block template object. */ public static function create_new_block_template_object( $template_file, $template_type, $template_slug, $template_is_from_theme = false ) { $theme_name = wp_get_theme()->get( 'TextDomain' ); $new_template_item = array( 'slug' => $template_slug, 'id' => $template_is_from_theme ? $theme_name . '//' . $template_slug : self::PLUGIN_SLUG . '//' . $template_slug, 'path' => $template_file, 'type' => $template_type, 'theme' => $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG, // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909. 'source' => $template_is_from_theme ? 'theme' : 'plugin', 'title' => self::get_block_template_title( $template_slug ), 'description' => self::get_block_template_description( $template_slug ), 'post_types' => array(), // Don't appear in any Edit Post template selector dropdown. ); return (object) $new_template_item; } /** * Finds all nested template part file paths in a theme's directory. * * @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( $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_brand.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', 'coming-soon-social-links.html', 'mini-cart.html', 'simple-product-add-to-cart-with-options.html', 'external-product-add-to-cart-with-options.html', 'variable-product-add-to-cart-with-options.html', 'grouped-product-add-to-cart-with-options.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; } /** * Gets the directory where templates of a specific template type can be found. * * @param string $template_type wp_template or wp_template_part. * * @return string */ public static function get_templates_directory( $template_type = 'wp_template' ) { $root_path = dirname( __DIR__, 3 ) . '/' . self::TEMPLATES_ROOT_DIR . DIRECTORY_SEPARATOR; $templates_directory = $root_path . self::DIRECTORY_NAMES['TEMPLATES']; $template_parts_directory = $root_path . self::DIRECTORY_NAMES['TEMPLATE_PARTS']; if ( 'wp_template_part' === $template_type ) { return $template_parts_directory; } if ( self::should_use_blockified_product_grid_templates() ) { return $templates_directory . '/blockified'; } return $templates_directory; } /** * Returns template title. * * @param string $template_slug The template slug (e.g. single-product). * @return string Human friendly title. */ public static function get_block_template_title( $template_slug ) { $registered_template = self::get_template( $template_slug ); if ( isset( $registered_template ) ) { return $registered_template->get_template_title(); } else { // Human friendly title converted from the slug. return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) ); } } /** * Returns template description. * * @param string $template_slug The template slug (e.g. single-product). * @return string Template description. */ public static function get_block_template_description( $template_slug ) { $registered_template = self::get_template( $template_slug ); if ( isset( $registered_template ) ) { return $registered_template->get_template_description(); } return ''; } /** * Returns area for template parts. * * @param string $template_slug The template part slug (e.g. mini-cart). * @param string $template_type Either `wp_template` or `wp_template_part`. * @return string Template part area. */ public static function get_block_template_area( $template_slug, $template_type ) { if ( 'wp_template_part' === $template_type ) { $registered_template = self::get_template( $template_slug ); if ( $registered_template && property_exists( $registered_template, 'template_area' ) ) { return $registered_template->template_area; } } return 'uncategorized'; } /** * Converts template paths into a slug * * @param string $path The template's path. * @return string slug */ public static function generate_template_slug_from_path( $path ) { $template_extension = '.html'; return basename( $path, $template_extension ); } /** * Gets the first matching template part within themes directories * * Since [Gutenberg 12.1.0](https://github.com/WordPress/gutenberg/releases/tag/v12.1.0), the conventions for * block templates and parts directory has changed from `block-templates` and `block-templates-parts` * to `templates` and `parts` respectively. * * This function traverses all possible combinations of directory paths where a template or part * could be located and returns the first one which is readable, prioritizing the new convention * over the deprecated one, but maintaining that one for backwards compatibility. * * @param string $template_slug The slug of the template (i.e. without the file extension). * @param string $template_type Either `wp_template` or `wp_template_part`. * * @return string|null The matched path or `null` if no match was found. */ public static function get_theme_template_path( $template_slug, $template_type = 'wp_template' ) { $template_filename = $template_slug . '.html'; $possible_templates_dir = 'wp_template' === $template_type ? array( self::DIRECTORY_NAMES['TEMPLATES'], self::DIRECTORY_NAMES['DEPRECATED_TEMPLATES'], ) : array( self::DIRECTORY_NAMES['TEMPLATE_PARTS'], self::DIRECTORY_NAMES['DEPRECATED_TEMPLATE_PARTS'], ); // Combine the possible root directory names with either the template directory // or the stylesheet directory for child themes. $possible_paths = array_reduce( $possible_templates_dir, function ( $carry, $item ) use ( $template_filename ) { $filepath = DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $template_filename; $carry[] = get_stylesheet_directory() . $filepath; $carry[] = get_template_directory() . $filepath; return $carry; }, array() ); // Return the first matching. foreach ( $possible_paths as $path ) { if ( is_readable( $path ) ) { return $path; } } return null; } /** * Check if the theme has a template. So we know if to load our own in or not. * * @param string $template_name name of the template file without .html extension e.g. 'single-product'. * @return boolean */ public static function theme_has_template( $template_name ) { return (bool) self::get_theme_template_path( $template_name, 'wp_template' ); } /** * Check if the theme has a template. So we know if to load our own in or not. * * @param string $template_name name of the template file without .html extension e.g. 'single-product'. * @return boolean */ public static function theme_has_template_part( $template_name ) { return (bool) self::get_theme_template_path( $template_name, 'wp_template_part' ); } /** * Checks to see if they are using a compatible version of WP, or if not they have a compatible version of the Gutenberg plugin installed. * * @param string $template_type Optional. Template type: `wp_template` or `wp_template_part`. * Default `wp_template`. * @return boolean */ public static function supports_block_templates( $template_type = 'wp_template' ) { if ( 'wp_template_part' === $template_type && ( wp_is_block_theme() || current_theme_supports( 'block-template-parts' ) ) ) { return true; } elseif ( 'wp_template' === $template_type && wp_is_block_theme() ) { return true; } return false; } /** * Gets the `archive-product` fallback template stored on the db for a given slug. * * @param string $template_slug Slug to check for fallbacks. * @param array $db_templates Templates that have already been found on the db. * @return boolean|object */ public static function get_fallback_template_from_db( $template_slug, $db_templates ) { $registered_template = self::get_template( $template_slug ); if ( $registered_template && isset( $registered_template->fallback_template ) ) { foreach ( $db_templates as $template ) { if ( $registered_template->fallback_template === $template->slug ) { return $template; } } } return false; } /** * Removes templates from the theme or WooCommerce which have the same slug * as template saved in the database with the `woocommerce/woocommerce` theme. * Before WC migrated to the Template Registration API from WordPress, templates * were saved in the database with the `woocommerce/woocommerce` theme instead * of the theme's slug. * * @param \WP_Block_Template[]|\stdClass[] $templates List of templates to run the filter on. * * @return array List of templates with duplicates removed. The customised alternative is preferred over the theme default. */ public static function remove_templates_with_custom_alternative( $templates ) { // Get the slugs of all templates that have been customised and saved in the database. $customised_template_slugs = array_column( array_filter( $templates, function ( $template ) { // This template has been customised and saved as a post. return 'custom' === $template->source && ( self::PLUGIN_SLUG === $template->theme || self::DEPRECATED_PLUGIN_SLUG === $template->theme ); } ), 'slug' ); // Remove theme and WC templates that have the same slug as a customised one. return array_values( array_filter( $templates, function ( $template ) use ( $customised_template_slugs ) { // This template has been customised and saved as a post, so return it. return ! ( 'custom' !== $template->source && in_array( $template->slug, $customised_template_slugs, true ) ); } ) ); } /** * Removes customized templates that shouldn't be available. That means customized templates based on the * WooCommerce default template when there is a customized template based on the theme template. * * @param \WP_Block_Template[]|\stdClass[] $templates List of templates to run the filter on. * * @return array Filtered list of templates with only relevant templates available. */ public static function remove_duplicate_customized_templates( $templates ) { $theme_slug = get_stylesheet(); $customized_theme_template_slugs = array_column( array_filter( $templates, function ( $template ) use ( $theme_slug ) { // This template has been customised and saved as a post. return 'custom' === $template->source && $theme_slug === $template->theme; } ), 'slug' ); return array_filter( $templates, function ( $template ) use ( $theme_slug, $customized_theme_template_slugs ) { if ( $template->theme === $theme_slug ) { // This is a customized template based on the theme template, so it should be returned. return true; } // Customized from the WooCommerce default template: keep only if there is no customized theme template with same slug. if ( 'custom' === $template->source ) { return ! in_array( $template->slug, $customized_theme_template_slugs, true ); } return true; } ); } /** * Returns whether the blockified templates should be used or not. * If the option is not stored on the db, we need to check if the current theme is a block one or not. * * @return boolean */ public static function should_use_blockified_product_grid_templates() { $use_blockified_templates = get_option( Options::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE ); if ( false === $use_blockified_templates ) { return wp_is_block_theme(); } return wc_string_to_bool( $use_blockified_templates ); } /** * Determines whether the provided $blocks contains any of the $block_names, * or if they contain a pattern that contains any of the $block_names. * * @param string[] $block_names Full block types to look for. * @param WP_Block[] $blocks Array of block objects. * @return bool Whether the content contains the specified block. */ public static function has_block_including_patterns( $block_names, $blocks ) { $flattened_blocks = self::flatten_blocks( $blocks ); foreach ( $flattened_blocks as &$block ) { if ( isset( $block['blockName'] ) && in_array( $block['blockName'], $block_names, true ) ) { return true; } if ( 'core/pattern' === $block['blockName'] && isset( $block['attrs']['slug'] ) ) { $registry = WP_Block_Patterns_Registry::get_instance(); $pattern = $registry->get_registered( $block['attrs']['slug'] ); if ( isset( $pattern['content'] ) ) { $pattern_blocks = parse_blocks( $pattern['content'] ); if ( self::has_block_including_patterns( $block_names, $pattern_blocks ) ) { return true; } } } } return false; } /** * Returns whether the passed `$template` has the legacy template block. * * @param object $template The template object. * @return boolean */ public static function template_has_legacy_template_block( $template ) { if ( has_block( 'woocommerce/legacy-template', $template->content ) ) { return true; } $blocks = parse_blocks( $template->content ); return self::has_block_including_patterns( array( 'woocommerce/legacy-template' ), $blocks ); } /** * 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. * * @param array $slugs An array of slugs to retrieve templates for. * @param string $template_type wp_template or wp_template_part. * * @return \WP_Block_Template[] An array of found templates. */ public static function get_block_templates_from_db( $slugs = array(), $template_type = 'wp_template' ) { $check_query_args = array( 'post_type' => $template_type, 'posts_per_page' => -1, 'no_found_rows' => true, 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query array( 'taxonomy' => 'wp_theme', 'field' => 'name', 'terms' => array( self::DEPRECATED_PLUGIN_SLUG, self::PLUGIN_SLUG, get_stylesheet() ), ), ), ); if ( is_array( $slugs ) && count( $slugs ) > 0 ) { $check_query_args['post_name__in'] = $slugs; } $check_query = new \WP_Query( $check_query_args ); $saved_woo_templates = $check_query->posts; return array_map( function ( $saved_woo_template ) { return self::build_template_result_from_post( $saved_woo_template ); }, $saved_woo_templates ); } /** * Gets the template part by slug * * @param string $slug The template part slug. * * @return string The template part content. */ public static function get_template_part( $slug ) { $templates_from_db = self::get_block_templates_from_db( array( $slug ), 'wp_template_part' ); if ( count( $templates_from_db ) > 0 ) { $template_slug_to_load = $templates_from_db[0]->theme; } else { $theme_has_template = self::theme_has_template_part( $slug ); $template_slug_to_load = $theme_has_template ? get_stylesheet() : self::PLUGIN_SLUG; } $template_part = get_block_template( $template_slug_to_load . '//' . $slug, 'wp_template_part' ); if ( $template_part && ! empty( $template_part->content ) ) { return $template_part->content; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents return file_get_contents( self::get_templates_directory( 'wp_template_part' ) . DIRECTORY_SEPARATOR . $slug . '.html' ); } } StyleAttributesUtils.php 0000777 00000056175 15252146202 0011471 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; /** * StyleAttributesUtils class used for getting class and style from attributes. */ class StyleAttributesUtils { // Empty style array. const EMPTY_STYLE = [ 'class' => '', 'style' => '', 'value' => '', ]; /** * If color value is in preset format, convert it to a CSS var. Else return same value * For example: * "var:preset|color|pale-pink" -> "var(--wp--preset--color--pale-pink)" * "#98b66e" -> "#98b66e" * * @param string $color_value value to be processed. * * @return (string) */ public static function get_color_value( $color_value ) { if ( is_string( $color_value ) && strpos( $color_value, 'var:preset|color|' ) !== false ) { $color_value = str_replace( 'var:preset|color|', '', $color_value ); return sprintf( 'var(--wp--preset--color--%s)', $color_value ); } return $color_value; } /** * Get CSS value for color preset. * * @param string $preset_name Preset name. * * @return string CSS value for color preset. */ public static function get_preset_value( $preset_name ) { return "var(--wp--preset--color--$preset_name)"; } /** * Get CSS value for shadow preset. Returns the same value if it's not a preset. * * @param string $shadow_name Shadow name. * * @return string CSS value for shadow preset. */ public static function get_shadow_value( $shadow_name ) { if ( is_string( $shadow_name ) && strpos( $shadow_name, 'var:preset|shadow|' ) !== false ) { $shadow_name = str_replace( 'var:preset|shadow|', '', $shadow_name ); return "var(--wp--preset--shadow--{$shadow_name})"; } return $shadow_name; } /** * If spacing value is in preset format, convert it to a CSS var. Else return same value * For example: * "var:preset|spacing|50" -> "var(--wp--preset--spacing--50)" * "50px" -> "50px" * * @param string $spacing_value value to be processed. * * @return (string) */ public static function get_spacing_value( $spacing_value ) { // Used following code as reference: https://github.com/WordPress/gutenberg/blob/cff6d70d6ff5a26e212958623dc3130569f95685/lib/block-supports/layout.php/#L219-L225. if ( is_string( $spacing_value ) && strpos( $spacing_value, 'var:preset|spacing|' ) !== false ) { $spacing_value = str_replace( 'var:preset|spacing|', '', $spacing_value ); return sprintf( 'var(--wp--preset--spacing--%s)', $spacing_value ); } return $spacing_value; } /** * Get class and style for align from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_align_class_and_style( $attributes ) { $align_attribute = $attributes['align'] ?? null; if ( 'wide' === $align_attribute ) { return array( 'class' => 'alignwide', 'style' => null, ); } if ( 'full' === $align_attribute ) { return array( 'class' => 'alignfull', 'style' => null, ); } if ( 'left' === $align_attribute ) { return array( 'class' => 'alignleft', 'style' => null, ); } if ( 'right' === $align_attribute ) { return array( 'class' => 'alignright', 'style' => null, ); } if ( 'center' === $align_attribute ) { return array( 'class' => 'aligncenter', 'style' => null, ); } return self::EMPTY_STYLE; } /** * Get class and style for background-color from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_background_color_class_and_style( $attributes ) { $gradient = $attributes['gradient'] ?? null; $background_color = $attributes['backgroundColor'] ?? ''; $custom_background_color = $attributes['style']['color']['background'] ?? ''; $classes = [ $gradient ]; $styles = []; $value = null; if ( $background_color || $custom_background_color || $gradient ) { $classes[] = 'has-background'; } if ( $background_color ) { $classes[] = sprintf( 'has-%s-background-color', $background_color ); $value = self::get_preset_value( $background_color ); } if ( $custom_background_color ) { $styles[] = sprintf( 'background-color: %s;', $custom_background_color ); $value = $custom_background_color; } if ( $gradient ) { $classes[] = sprintf( 'has-%s-gradient-background', $gradient ); } return array( 'class' => self::join_styles( $classes ), 'style' => self::join_styles( $styles ), 'value' => $value, ); } /** * Join classes and styles while removing duplicates and null values. * * @param array $rules Array of classes or styles. * @return array */ protected static function join_styles( $rules ) { return implode( ' ', array_unique( array_filter( $rules ) ) ); } /** * Get class and style for border-color from attributes. * * Data passed to this function is not always consistent. It can be: * Linked - preset color: $attributes['borderColor'] => 'luminous-vivid-orange'. * Linked - custom color: $attributes['style']['border']['color'] => '#681228'. * Unlinked - preset color: $attributes['style']['border']['top']['color'] => 'var:preset|color|luminous-vivid-orange' * Unlinked - custom color: $attributes['style']['border']['top']['color'] => '#681228'. * * @param array $attributes Block attributes. * @return array */ public static function get_border_color_class_and_style( $attributes ) { $border_color_linked_preset = $attributes['borderColor'] ?? ''; $border_color_linked_custom = $attributes['style']['border']['color'] ?? ''; $custom_border = $attributes['style']['border'] ?? ''; $class = ''; $style = ''; $value = ''; if ( $border_color_linked_preset ) { // Linked preset color. $class = sprintf( 'has-border-color has-%s-border-color', $border_color_linked_preset ); $value = self::get_preset_value( $border_color_linked_preset ); $style = 'border-color:' . $value . ';'; } elseif ( $border_color_linked_custom ) { // Linked custom color. $style .= 'border-color:' . $border_color_linked_custom . ';'; $value = $border_color_linked_custom; } elseif ( is_array( $custom_border ) ) { // Unlinked. foreach ( $custom_border as $border_color_key => $border_color_value ) { if ( is_array( $border_color_value ) && array_key_exists( 'color', ( $border_color_value ) ) ) { $style .= 'border-' . $border_color_key . '-color:' . self::get_color_value( $border_color_value['color'] ) . ';'; } } } if ( ! $class && ! $style ) { return self::EMPTY_STYLE; } return array( 'class' => $class, 'style' => $style, 'value' => $value, ); } /** * Get class and style for border-radius from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_border_radius_class_and_style( $attributes ) { $custom_border_radius = $attributes['style']['border']['radius'] ?? ''; if ( '' === $custom_border_radius ) { return self::EMPTY_STYLE; } $style = ''; if ( is_string( $custom_border_radius ) ) { // Linked sides. $style = 'border-radius:' . $custom_border_radius . ';'; } else { // Unlinked sides. $border_radius = array(); $border_radius['border-top-left-radius'] = $custom_border_radius['topLeft'] ?? ''; $border_radius['border-top-right-radius'] = $custom_border_radius['topRight'] ?? ''; $border_radius['border-bottom-right-radius'] = $custom_border_radius['bottomRight'] ?? ''; $border_radius['border-bottom-left-radius'] = $custom_border_radius['bottomLeft'] ?? ''; foreach ( $border_radius as $border_radius_side => $border_radius_value ) { if ( '' !== $border_radius_value ) { $style .= $border_radius_side . ':' . $border_radius_value . ';'; } } } return array( 'class' => null, 'style' => $style, ); } /** * Get class and style for border width from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_border_width_class_and_style( $attributes ) { $custom_border = $attributes['style']['border'] ?? ''; if ( '' === $custom_border ) { return self::EMPTY_STYLE; } $style = ''; if ( array_key_exists( 'width', ( $custom_border ) ) && ! empty( $custom_border['width'] ) ) { // Linked sides. $style = 'border-width:' . $custom_border['width'] . ';'; } else { // Unlinked sides. foreach ( $custom_border as $border_width_side => $border_width_value ) { if ( isset( $border_width_value['width'] ) ) { $style .= 'border-' . $border_width_side . '-width:' . $border_width_value['width'] . ';'; } } } return array( 'class' => null, 'style' => $style, ); } /** * Get class and style for border width from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_border_style_class_and_style( $attributes ) { $custom_border = $attributes['style']['border'] ?? ''; if ( '' === $custom_border ) { return self::EMPTY_STYLE; } $style = ''; if ( array_key_exists( 'style', ( $custom_border ) ) && ! empty( $custom_border['style'] ) ) { $style = 'border-style:' . $custom_border['style'] . ';'; } else { foreach ( $custom_border as $side => $value ) { if ( isset( $value['style'] ) ) { $style .= 'border-' . $side . '-style:' . $value['style'] . ';'; } } } return array( 'class' => null, 'style' => $style, ); } /** * Get space-separated classes from block attributes. * * @param array $attributes Block attributes. * @param array $properties Properties to get classes from. * * @return string Space-separated classes. */ public static function get_classes_by_attributes( $attributes, $properties = array() ) { $classes_and_styles = self::get_classes_and_styles_by_attributes( $attributes, $properties ); return $classes_and_styles['classes']; } /** * Get class and style for font-family from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_font_family_class_and_style( $attributes ) { $font_family = $attributes['fontFamily'] ?? ''; if ( $font_family ) { return array( 'class' => sprintf( 'has-%s-font-family', $font_family ), 'style' => null, ); } return self::EMPTY_STYLE; } /** * Get class and style for font-size from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_font_size_class_and_style( $attributes ) { $font_size = $attributes['fontSize'] ?? ''; $custom_font_size = $attributes['style']['typography']['fontSize'] ?? ''; if ( ! $font_size && '' === $custom_font_size ) { return self::EMPTY_STYLE; } if ( $font_size ) { return array( 'class' => sprintf( 'has-font-size has-%s-font-size', $font_size ), 'style' => null, ); } elseif ( '' !== $custom_font_size ) { return array( 'class' => null, 'style' => sprintf( 'font-size: %s;', $custom_font_size ), ); } return self::EMPTY_STYLE; } /** * Get class and style for font-style from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_font_style_class_and_style( $attributes ) { $custom_font_style = $attributes['style']['typography']['fontStyle'] ?? ''; if ( '' !== $custom_font_style ) { return array( 'class' => null, 'style' => sprintf( 'font-style: %s;', $custom_font_style ), ); } return self::EMPTY_STYLE; } /** * Get class and style for font-weight from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_font_weight_class_and_style( $attributes ) { $custom_font_weight = $attributes['style']['typography']['fontWeight'] ?? ''; if ( '' !== $custom_font_weight ) { return array( 'class' => null, 'style' => sprintf( 'font-weight: %s;', $custom_font_weight ), ); } return self::EMPTY_STYLE; } /** * Get class and style for letter-spacing from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_letter_spacing_class_and_style( $attributes ) { $custom_letter_spacing = $attributes['style']['typography']['letterSpacing'] ?? ''; if ( '' !== $custom_letter_spacing ) { return array( 'class' => null, 'style' => sprintf( 'letter-spacing: %s;', $custom_letter_spacing ), ); } return self::EMPTY_STYLE; } /** * Get class and style for line height from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_line_height_class_and_style( $attributes ) { $line_height = $attributes['style']['typography']['lineHeight'] ?? ''; if ( ! $line_height ) { return self::EMPTY_STYLE; } return array( 'class' => null, 'style' => sprintf( 'line-height: %s;', $line_height ), ); } /** * Get a value from an array based on a path e.g style.elements.link * * @param array $array Target array. * @param string $path Path joined by delimiter. * @param string $delimiter Chosen delimiter defaults to ".". * @return mixed */ protected static function array_get_value_by_path( array &$array, $path, $delimiter = '.' ) { $array_path = explode( $delimiter, $path ); $ref = &$array; foreach ( $array_path as $key ) { if ( is_array( $ref ) && array_key_exists( $key, $ref ) ) { $ref = &$ref[ $key ]; } else { return null; } } return $ref; } /** * Get class and style for link-color from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_link_color_class_and_style( $attributes ) { $link_color = self::array_get_value_by_path( $attributes, 'style.elements.link.color.text' ); if ( empty( $link_color ) ) { return self::EMPTY_STYLE; } // If the link color is selected from the theme color picker, the value of $link_color is var:preset|color|slug. // If the link color is selected from the core color picker, the value of $link_color is an hex value. // When the link color is a string var:preset|color|slug we parsed it for get the slug, otherwise we use the hex value. if ( strstr( $link_color, '|' ) ) { $link_color_parts = explode( '|', $link_color ); $link_color = self::get_preset_value( end( $link_color_parts ) ); } return array( 'class' => 'has-link-color', 'style' => sprintf( 'color: %s;', $link_color ), 'value' => $link_color, ); } /** * Get class and style for link-hover-color from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_link_hover_color_class_and_style( $attributes ) { $link_color = self::array_get_value_by_path( $attributes, 'style.elements.link.:hover.color.text' ); if ( empty( $link_color ) ) { return self::EMPTY_STYLE; } // If the link color is selected from the theme color picker, the value of $link_color is var:preset|color|slug. // If the link color is selected from the core color picker, the value of $link_color is an hex value. // When the link color is a string var:preset|color|slug we parsed it for get the slug, otherwise we use the hex value. if ( strstr( $link_color, '|' ) ) { $link_color_parts = explode( '|', $link_color ); $link_color = self::get_preset_value( end( $link_color_parts ) ); } return array( 'class' => 'has-link-color', 'style' => sprintf( 'color: %s;', $link_color ), 'value' => $link_color, ); } /** * Get class and style for margin from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_margin_class_and_style( $attributes ) { $margin = $attributes['style']['spacing']['margin'] ?? null; if ( ! $margin ) { return self::EMPTY_STYLE; } $spacing_values_css = ''; foreach ( $margin as $margin_side => $margin_value ) { $spacing_values_css .= 'margin-' . $margin_side . ':' . self::get_spacing_value( $margin_value ) . ';'; } return array( 'class' => null, 'style' => $spacing_values_css, ); } /** * Get class and style for padding from attributes. * * @param array $attributes Block attributes. * * @return array */ public static function get_padding_class_and_style( $attributes ) { $padding = $attributes['style']['spacing']['padding'] ?? null; if ( ! $padding ) { return self::EMPTY_STYLE; } $spacing_values_css = ''; foreach ( $padding as $padding_side => $padding_value ) { $spacing_values_css .= 'padding-' . $padding_side . ':' . self::get_spacing_value( $padding_value ) . ';'; } return array( 'class' => null, 'style' => $spacing_values_css, ); } /** * Get class and style for shadow from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_shadow_class_and_style( $attributes ) { $shadow = $attributes['style']['shadow'] ?? null; if ( ! $shadow ) { return self::EMPTY_STYLE; } return array( 'class' => null, 'style' => sprintf( 'box-shadow: %s;', self::get_shadow_value( $shadow ) ), ); } /** * Get space-separated style rules from block attributes. * * @param array $attributes Block attributes. * @param array $properties Properties to get styles from. * * @return string Space-separated style rules. */ public static function get_styles_by_attributes( $attributes, $properties = array() ) { $classes_and_styles = self::get_classes_and_styles_by_attributes( $attributes, $properties ); return $classes_and_styles['styles']; } /** * Get class and style for text align from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_text_align_class_and_style( $attributes ) { // Check if the text align is set in the attributes manually (legacy) or in the global styles. $text_align = $attributes['textAlign'] ?? $attributes['style']['typography']['textAlign'] ?? null; if ( $text_align ) { return array( 'class' => 'has-text-align-' . $text_align, 'style' => null, ); } return self::EMPTY_STYLE; } /** * Get class and style for text-color from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_text_color_class_and_style( $attributes ) { $text_color = $attributes['textColor'] ?? ''; $custom_text_color = $attributes['style']['color']['text'] ?? ''; if ( ! $text_color && ! $custom_text_color ) { return self::EMPTY_STYLE; } if ( $text_color ) { return array( 'class' => sprintf( 'has-text-color has-%s-color', $text_color ), 'style' => null, 'value' => self::get_preset_value( $text_color ), ); } elseif ( $custom_text_color ) { return array( 'class' => null, 'style' => sprintf( 'color: %s;', $custom_text_color ), 'value' => $custom_text_color, ); } return self::EMPTY_STYLE; } /** * Get class and style for text-decoration from attributes. * * @param array $attributes Block attributes. * * @return array */ public static function get_text_decoration_class_and_style( $attributes ) { $custom_text_decoration = $attributes['style']['typography']['textDecoration'] ?? ''; if ( '' !== $custom_text_decoration ) { return array( 'class' => null, 'style' => sprintf( 'text-decoration: %s;', $custom_text_decoration ), ); } return self::EMPTY_STYLE; } /** * Get class and style for text-transform from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_text_transform_class_and_style( $attributes ) { $custom_text_transform = $attributes['style']['typography']['textTransform'] ?? ''; if ( '' !== $custom_text_transform ) { return array( 'class' => null, 'style' => sprintf( 'text-transform: %s;', $custom_text_transform ), ); } return self::EMPTY_STYLE; } /** * Get extra CSS classes from attributes. * * @param array $attributes Block attributes. * @return array */ public static function get_classes_from_attributes( $attributes ) { $extra_css_classes = $attributes['className'] ?? ''; if ( '' !== $extra_css_classes ) { return array( 'class' => esc_attr( $extra_css_classes ), 'style' => null, ); } return self::EMPTY_STYLE; } /** * Get classes and styles from attributes. * * Excludes link_color and link_hover_color since those should not apply to the container. * * @param array $attributes Block attributes. * @param array $properties Properties to get classes/styles from. * @param array $exclude Properties to exclude. * @return array */ public static function get_classes_and_styles_by_attributes( $attributes, $properties = array(), $exclude = array() ) { $classes_and_styles = array( 'align' => self::get_align_class_and_style( $attributes ), 'background_color' => self::get_background_color_class_and_style( $attributes ), 'border_color' => self::get_border_color_class_and_style( $attributes ), 'border_radius' => self::get_border_radius_class_and_style( $attributes ), 'border_width' => self::get_border_width_class_and_style( $attributes ), 'border_style' => self::get_border_style_class_and_style( $attributes ), 'font_family' => self::get_font_family_class_and_style( $attributes ), 'font_size' => self::get_font_size_class_and_style( $attributes ), 'font_style' => self::get_font_style_class_and_style( $attributes ), 'font_weight' => self::get_font_weight_class_and_style( $attributes ), 'letter_spacing' => self::get_letter_spacing_class_and_style( $attributes ), 'line_height' => self::get_line_height_class_and_style( $attributes ), 'margin' => self::get_margin_class_and_style( $attributes ), 'padding' => self::get_padding_class_and_style( $attributes ), 'shadow' => self::get_shadow_class_and_style( $attributes ), 'text_align' => self::get_text_align_class_and_style( $attributes ), 'text_color' => self::get_text_color_class_and_style( $attributes ), 'text_decoration' => self::get_text_decoration_class_and_style( $attributes ), 'text_transform' => self::get_text_transform_class_and_style( $attributes ), 'extra_classes' => self::get_classes_from_attributes( $attributes ), ); if ( ! empty( $properties ) ) { foreach ( $classes_and_styles as $key => $value ) { if ( ! in_array( $key, $properties, true ) ) { unset( $classes_and_styles[ $key ] ); } } } if ( ! empty( $exclude ) ) { foreach ( $classes_and_styles as $key => $value ) { if ( in_array( $key, $exclude, true ) ) { unset( $classes_and_styles[ $key ] ); } } } $classes_and_styles = array_filter( $classes_and_styles ); $classes = array_map( function ( $item ) { return $item['class']; }, $classes_and_styles ); $styles = array_map( function ( $item ) { return $item['style']; }, // Exclude link color styles from parent to avoid conflict with text color. array_diff_key( $classes_and_styles, array_flip( array( 'link_color' ) ) ) ); $classes = array_filter( $classes ); $styles = array_filter( $styles ); return array( 'classes' => implode( ' ', $classes ), 'styles' => implode( ' ', $styles ), ); } } BlocksWpQuery.php 0000777 00000004125 15252146202 0010037 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; use WP_Query; /** * BlocksWpQuery query. * * Wrapper for WP Query with additional helper methods. * Allows query args to be set and parsed without doing running it, so that a cache can be used. * * @deprecated 2.5.0 */ class BlocksWpQuery extends WP_Query { /** * Constructor. * * Sets up the WordPress query, if parameter is not empty. * * Unlike the constructor in WP_Query, this does not RUN the query. * * @param string|array $query URL query string or array of vars. */ public function __construct( $query = '' ) { if ( ! empty( $query ) ) { $this->init(); $this->query = wp_parse_args( $query ); $this->query_vars = $this->query; $this->parse_query_vars(); } } /** * Get cached posts, if a cache exists. * * A hash is generated using the array of query_vars. If doing custom queries via filters such as posts_where * (where the SQL query is manipulated directly) you can still ensure there is a unique hash by injecting custom * query vars via the parse_query filter. For example: * * add_filter( 'parse_query', function( $wp_query ) { * $wp_query->query_vars['my_custom_query_var'] = true; * } ); * * Doing so won't have any negative effect on the query itself, and it will cause the hash to change. * * @param string $transient_version Transient version to allow for invalidation. * @return WP_Post[]|int[] Array of post objects or post IDs. */ public function get_cached_posts( $transient_version = '' ) { $hash = md5( wp_json_encode( $this->query_vars ) ); $transient_name = 'wc_blocks_query_' . $hash; $transient_value = get_transient( $transient_name ); if ( isset( $transient_value, $transient_value['version'], $transient_value['value'] ) && $transient_value['version'] === $transient_version ) { return $transient_value['value']; } $results = $this->get_posts(); set_transient( $transient_name, array( 'version' => $transient_version, 'value' => $results, ), DAY_IN_SECONDS * 30 ); return $results; } } CartCheckoutUtils.php 0000777 00000042543 15252146202 0010673 0 ustar 00 <?php // phpcs:ignore Generic.PHP.RequireStrictTypes.MissingDeclaration namespace Automattic\WooCommerce\Blocks\Utils; use Automattic\Block_Scanner; /** * Class containing utility methods for dealing with the Cart and Checkout blocks. */ class CartCheckoutUtils { /** * Caches if we're on the cart page. * * @var bool */ private static $is_cart_page = null; /** * Caches if we're on the checkout page. * * @var bool */ private static $is_checkout_page = null; /** * Returns true if the current page is a specific page type (cart or checkout). * * This is determined by looking at the global $post object and comparing it to the post ID defined in settings, * or checking the page contents for a block or shortcode. * * This function cannot be used accurately before the `pre_get_posts` action has been run. * * @param string $page_type The page type to check for. * @return bool|null */ private static function is_page_type( string $page_type ): ?bool { if ( ! did_action( 'pre_get_posts' ) ) { return null; } $page_id = wc_get_page_id( $page_type ); if ( $page_id && is_page( $page_id ) ) { return true; } // If the is_page check returned false, check the page contents for a cart block or shortcode. global $post; if ( null === $post ) { return null; } if ( $post instanceof \WP_Post ) { return wc_post_content_has_shortcode( 'cart' === $page_type ? 'woocommerce_cart' : 'woocommerce_checkout' ) || self::has_block_variation( 'woocommerce/classic-shortcode', 'shortcode', $page_type, $post->post_content ); } return false; } /** * Returns true on the cart page. * * @return bool */ public static function is_cart_page(): bool { if ( null === self::$is_cart_page ) { self::$is_cart_page = self::is_page_type( 'cart' ); } return true === self::$is_cart_page; } /** * Returns true on the checkout page. * * @return bool */ public static function is_checkout_page(): bool { if ( null === self::$is_checkout_page ) { self::$is_checkout_page = self::is_page_type( 'checkout' ); } return true === self::$is_checkout_page; } /** * Returns true if shipping methods exist in the store. Excludes local pickup and only counts enabled shipping methods. * * @return bool true if shipping methods exist. */ public static function shipping_methods_exist() { // Local pickup is included with legacy shipping methods since they do not support shipping zones. $local_pickup_count = count( array_filter( WC()->shipping()->get_shipping_methods(), function ( $method ) { return isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) && $method->supports( 'local-pickup' ); } ) ); $shipping_methods_count = wc_get_shipping_method_count( true, true ) - $local_pickup_count; return $shipping_methods_count > 0; } /** * Check if the post content contains a block with a specific attribute value. * * @param string $block_id The block ID to check for. * @param string $attribute The attribute to check. * @param string $value The value to check for. * @param string $post_content The post content to check. * @return boolean */ public static function has_block_variation( $block_id, $attribute, $value, $post_content ) { if ( ! $post_content ) { return false; } $scanner = Block_Scanner::create( $post_content ); if ( ! $scanner ) { return false; } while ( $scanner->next_delimiter() ) { if ( ! $scanner->opens_block( $block_id ) ) { continue; } $attrs = $scanner->allocate_and_return_parsed_attributes(); if ( isset( $attrs[ $attribute ] ) && $value === $attrs[ $attribute ] ) { return true; } // `Cart` is default for `woocommerce/classic-shortcode` so it will be empty in the block attributes. if ( 'woocommerce/classic-shortcode' === $block_id && 'shortcode' === $attribute && 'cart' === $value && ! isset( $attrs['shortcode'] ) ) { return true; } } return false; } /** * Checks if the default cart page is using the Cart block. * * @return bool true if the WC cart page is using the Cart block. */ public static function is_cart_block_default() { if ( wp_is_block_theme() ) { // Ignore the pages and check the templates. $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'cart' ), 'wp_template' ); foreach ( $templates_from_db as $template ) { if ( has_block( 'woocommerce/cart', $template->content ) ) { return true; } } } $cart_page_id = wc_get_page_id( 'cart' ); return $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); } /** * Checks if the default checkout page is using the Checkout block. * * @return bool true if the WC checkout page is using the Checkout block. */ public static function is_checkout_block_default() { if ( wp_is_block_theme() ) { // Ignore the pages and check the templates. $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'checkout' ), 'wp_template' ); foreach ( $templates_from_db as $template ) { if ( has_block( 'woocommerce/checkout', $template->content ) ) { return true; } } } $checkout_page_id = wc_get_page_id( 'checkout' ); return $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id ); } /** * Migrate checkout block field visibility attributes to settings when using the checkout block. * * This migration routine is called if the options (woocommerce_checkout_phone_field, woocommerce_checkout_company_field, * woocommerce_checkout_address_2_field) are not set. They are not set by default; they were orignally set by the * customizer interface of the legacy shortcode based checkout. * * Once migration is initiated, the settings will be updated and will not trigger this routine again. * * Note: The block only stores non-default attributes. Not all attributes will be present. * * e.g. `{"showCompanyField":true,"requireCompanyField":true,"showApartmentField":false,"className":"wc-block-checkout"}` * * If the attributes are missing, we assume default values are needed. */ protected static function migrate_checkout_block_field_visibility_attributes() { // Before migrating attributes, migrate the "default" options checkout block uses into the settings. update_option( 'woocommerce_checkout_phone_field', 'optional' ); update_option( 'woocommerce_checkout_company_field', 'hidden' ); update_option( 'woocommerce_checkout_address_2_field', 'optional' ); // Parse the block from the checkout page. $checkout_blocks = \WC_Blocks_Utils::get_blocks_from_page( 'woocommerce/checkout', 'checkout' ); if ( empty( $checkout_blocks ) || ! isset( $checkout_blocks[0]['attrs'] ) ) { return; } // Combine actual attributes with default values. $block_attributes = wp_parse_args( $checkout_blocks[0]['attrs'], array( 'showPhoneField' => true, 'requirePhoneField' => false, 'showCompanyField' => false, 'requireCompanyField' => false, 'showApartmentField' => true, 'requireApartmentField' => false, ) ); if ( $block_attributes['showPhoneField'] ) { update_option( 'woocommerce_checkout_phone_field', $block_attributes['requirePhoneField'] ? 'required' : 'optional' ); } else { update_option( 'woocommerce_checkout_phone_field', 'hidden' ); } if ( $block_attributes['showCompanyField'] ) { update_option( 'woocommerce_checkout_company_field', $block_attributes['requireCompanyField'] ? 'required' : 'optional' ); } else { update_option( 'woocommerce_checkout_company_field', 'hidden' ); } if ( $block_attributes['showApartmentField'] ) { update_option( 'woocommerce_checkout_address_2_field', $block_attributes['requireApartmentField'] ? 'required' : 'optional' ); } else { update_option( 'woocommerce_checkout_address_2_field', 'hidden' ); } } /** * Get the default visibility for the address_2 field. * * @return string */ public static function get_company_field_visibility() { $option_value = get_option( 'woocommerce_checkout_company_field' ); if ( $option_value ) { return $option_value; } if ( self::is_checkout_block_default() ) { self::migrate_checkout_block_field_visibility_attributes(); return get_option( 'woocommerce_checkout_company_field', 'hidden' ); } return 'optional'; } /** * Get the default visibility for the address_2 field. * * @return string */ public static function get_address_2_field_visibility() { $option_value = get_option( 'woocommerce_checkout_address_2_field' ); if ( $option_value ) { return $option_value; } if ( self::is_checkout_block_default() ) { self::migrate_checkout_block_field_visibility_attributes(); return get_option( 'woocommerce_checkout_address_2_field', 'optional' ); } return 'optional'; } /** * Get the default visibility for the address_2 field. * * @return string */ public static function get_phone_field_visibility() { $option_value = get_option( 'woocommerce_checkout_phone_field' ); if ( $option_value ) { return $option_value; } if ( self::is_checkout_block_default() ) { self::migrate_checkout_block_field_visibility_attributes(); return get_option( 'woocommerce_checkout_phone_field', 'optional' ); } return 'required'; } /** * Checks if the template overriding the page loads the page content or not. * Templates by default load the page content, but if that block is deleted the content can get out of sync with the one presented in the page editor. * * @param string $block The block to check. * * @return bool true if the template has out of sync content. */ public static function is_overriden_by_custom_template_content( string $block ): bool { $block = str_replace( 'woocommerce/', '', $block ); if ( wp_is_block_theme() ) { $templates_from_db = BlockTemplateUtils::get_block_templates_from_db( array( 'page-' . $block ) ); foreach ( $templates_from_db as $template ) { if ( ! has_block( 'woocommerce/page-content-wrapper', $template->content ) ) { // Return true if the template does not load the page content via the woocommerce/page-content-wrapper block. return true; } } } return false; } /** * Gets country codes, names, states, and locale information. * * @return array */ public static function get_country_data() { $billing_countries = WC()->countries->get_allowed_countries(); $shipping_countries = WC()->countries->get_shipping_countries(); $country_states = wc()->countries->get_states(); $all_countries = self::deep_sort_with_accents( array_unique( array_merge( $billing_countries, $shipping_countries ) ) ); $country_locales = array_map( function ( $locale ) { foreach ( $locale as $field => $field_data ) { if ( isset( $field_data['priority'] ) ) { $locale[ $field ]['index'] = $field_data['priority']; unset( $locale[ $field ]['priority'] ); } if ( isset( $field_data['class'] ) ) { unset( $locale[ $field ]['class'] ); } } return $locale; }, WC()->countries->get_country_locale() ); $country_data = array(); foreach ( array_keys( $all_countries ) as $country_code ) { $country_data[ $country_code ] = array( 'allowBilling' => isset( $billing_countries[ $country_code ] ), 'allowShipping' => isset( $shipping_countries[ $country_code ] ), 'states' => $country_states[ $country_code ] ?? array(), 'locale' => $country_locales[ $country_code ] ?? array(), ); } return $country_data; } /** * Removes accents from an array of values, sorts by the values, then returns the original array values sorted. * * @param array $sort_array Array of values to sort. * @return array Sorted array. */ protected static function deep_sort_with_accents( $sort_array ) { if ( ! is_array( $sort_array ) || empty( $sort_array ) ) { return $sort_array; } $array_without_accents = array_map( function ( $value ) { return is_array( $value ) ? self::deep_sort_with_accents( $value ) : remove_accents( wc_strtolower( html_entity_decode( $value ) ) ); }, $sort_array ); asort( $array_without_accents ); return array_replace( $array_without_accents, $sort_array ); } /** * Retrieves formatted shipping zones from WooCommerce. * * @return array An array of formatted shipping zones. */ public static function get_shipping_zones() { $shipping_zones = \WC_Shipping_Zones::get_zones(); $formatted_shipping_zones = array_reduce( $shipping_zones, function ( $acc, $zone ) { $acc[] = array( 'id' => $zone['id'], 'title' => $zone['zone_name'], 'description' => $zone['formatted_zone_location'], ); return $acc; }, array() ); $formatted_shipping_zones[] = array( 'id' => 0, 'title' => __( 'International', 'woocommerce' ), 'description' => __( 'Locations outside all other zones', 'woocommerce' ), ); return $formatted_shipping_zones; } /** * Recursively search the checkout block to find the express checkout block and * get the button style attributes using the parse_blocks function. * * @param array $blocks Blocks to search. * @param string $cart_or_checkout The block type to check. * * @return array Block attributes. */ public static function find_express_checkout_attributes_in_parsed_blocks( $blocks, $cart_or_checkout ) { $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; foreach ( $blocks as $block ) { if ( ! empty( $block['blockName'] ) && $express_block_name === $block['blockName'] && ! empty( $block['attrs'] ) ) { return $block['attrs']; } if ( ! empty( $block['innerBlocks'] ) ) { $answer = self::find_express_checkout_attributes_in_parsed_blocks( $block['innerBlocks'], $cart_or_checkout ); if ( $answer ) { return $answer; } } } } /** * Recursively search the checkout block to find the express checkout block and * get the button style attributes * * @param string|array $post_content The post content. * @param string $cart_or_checkout The block type to check. * * @return array|null Block attributes, if present and valid, otherwise `null`. */ public static function find_express_checkout_attributes( $post_content, $cart_or_checkout ) { if ( is_array( $post_content ) ) { // If an array is passed, assume it's already been parsed with parse_blocks, // use the old method, and show a deprecation warning. wc_deprecated_argument( 'post_content', '10.3.0', 'Passing parsed blocks as an array in $post_content is deprecated. Please pass the post content as a string.' ); return self::find_express_checkout_attributes_in_parsed_blocks( $post_content, $cart_or_checkout ); } $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; $scanner = Block_Scanner::create( $post_content ); while ( $scanner->next_delimiter() ) { if ( $scanner->opens_block( $express_block_name ) ) { return $scanner->allocate_and_return_parsed_attributes(); } } return null; } /** * Given an array of blocks, find the express payment block and update its attributes. * * @param array $blocks Blocks to search. * @param string $cart_or_checkout The block type to check. * @param array $updated_attrs The new attributes to set. */ public static function update_blocks_with_new_attrs( &$blocks, $cart_or_checkout, $updated_attrs ) { $express_block_name = 'woocommerce/' . $cart_or_checkout . '-express-payment-block'; foreach ( $blocks as $key => &$block ) { if ( ! empty( $block['blockName'] ) && $express_block_name === $block['blockName'] ) { $blocks[ $key ]['attrs'] = $updated_attrs; } if ( ! empty( $block['innerBlocks'] ) ) { self::update_blocks_with_new_attrs( $block['innerBlocks'], $cart_or_checkout, $updated_attrs ); } } } /** * Check if the cart page is defined. * * @return bool True if the cart page is defined, false otherwise. */ public static function has_cart_page() { return wc_get_page_permalink( 'cart', -1 ) !== -1; } /** * Get product IDs from a user's persistent cart. * * This method retrieves product IDs stored in the user's persistent cart meta. * It can be used for abandoned cart emails, cart-based product collections, * and other scenarios where cart products need to be retrieved for a user. * * @param int|null $user_id The user ID. If not provided, will attempt to look up by email. * @param string|null $user_email The user email. Used to lookup user if ID not provided. * @return array<int> Array of product IDs from the user's cart, or empty array if none found. */ public static function get_cart_product_ids_for_user( ?int $user_id, ?string $user_email ) { if ( empty( $user_id ) && ! empty( $user_email ) ) { $user = get_user_by( 'email', $user_email ); if ( $user ) { $user_id = $user->ID; } } if ( empty( $user_id ) ) { return array(); } $cart_meta = get_user_meta( $user_id, '_woocommerce_persistent_cart_' . get_current_blog_id(), true ); if ( empty( $cart_meta ) || ! is_array( $cart_meta ) || empty( $cart_meta['cart'] ) ) { return array(); } return array_values( array_unique( array_filter( array_map( function ( $cart_item ) { return isset( $cart_item['product_id'] ) ? intval( $cart_item['product_id'] ) : 0; }, $cart_meta['cart'] ) ) ) ); } } ProductAvailabilityUtils.php 0000777 00000002413 15252146202 0012257 0 ustar 00 <?php declare(strict_types=1); namespace Automattic\WooCommerce\Blocks\Utils; use Automattic\WooCommerce\Blocks\Templates\ProductStockIndicator; use Automattic\WooCommerce\Enums\ProductType; /** * Utility functions for product availability. */ class ProductAvailabilityUtils { /** * Get product availability information. * * @param \WC_Product $product Product object. * @return string[] The product availability class and text. */ public static function get_product_availability( $product ) { $product_availability = array( 'availability' => '', 'class' => '', ); if ( ! $product ) { return $product_availability; } $product_availability = $product->get_availability(); // If the product is a variable product, make sure at least one of its // variations is purchasable. if ( isset( $product_availability['class'] ) && ( 'in-stock' === $product_availability['class'] || 'available-on-backorder' === $product_availability['class'] ) && ProductType::VARIABLE === $product->get_type() ) { if ( ! $product->has_purchasable_variations() ) { $product_availability['availability'] = __( 'Out of stock', 'woocommerce' ); $product_availability['class'] = 'out-of-stock'; } } return $product_availability; } } MiniCartUtils.php 0000777 00000007012 15252146202 0010012 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Utils; /** * Utility methods used for the Mini Cart block. */ class MiniCartUtils { /** * Migrate attributes to color panel component format. * * @param array $attributes Any attributes that currently are available from the block. * @return array Reformatted attributes that are compatible with the color panel component. */ public static function migrate_attributes_to_color_panel( $attributes ) { if ( isset( $attributes['priceColorValue'] ) && ! isset( $attributes['priceColor'] ) ) { $attributes['priceColor'] = array( 'color' => $attributes['priceColorValue'], ); unset( $attributes['priceColorValue'] ); } if ( isset( $attributes['iconColorValue'] ) && ! isset( $attributes['iconColor'] ) ) { $attributes['iconColor'] = array( 'color' => $attributes['iconColorValue'], ); unset( $attributes['iconColorValue'] ); } if ( isset( $attributes['productCountColorValue'] ) && ! isset( $attributes['productCountColor'] ) ) { $attributes['productCountColor'] = array( 'color' => $attributes['productCountColorValue'], ); unset( $attributes['productCountColorValue'] ); } return $attributes; } /** * Get the SVG icon for the mini cart. * * @param string $icon_name The name of the icon. * @param string $icon_color The color of the icon. * @return string The SVG icon. */ public static function get_svg_icon( $icon_name, $icon_color = 'currentColor' ) { // Default "Cart" icon. $icon = '<svg xmlns="http://www.w3.org/2000/svg" fill="' . esc_attr( $icon_color ) . '" class="wc-block-mini-cart__icon" viewBox="0 0 32 32"><circle cx="12.667" cy="24.667" r="2"/><circle cx="23.333" cy="24.667" r="2"/><path fill-rule="evenodd" d="M9.285 10.036a1 1 0 0 1 .776-.37h15.272a1 1 0 0 1 .99 1.142l-1.333 9.333A1 1 0 0 1 24 21H12a1 1 0 0 1-.98-.797L9.083 10.87a1 1 0 0 1 .203-.834m2.005 1.63L12.814 19h10.319l1.047-7.333z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M5.667 6.667a1 1 0 0 1 1-1h2.666a1 1 0 0 1 .984.82l.727 4a1 1 0 1 1-1.967.359l-.578-3.18H6.667a1 1 0 0 1-1-1" clip-rule="evenodd"/></svg>'; if ( isset( $icon_name ) ) { if ( 'bag' === $icon_name ) { $icon = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" class="wc-block-mini-cart__icon" viewBox="0 0 32 32"><path fill="' . esc_attr( $icon_color ) . '" fill-rule="evenodd" d="M12.444 14.222a.89.89 0 0 1 .89.89 2.667 2.667 0 0 0 5.333 0 .889.889 0 1 1 1.777 0 4.444 4.444 0 1 1-8.888 0c0-.492.398-.89.888-.89M11.24 6.683a1 1 0 0 1 .76-.35h8a1 1 0 0 1 .76.35l4 4.666A1 1 0 0 1 24 13H8a1 1 0 0 1-.76-1.65zm1.22 1.65L10.174 11h11.652L19.54 8.333z" clip-rule="evenodd"/><path fill="' . esc_attr( $icon_color ) . '" fill-rule="evenodd" d="M7 12a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v13.333a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1zm2 1v11.333h14V13z" clip-rule="evenodd"/></svg>'; } elseif ( 'bag-alt' === $icon_name ) { $icon = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" class="wc-block-mini-cart__icon" viewBox="0 0 32 32"><path fill="' . esc_attr( $icon_color ) . '" fill-rule="evenodd" d="M19.556 12.333a.89.89 0 0 1-.89-.889c0-.707-.28-3.385-.78-3.885a2.667 2.667 0 0 0-3.772 0c-.5.5-.78 3.178-.78 3.885a.889.889 0 1 1-1.778 0c0-1.178.468-4.309 1.301-5.142a4.445 4.445 0 0 1 6.286 0c.833.833 1.302 3.964 1.302 5.142a.89.89 0 0 1-.89.89" clip-rule="evenodd"/><path fill="' . esc_attr( $icon_color ) . '" fill-rule="evenodd" d="M7.5 12a1 1 0 0 1 1-1h15a1 1 0 0 1 1 1v13.333a1 1 0 0 1-1 1h-15a1 1 0 0 1-1-1zm2 1v11.333h13V13z" clip-rule="evenodd"/></svg>'; } } return $icon; } } ProductDataUtils.php 0000777 00000000707 15252146202 0010522 0 ustar 00 <?php declare(strict_types=1); namespace Automattic\WooCommerce\Blocks\Utils; /** * Utility class to get product data consumable by the blocks. * * @internal */ class ProductDataUtils { /** * Get the product data. * * @param \WC_Product $product Product object. * @return array The product data. */ public static function get_product_data( \WC_Product $product ) { return array( 'price_html' => $product->get_price_html(), ); } }
| ver. 1.6 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка