plugin updates
This commit is contained in:
@@ -0,0 +1,673 @@
|
||||
<?php
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\AIContent;
|
||||
|
||||
/**
|
||||
* Patterns Dictionary class.
|
||||
*/
|
||||
class PatternsDictionary {
|
||||
/**
|
||||
* Returns the patterns' dictionary.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function get() {
|
||||
return [
|
||||
[
|
||||
'name' => 'Banner',
|
||||
'slug' => 'woocommerce-blocks/banner',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Up to 60% off', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A four words title advertising the sale', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Holiday Sale', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words label with the sale name', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Get your favorite vinyl at record-breaking prices.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The main description of the sale with at least 65 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop vinyl records', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A 3 words button text to go to the sale page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Discount Banner',
|
||||
'slug' => 'woocommerce-blocks/discount-banner',
|
||||
'content' => [
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Select products', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words description of the products on sale', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Discount Banner with Image',
|
||||
'slug' => 'woocommerce-blocks/discount-banner-with-image',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Select products', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words description of the products on sale', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Featured Category Focus',
|
||||
'slug' => 'woocommerce-blocks/featured-category-focus',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Black and white high-quality prints', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The four words title of the featured category related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop prints', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the featured category', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Featured Category Triple',
|
||||
'slug' => 'woocommerce-blocks/featured-category-triple',
|
||||
'images_total' => 3,
|
||||
'images_format' => 'portrait',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Home decor', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A one-word graphic title that encapsulates the essence of the business, inspired by the following image description: [image.0] and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Retro photography', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two-words graphic title that encapsulates the essence of the business, inspired by the following image description: [image.1] and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Handmade gifts', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two-words graphic title that encapsulates the essence of the business, inspired by the following image description: [image.2] and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Featured Products: Fresh & Tasty',
|
||||
'slug' => 'woocommerce-blocks/featured-products-fresh-and-tasty',
|
||||
'images_total' => 4,
|
||||
'images_format' => 'portrait',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Fresh & tasty goods', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The title of the featured products with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Sweet Organic Lemons', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The three words description of the featured product related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Fresh Organic Tomatoes', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The three words description of the featured product related to the following image description: [image.1]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Fresh Lettuce (Washed)', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The three words description of the featured product related to the following image description: [image.2]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Russet Organic Potatoes', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The three words description of the featured product related to the following image description: [image.3]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Hero Product 3 Split',
|
||||
'slug' => 'woocommerce-blocks/hero-product-3-split',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'portrait',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Timeless elegance', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a two words title for advertising the store', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Durable glass', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a two words title for advertising the store', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Versatile charm', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a two words title for advertising the store', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'New: Retro Glass Jug', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a title with less than 20 characters for advertising the store', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Elevate your table with a 330ml Retro Glass Jug, blending classic design and durable hardened glass.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a text with approximately 130 characters, to describe a product the business is selling', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Crafted from resilient thick glass, this jug ensures lasting quality, making it perfect for everyday use with a touch of vintage charm.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a text with approximately 130 characters, to describe a product the business is selling', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( "The Retro Glass Jug's classic silhouette effortlessly complements any setting, making it the ideal choice for serving beverages with style and flair.", 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a long text, with at least 130 characters, to describe a product the business is selling', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Hero Product Chessboard',
|
||||
'slug' => 'woocommerce-blocks/hero-product-chessboard',
|
||||
'images_total' => 2,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Quality Materials', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title describing the first displayed product feature', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Unique design', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title describing the second displayed product feature', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Make your house feel like home', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title describing the fourth displayed product feature', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'We use only the highest-quality materials in our products, ensuring that they look great and last for years to come.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the product feature with at least 115 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'From bold prints to intricate details, our products are a perfect combination of style and function.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the product feature with at least 115 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Add a touch of charm and coziness this holiday season with a wide selection of hand-picked decorations — from minimalist vases to designer furniture.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the product feature with at least 115 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop home decor', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Hero Product Split',
|
||||
'slug' => 'woocommerce-blocks/hero-product-split',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Keep dry with 50% off rain jackets', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the product the store is selling with at least 35 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Just Arrived Full Hero',
|
||||
'slug' => 'woocommerce-blocks/just-arrived-full-hero',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Sound like no other', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the displayed product collection with at least 10 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Experience your music like never before with our latest generation of hi-fidelity headphones.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the product collection with at least 35 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop now', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product collection page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collection Banner',
|
||||
'slug' => 'woocommerce-blocks/product-collection-banner',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Brand New for the Holidays', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the displayed product collection with at least 25 characters related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Check out our brand new collection of holiday products and find the right gift for anyone.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the product collection with at least 90 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collections Featured Collection',
|
||||
'slug' => 'woocommerce-blocks/product-collections-featured-collection',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => "This week's popular products",
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the displayed product collection with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collections Featured Collections',
|
||||
'slug' => 'woocommerce-blocks/product-collections-featured-collections',
|
||||
'images_total' => 4,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Tech gifts under $100', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the product collection with at least 20 characters related to the following image descriptions: [image.0], [image.1]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'For the gamers', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the product collection with at least 15 characters related to the following image descriptions: [image.2], [image.3]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop tech', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product collection page', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Shop games', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product collection page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collections Newest Arrivals',
|
||||
'slug' => 'woocommerce-blocks/product-collections-newest-arrivals',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Our newest arrivals', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the displayed product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'More new products', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The button text to go to the product collection page with at least 15 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collection 4 Columns',
|
||||
'slug' => 'woocommerce-blocks/product-collection-4-columns',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Staff picks', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the displayed product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collection 5 Columns',
|
||||
'slug' => 'woocommerce-blocks/product-collection-5-columns',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Our latest and greatest', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase with that advertises the product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Gallery',
|
||||
'slug' => 'woocommerce-blocks/product-query-product-gallery',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Bestsellers', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the featured products with at least 10 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Featured Products 2 Columns',
|
||||
'slug' => 'woocommerce-blocks/featured-products-2-cols',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Fan favorites', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the featured products with at least 10 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Get ready to start the season right. All the fan favorites in one place at the best price.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the featured products with at least 90 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop All', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the featured products page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Hero 2 Column 2 Row',
|
||||
'slug' => 'woocommerce-blocks/product-hero-2-col-2-row',
|
||||
'images_total' => 2,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'The Eden Jacket', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three words title that advertises a product related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( '100% Woolen', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title that advertises a product feature', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Fits your wardrobe', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three words title that advertises a product feature', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Versatile', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An one word title that advertises a product feature', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Normal Fit', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title that advertises a product feature', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Perfect for any look featuring a mid-rise, relax fitting silhouette.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The description of a product with at least 65 characters related to the following image: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Reflect your fashionable style.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The description of a product feature with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Half tuck into your pants or layer over.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The description of a product feature with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Button-down front for any type of mood or look.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The description of a product feature with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( '42% Cupro 34% Linen 24% Viscose', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'The description of a product feature with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'View product', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Shop by Price',
|
||||
'slug' => 'woocommerce-blocks/shop-by-price',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Outdoor Furniture & Accessories', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the first product collection with at least 30 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Summer Dinning', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the second product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => "Women's Styles",
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the third product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => "Kids' Styles",
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the fourth product collection with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Small Discount Banner with Image',
|
||||
'slug' => 'woocommerce-blocks/small-discount-banner-with-image',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Chairs', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A single word that advertises the product and is related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Social: Follow us on social media',
|
||||
'slug' => 'woocommerce-blocks/social-follow-us-in-social-media',
|
||||
'images_total' => 4,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Stay in the loop', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A phrase that advertises the social media accounts of the store with at least 25 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Alternating Image and Text',
|
||||
'slug' => 'woocommerce-blocks/alt-image-and-text',
|
||||
'images_total' => 2,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Our products', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words impact phrase that advertises the products', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Sustainable blends, stylish accessories', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the products with at least 40 characters and related to the following image description: [image.0]', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'About us', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words impact phrase that advertises the brand', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Committed to a greener lifestyle', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the brand with at least 50 characters related to the following image description: [image.1]', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Indulge in the finest organic coffee beans, teas, and hand-picked accessories, all locally sourced and sustainable for a mindful lifestyle.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the products with at least 180 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => "Our passion is crafting mindful moments with locally sourced, organic, and sustainable products. We're more than a store; we're your path to a community-driven, eco-friendly lifestyle that embraces premium quality.",
|
||||
'ai_prompt' => __( 'A description of the products with at least 180 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Locally sourced ingredients', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three word description of the products', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Premium organic blends', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three word description of the products', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Hand-picked accessories', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three word description of the products', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Sustainable business practices', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A three word description of the products', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Meet us', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the product page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Testimonials 3 Columns',
|
||||
'slug' => 'woocommerce-blocks/testimonials-3-columns',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Eclectic finds, ethical delights', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a short title advertising a testimonial from a customer', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'Sip, Shop, Savor', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a short title advertising a testimonial from a customer', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'LOCAL LOVE', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write a short title advertising a testimonial from a customer', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'What our customers say', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write just 4 words to advertise testimonials from customers', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Transformed my daily routine with unique, eco-friendly treasures. Exceptional quality and service. Proud to support a store that aligns with my values.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write the testimonial from a customer with approximately 150 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'The organic coffee beans are a revelation. Each sip feels like a journey. Beautifully crafted accessories add a touch of elegance to my home.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write the testimonial from a customer with approximately 150 characters', 'woocommerce' ),
|
||||
],
|
||||
[
|
||||
'default' => __( 'From sustainably sourced teas to chic vases, this store is a treasure trove. Love knowing my purchases contribute to a greener planet.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'Write the testimonial from a customer with approximately 150 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Testimonials Single',
|
||||
'slug' => 'woocommerce-blocks/testimonials-single',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'A ‘brewtiful’ experience :-)', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words title that advertises the testimonial', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'Exceptional flavors, sustainable choices. The carefully curated collection of coffee pots and accessories turned my kitchen into a haven of style and taste.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description of the testimonial with at least 225 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Featured Category Cover Image',
|
||||
'slug' => 'woocommerce-blocks/featured-category-cover-image',
|
||||
'images_total' => 1,
|
||||
'images_format' => 'landscape',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Sit back and relax', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A description for a product with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'descriptions' => [
|
||||
[
|
||||
'default' => __( 'With a wide range of designer chairs to elevate your living space.', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the products with at least 55 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
'buttons' => [
|
||||
[
|
||||
'default' => __( 'Shop chairs', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'A two words button text to go to the shop page', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Product Collection: Featured Products 5 Columns',
|
||||
'slug' => 'woocommerce-blocks/product-collection-featured-products-5-columns',
|
||||
'content' => [
|
||||
'titles' => [
|
||||
[
|
||||
'default' => __( 'Shop new arrivals', 'woocommerce' ),
|
||||
'ai_prompt' => __( 'An impact phrase that advertises the newest additions to the store with at least 20 characters', 'woocommerce' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class PatternsHelper {
|
||||
/**
|
||||
* Upsert the patterns AI data.
|
||||
*
|
||||
* @param array $patterns_dictionary The patterns dictionary.
|
||||
* @param array $patterns_dictionary The patterns' dictionary.
|
||||
*
|
||||
* @return WP_Error|null
|
||||
*/
|
||||
@@ -92,18 +92,12 @@ class PatternsHelper {
|
||||
* @return array|WP_Error Returns pattern dictionary or WP_Error on failure.
|
||||
*/
|
||||
public static function get_patterns_dictionary( $pattern_slug = null ) {
|
||||
$patterns_dictionary_file = plugin_dir_path( __FILE__ ) . 'dictionary.json';
|
||||
$default_patterns_dictionary = PatternsDictionary::get();
|
||||
|
||||
if ( ! file_exists( $patterns_dictionary_file ) ) {
|
||||
if ( empty( $default_patterns_dictionary ) ) {
|
||||
return new WP_Error( 'missing_patterns_dictionary', __( 'The patterns dictionary is missing.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$default_patterns_dictionary = wp_json_file_decode( $patterns_dictionary_file, array( 'associative' => true ) );
|
||||
|
||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||
return new WP_Error( 'json_decode_error', __( 'Error decoding JSON.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$patterns_dictionary = '';
|
||||
$ai_connection_allowed = get_option( 'woocommerce_blocks_allow_ai_connection' );
|
||||
|
||||
|
||||
@@ -402,13 +402,13 @@ class UpdatePatterns {
|
||||
* @return mixed|WP_Error|null
|
||||
*/
|
||||
public static function get_patterns_dictionary() {
|
||||
$patterns_dictionary = plugin_dir_path( __FILE__ ) . 'dictionary.json';
|
||||
$patterns_dictionary = PatternsDictionary::get();
|
||||
|
||||
if ( ! file_exists( $patterns_dictionary ) ) {
|
||||
if ( empty( $patterns_dictionary ) ) {
|
||||
return new WP_Error( 'missing_patterns_dictionary', __( 'The patterns dictionary is missing.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
return wp_json_file_decode( $patterns_dictionary, array( 'associative' => true ) );
|
||||
return $patterns_dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ class UpdateProducts {
|
||||
'price' => 249,
|
||||
],
|
||||
[
|
||||
'title' => 'Black and White Summer Portrait',
|
||||
'title' => 'Black and White',
|
||||
'image' => 'assets/images/pattern-placeholders/white-black-black-and-white-photograph-monochrome-photography.jpg',
|
||||
'description' => 'This 24" x 30" high-quality print just exudes summer. Hang it on the wall and forget about the world outside.',
|
||||
'price' => 115,
|
||||
@@ -113,7 +113,7 @@ class UpdateProducts {
|
||||
$products_to_create = max( 0, 6 - $real_products_count - $dummy_products_count );
|
||||
while ( $products_to_create > 0 ) {
|
||||
$this->create_new_product( self::DUMMY_PRODUCTS[ $products_to_create - 1 ] );
|
||||
$products_to_create--;
|
||||
--$products_to_create;
|
||||
}
|
||||
|
||||
// Identify dummy products that need to have their content updated.
|
||||
@@ -327,7 +327,7 @@ class UpdateProducts {
|
||||
public function assign_ai_selected_images_to_dummy_products( $dummy_products_to_update, $ai_selected_images ) {
|
||||
$products_information_list = [];
|
||||
$dummy_products_count = count( $dummy_products_to_update );
|
||||
for ( $i = 0; $i < $dummy_products_count; $i ++ ) {
|
||||
for ( $i = 0; $i < $dummy_products_count; $i++ ) {
|
||||
$image_src = $ai_selected_images[ $i ]['URL'] ?? '';
|
||||
|
||||
if ( wc_is_valid_url( $image_src ) ) {
|
||||
@@ -396,7 +396,7 @@ class UpdateProducts {
|
||||
$ai_request_retries = 0;
|
||||
$success = false;
|
||||
while ( $ai_request_retries < 5 && ! $success ) {
|
||||
$ai_request_retries ++;
|
||||
++$ai_request_retries;
|
||||
$ai_response = $ai_connection->fetch_ai_response( $token, $formatted_prompt, 30 );
|
||||
if ( is_wp_error( $ai_response ) ) {
|
||||
continue;
|
||||
@@ -464,7 +464,7 @@ class UpdateProducts {
|
||||
|
||||
$this->product_update( $product, $product_image_id, self::DUMMY_PRODUCTS[ $i ]['title'], self::DUMMY_PRODUCTS[ $i ]['description'], self::DUMMY_PRODUCTS[ $i ]['price'] );
|
||||
|
||||
$i++;
|
||||
++$i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,656 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Banner",
|
||||
"slug": "woocommerce-blocks/banner",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Up to 60% off",
|
||||
"ai_prompt": "A four words title advertising the sale"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Holiday Sale",
|
||||
"ai_prompt": "A two words label with the sale name"
|
||||
},
|
||||
{
|
||||
"default": "Get your favorite vinyl at record-breaking prices.",
|
||||
"ai_prompt": "The main description of the sale with at least 65 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop vinyl records",
|
||||
"ai_prompt": "A 3 words button text to go to the sale page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Discount Banner",
|
||||
"slug": "woocommerce-blocks/discount-banner",
|
||||
"content": {
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Select products",
|
||||
"ai_prompt": "A two words description of the products on sale"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Discount Banner with Image",
|
||||
"slug": "woocommerce-blocks/discount-banner-with-image",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Select products",
|
||||
"ai_prompt": "A two words description of the products on sale"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Featured Category Focus",
|
||||
"slug": "woocommerce-blocks/featured-category-focus",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Black and white high-quality prints",
|
||||
"ai_prompt": "The four words title of the featured category related to the following image description: {image.0}"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop prints",
|
||||
"ai_prompt": "A two words button text to go to the featured category"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Featured Category Triple",
|
||||
"slug": "woocommerce-blocks/featured-category-triple",
|
||||
"images_total": 3,
|
||||
"images_format": "portrait",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Home decor",
|
||||
"ai_prompt": "A one-word graphic title that encapsulates the essence of the business, inspired by the following image description: {image.0} and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image"
|
||||
},
|
||||
{
|
||||
"default": "Retro photography",
|
||||
"ai_prompt": "A two-words graphic title that encapsulates the essence of the business, inspired by the following image description: {image.1} and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image"
|
||||
},
|
||||
{
|
||||
"default": "Handmade gifts",
|
||||
"ai_prompt": "A two-words graphic title that encapsulates the essence of the business, inspired by the following image description: {image.2} and the nature of the business. The title should reflect the key elements and characteristics of the business, as portrayed in the image"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Featured Products: Fresh & Tasty",
|
||||
"slug": "woocommerce-blocks/featured-products-fresh-and-tasty",
|
||||
"images_total": 4,
|
||||
"images_format": "portrait",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Fresh & tasty goods",
|
||||
"ai_prompt": "The title of the featured products with at least 20 characters"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Sweet Organic Lemons",
|
||||
"ai_prompt": "The three words description of the featured product related to the following image description: {image.0}"
|
||||
},
|
||||
{
|
||||
"default": "Fresh Organic Tomatoes",
|
||||
"ai_prompt": "The three words description of the featured product related to the following image description: {image.1}"
|
||||
},
|
||||
{
|
||||
"default": "Fresh Lettuce (Washed)",
|
||||
"ai_prompt": "The three words description of the featured product related to the following image description: {image.2}"
|
||||
},
|
||||
{
|
||||
"default": "Russet Organic Potatoes",
|
||||
"ai_prompt": "The three words description of the featured product related to the following image description: {image.3}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Hero Product 3 Split",
|
||||
"slug": "woocommerce-blocks/hero-product-3-split",
|
||||
"images_total": 1,
|
||||
"images_format": "portrait",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Timeless elegance",
|
||||
"ai_prompt": "Write a two words title for advertising the store"
|
||||
},
|
||||
{
|
||||
"default": "Durable glass",
|
||||
"ai_prompt": "Write a two words title for advertising the store"
|
||||
},
|
||||
{
|
||||
"default": "Versatile charm",
|
||||
"ai_prompt": "Write a two words title for advertising the store"
|
||||
},
|
||||
{
|
||||
"default": "New: Retro Glass Jug",
|
||||
"ai_prompt": "Write a title with less than 20 characters for advertising the store"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Elevate your table with a 330ml Retro Glass Jug, blending classic design and durable hardened glass.",
|
||||
"ai_prompt": "Write a text with approximately 130 characters, to describe a product the business is selling"
|
||||
},
|
||||
{
|
||||
"default": "Crafted from resilient thick glass, this jug ensures lasting quality, making it perfect for everyday use with a touch of vintage charm.",
|
||||
"ai_prompt": "Write a text with approximately 130 characters, to describe a product the business is selling"
|
||||
},
|
||||
{
|
||||
"default": "The Retro Glass Jug's classic silhouette effortlessly complements any setting, making it the ideal choice for serving beverages with style and flair.",
|
||||
"ai_prompt": "Write a long text, with at least 130 characters, to describe a product the business is selling"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Hero Product Chessboard",
|
||||
"slug": "woocommerce-blocks/hero-product-chessboard",
|
||||
"images_total": 2,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Quality Materials",
|
||||
"ai_prompt": "A two words title describing the first displayed product feature"
|
||||
},
|
||||
{
|
||||
"default": "Unique design",
|
||||
"ai_prompt": "A two words title describing the second displayed product feature"
|
||||
},
|
||||
{
|
||||
"default": "Make your house feel like home",
|
||||
"ai_prompt": "A two words title describing the fourth displayed product feature"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "We use only the highest-quality materials in our products, ensuring that they look great and last for years to come.",
|
||||
"ai_prompt": "A description of the product feature with at least 115 characters"
|
||||
},
|
||||
{
|
||||
"default": "From bold prints to intricate details, our products are a perfect combination of style and function.",
|
||||
"ai_prompt": "A description of the product feature with at least 115 characters"
|
||||
},
|
||||
{
|
||||
"default": "Add a touch of charm and coziness this holiday season with a wide selection of hand-picked decorations — from minimalist vases to designer furniture.",
|
||||
"ai_prompt": "A description of the product feature with at least 115 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop home decor",
|
||||
"ai_prompt": "A two words button text to go to the product page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Hero Product Split",
|
||||
"slug": "woocommerce-blocks/hero-product-split",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Keep dry with 50% off rain jackets",
|
||||
"ai_prompt": "An impact phrase that advertises the product the store is selling with at least 35 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Just Arrived Full Hero",
|
||||
"slug": "woocommerce-blocks/just-arrived-full-hero",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Sound like no other",
|
||||
"ai_prompt": "An impact phrase that advertises the displayed product collection with at least 10 characters"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Experience your music like never before with our latest generation of hi-fidelity headphones.",
|
||||
"ai_prompt": "A description of the product collection with at least 35 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop now",
|
||||
"ai_prompt": "A two words button text to go to the product collection page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collection Banner",
|
||||
"slug": "woocommerce-blocks/product-collection-banner",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Brand New for the Holidays",
|
||||
"ai_prompt": "An impact phrase that advertises the displayed product collection with at least 25 characters related to the following image description: {image.0}"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Check out our brand new collection of holiday products and find the right gift for anyone.",
|
||||
"ai_prompt": "A description of the product collection with at least 90 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collections Featured Collection",
|
||||
"slug": "woocommerce-blocks/product-collections-featured-collection",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "This week's popular products",
|
||||
"ai_prompt": "An impact phrase that advertises the displayed product collection with at least 30 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collections Featured Collections",
|
||||
"slug": "woocommerce-blocks/product-collections-featured-collections",
|
||||
"images_total": 4,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Tech gifts under $100",
|
||||
"ai_prompt": "An impact phrase that advertises the product collection with at least 20 characters related to the following image descriptions: {image.0}, {image.1}"
|
||||
},
|
||||
{
|
||||
"default": "For the gamers",
|
||||
"ai_prompt": "An impact phrase that advertises the product collection with at least 15 characters related to the following image descriptions: {image.2}, {image.3}"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop tech",
|
||||
"ai_prompt": "A two words button text to go to the product collection page"
|
||||
},
|
||||
{
|
||||
"default": "Shop games",
|
||||
"ai_prompt": "A two words button text to go to the product collection page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collections Newest Arrivals",
|
||||
"slug": "woocommerce-blocks/product-collections-newest-arrivals",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Our newest arrivals",
|
||||
"ai_prompt": "An impact phrase that advertises the displayed product collection with at least 20 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "More new products",
|
||||
"ai_prompt": "The button text to go to the product collection page with at least 15 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collection 4 Columns",
|
||||
"slug": "woocommerce-blocks/product-collection-4-columns",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Staff picks",
|
||||
"ai_prompt": "An impact phrase that advertises the displayed product collection with at least 20 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collection 5 Columns",
|
||||
"slug": "woocommerce-blocks/product-collection-5-columns",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Our latest and greatest",
|
||||
"ai_prompt": "An impact phrase with that advertises the product collection with at least 20 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Gallery",
|
||||
"slug": "woocommerce-blocks/product-query-product-gallery",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Bestsellers",
|
||||
"ai_prompt": "An impact phrase that advertises the featured products with at least 10 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Featured Products 2 Columns",
|
||||
"slug": "woocommerce-blocks/featured-products-2-cols",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Fan favorites",
|
||||
"ai_prompt": "An impact phrase that advertises the featured products with at least 10 characters"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Get ready to start the season right. All the fan favorites in one place at the best price.",
|
||||
"ai_prompt": "A description of the featured products with at least 90 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop All",
|
||||
"ai_prompt": "A two words button text to go to the featured products page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Hero 2 Column 2 Row",
|
||||
"slug": "woocommerce-blocks/product-hero-2-col-2-row",
|
||||
"images_total": 2,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "The Eden Jacket",
|
||||
"ai_prompt": "A three words title that advertises a product related to the following image description: {image.0}"
|
||||
},
|
||||
{
|
||||
"default": "100% Woolen",
|
||||
"ai_prompt": "A two words title that advertises a product feature"
|
||||
},
|
||||
{
|
||||
"default": "Fits your wardrobe",
|
||||
"ai_prompt": "A three words title that advertises a product feature"
|
||||
},
|
||||
{
|
||||
"default": "Versatile",
|
||||
"ai_prompt": "An one word title that advertises a product feature"
|
||||
},
|
||||
{
|
||||
"default": "Normal Fit",
|
||||
"ai_prompt": "A two words title that advertises a product feature"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Perfect for any look featuring a mid-rise, relax fitting silhouette.",
|
||||
"ai_prompt": "The description of a product with at least 65 characters related to the following image: {image.0}"
|
||||
},
|
||||
{
|
||||
"default": "Reflect your fashionable style.",
|
||||
"ai_prompt": "The description of a product feature with at least 30 characters"
|
||||
},
|
||||
{
|
||||
"default": "Half tuck into your pants or layer over.",
|
||||
"ai_prompt": "The description of a product feature with at least 30 characters"
|
||||
},
|
||||
{
|
||||
"default": "Button-down front for any type of mood or look.",
|
||||
"ai_prompt": "The description of a product feature with at least 30 characters"
|
||||
},
|
||||
{
|
||||
"default": "42% Cupro 34% Linen 24% Viscose",
|
||||
"ai_prompt": "The description of a product feature with at least 30 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "View product",
|
||||
"ai_prompt": "A two words button text to go to the product page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Shop by Price",
|
||||
"slug": "woocommerce-blocks/shop-by-price",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Outdoor Furniture & Accessories",
|
||||
"ai_prompt": "An impact phrase that advertises the first product collection with at least 30 characters"
|
||||
},
|
||||
{
|
||||
"default": "Summer Dinning",
|
||||
"ai_prompt": "An impact phrase that advertises the second product collection with at least 20 characters"
|
||||
},
|
||||
{
|
||||
"default": "Women's Styles",
|
||||
"ai_prompt": "An impact phrase that advertises the third product collection with at least 20 characters"
|
||||
},
|
||||
{
|
||||
"default": "Kids' Styles",
|
||||
"ai_prompt": "An impact phrase that advertises the fourth product collection with at least 20 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Small Discount Banner with Image",
|
||||
"slug": "woocommerce-blocks/small-discount-banner-with-image",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Chairs",
|
||||
"ai_prompt": "A single word that advertises the product and is related to the following image description: {image.0}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Social: Follow us on social media",
|
||||
"slug": "woocommerce-blocks/social-follow-us-in-social-media",
|
||||
"images_total": 4,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Stay in the loop",
|
||||
"ai_prompt": "A phrase that advertises the social media accounts of the store with at least 25 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Alternating Image and Text",
|
||||
"slug": "woocommerce-blocks/alt-image-and-text",
|
||||
"images_total": 2,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Our products",
|
||||
"ai_prompt": "A two words impact phrase that advertises the products"
|
||||
},
|
||||
{
|
||||
"default": "Sustainable blends, stylish accessories",
|
||||
"ai_prompt": "An impact phrase that advertises the products with at least 40 characters and related to the following image description: {image.0}"
|
||||
},
|
||||
{
|
||||
"default": "About us",
|
||||
"ai_prompt": "A two words impact phrase that advertises the brand"
|
||||
},
|
||||
{
|
||||
"default": "Committed to a greener lifestyle",
|
||||
"ai_prompt": "An impact phrase that advertises the brand with at least 50 characters related to the following image description: {image.1}"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Indulge in the finest organic coffee beans, teas, and hand-picked accessories, all locally sourced and sustainable for a mindful lifestyle.",
|
||||
"ai_prompt": "A description of the products with at least 180 characters"
|
||||
},
|
||||
{
|
||||
"default": "Our passion is crafting mindful moments with locally sourced, organic, and sustainable products. We're more than a store; we're your path to a community-driven, eco-friendly lifestyle that embraces premium quality.",
|
||||
"ai_prompt": "A description of the products with at least 180 characters"
|
||||
},
|
||||
{
|
||||
"default": "Locally sourced ingredients",
|
||||
"ai_prompt": "A three word description of the products"
|
||||
},
|
||||
{
|
||||
"default": "Premium organic blends",
|
||||
"ai_prompt": "A three word description of the products"
|
||||
},
|
||||
{
|
||||
"default": "Hand-picked accessories",
|
||||
"ai_prompt": "A three word description of the products"
|
||||
},
|
||||
{
|
||||
"default": "Sustainable business practices",
|
||||
"ai_prompt": "A three word description of the products"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Meet us",
|
||||
"ai_prompt": "A two words button text to go to the product page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Testimonials 3 Columns",
|
||||
"slug": "woocommerce-blocks/testimonials-3-columns",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Eclectic finds, ethical delights",
|
||||
"ai_prompt": "Write a short title advertising a testimonial from a customer"
|
||||
},
|
||||
{
|
||||
"default": "Sip, Shop, Savor",
|
||||
"ai_prompt": "Write a short title advertising a testimonial from a customer"
|
||||
},
|
||||
{
|
||||
"default": "LOCAL LOVE",
|
||||
"ai_prompt": "Write a short title advertising a testimonial from a customer"
|
||||
},
|
||||
{
|
||||
"default": "What our customers say",
|
||||
"ai_prompt": "Write just 4 words to advertise testimonials from customers"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Transformed my daily routine with unique, eco-friendly treasures. Exceptional quality and service. Proud to support a store that aligns with my values.",
|
||||
"ai_prompt": "Write the testimonial from a customer with approximately 150 characters"
|
||||
},
|
||||
{
|
||||
"default": "The organic coffee beans are a revelation. Each sip feels like a journey. Beautifully crafted accessories add a touch of elegance to my home.",
|
||||
"ai_prompt": "Write the testimonial from a customer with approximately 150 characters"
|
||||
},
|
||||
{
|
||||
"default": "From sustainably sourced teas to chic vases, this store is a treasure trove. Love knowing my purchases contribute to a greener planet.",
|
||||
"ai_prompt": "Write the testimonial from a customer with approximately 150 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Testimonials Single",
|
||||
"slug": "woocommerce-blocks/testimonials-single",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "A ‘brewtiful’ experience :-)",
|
||||
"ai_prompt": "A two words title that advertises the testimonial"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "Exceptional flavors, sustainable choices. The carefully curated collection of coffee pots and accessories turned my kitchen into a haven of style and taste.",
|
||||
"ai_prompt": "A description of the testimonial with at least 225 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Featured Category Cover Image",
|
||||
"slug": "woocommerce-blocks/featured-category-cover-image",
|
||||
"images_total": 1,
|
||||
"images_format": "landscape",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Sit back and relax",
|
||||
"ai_prompt": "A description for a product with at least 20 characters"
|
||||
}
|
||||
],
|
||||
"descriptions": [
|
||||
{
|
||||
"default": "With a wide range of designer chairs to elevate your living space.",
|
||||
"ai_prompt": "An impact phrase that advertises the products with at least 55 characters"
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"default": "Shop chairs",
|
||||
"ai_prompt": "A two words button text to go to the shop page"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Product Collection: Featured Products 5 Columns",
|
||||
"slug": "woocommerce-blocks/product-collection-featured-products-5-columns",
|
||||
"content": {
|
||||
"titles": [
|
||||
{
|
||||
"default": "Shop new arrivals",
|
||||
"ai_prompt": "An impact phrase that advertises the newest additions to the store with at least 20 characters"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -40,6 +40,7 @@ final class AssetsController {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'update_block_style_dependencies' ), 20 );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
|
||||
add_filter( 'js_do_concat', array( $this, 'skip_boost_minification_for_cart_checkout' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,9 +63,14 @@ final class AssetsController {
|
||||
// The price package is shared externally so has no blocks prefix.
|
||||
$this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false );
|
||||
|
||||
$this->api->register_script( 'wc-blocks-vendors-frontend', $this->api->get_block_asset_build_path( 'wc-blocks-vendors-frontend' ), array(), false );
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
// Vendor scripts for blocks frontends (not including cart and checkout).
|
||||
$this->api->register_script( 'wc-blocks-frontend-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-frontend-vendors-frontend' ), array(), true );
|
||||
|
||||
// Cart and checkout frontend scripts.
|
||||
$this->api->register_script( 'wc-cart-checkout-vendors', $this->api->get_block_asset_build_path( 'wc-cart-checkout-vendors-frontend' ), array(), true );
|
||||
$this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), true );
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js' );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' );
|
||||
|
||||
// Register the interactivity components here for now.
|
||||
$this->api->register_script( 'wc-interactivity-dropdown', 'assets/client/blocks/wc-interactivity-dropdown.js', array() );
|
||||
@@ -253,6 +259,23 @@ final class AssetsController {
|
||||
return $src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip Jetpack Boost minification on older versions of Jetpack Boost where it causes issues.
|
||||
*
|
||||
* @param mixed $do_concat Whether to concatenate the script or not.
|
||||
* @param mixed $handle The script handle.
|
||||
* @return mixed
|
||||
*/
|
||||
public function skip_boost_minification_for_cart_checkout( $do_concat, $handle ) {
|
||||
$boost_is_outdated = defined( 'JETPACK_BOOST_VERSION' ) && version_compare( JETPACK_BOOST_VERSION, '3.4.2', '<' );
|
||||
$scripts_to_ignore = [
|
||||
'wc-cart-checkout-vendors',
|
||||
'wc-cart-checkout-base',
|
||||
];
|
||||
|
||||
return $boost_is_outdated && in_array( $handle, $scripts_to_ignore, true ) ? false : $do_concat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add body classes to the frontend and within admin.
|
||||
*
|
||||
|
||||
@@ -34,6 +34,8 @@ use WP_Error;
|
||||
* @internal
|
||||
*/
|
||||
class BlockPatterns {
|
||||
const CATEGORIES_PREFIXES = [ '_woo_', '_dotcom_imported_' ];
|
||||
|
||||
/**
|
||||
* Path to the patterns' directory.
|
||||
*
|
||||
@@ -145,6 +147,8 @@ class BlockPatterns {
|
||||
return;
|
||||
}
|
||||
|
||||
$patterns = $this->parse_categories( $patterns );
|
||||
|
||||
foreach ( $patterns as $pattern ) {
|
||||
$pattern['slug'] = $pattern['name'];
|
||||
$pattern['content'] = $pattern['html'];
|
||||
@@ -152,4 +156,33 @@ class BlockPatterns {
|
||||
$this->pattern_registry->register_block_pattern( $pattern['ID'], $pattern, $this->dictionary );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse prefixed categories from the PTK patterns into the actual WooCommerce categories.
|
||||
*
|
||||
* @param array $patterns The patterns to parse.
|
||||
* @return array The parsed patterns.
|
||||
*/
|
||||
private function parse_categories( array $patterns ) {
|
||||
return array_map(
|
||||
function ( $pattern ) {
|
||||
$pattern['categories'] = array_map(
|
||||
function ( $category ) {
|
||||
foreach ( self::CATEGORIES_PREFIXES as $prefix ) {
|
||||
if ( strpos( $category['title'], $prefix ) !== false ) {
|
||||
$parsed_category = str_replace( $prefix, '', $category['title'] );
|
||||
$parsed_category = str_replace( '_', ' ', $parsed_category );
|
||||
$category['title'] = ucfirst( $parsed_category );
|
||||
}
|
||||
}
|
||||
|
||||
return $category;
|
||||
},
|
||||
$pattern['categories']
|
||||
);
|
||||
return $pattern;
|
||||
},
|
||||
$patterns
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,76 +28,9 @@ class BlockTemplatesController {
|
||||
add_filter( 'get_block_template', array( $this, 'add_block_template_details' ), 10, 3 );
|
||||
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
|
||||
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 );
|
||||
add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 );
|
||||
|
||||
if ( wc_current_theme_is_fse_theme() ) {
|
||||
// By default, the Template Part Block only supports template parts that are in the current theme directory.
|
||||
// This render_callback wrapper allows us to add support for plugin-housed template parts.
|
||||
add_filter(
|
||||
'block_type_metadata_settings',
|
||||
function ( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/template-part' === $metadata['name'] &&
|
||||
in_array( $settings['render_callback'], array( 'render_block_core_template_part', 'gutenberg_render_block_core_template_part' ), true )
|
||||
) {
|
||||
$settings['render_callback'] = array( $this, 'render_woocommerce_template_part' );
|
||||
}
|
||||
return $settings;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
// Prevents shortcodes in templates having their HTML content broken by wpautop.
|
||||
// @see https://core.trac.wordpress.org/ticket/58366 for more info.
|
||||
add_filter(
|
||||
'block_type_metadata_settings',
|
||||
function ( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/shortcode' === $metadata['name']
|
||||
) {
|
||||
$settings['original_render_callback'] = $settings['render_callback'];
|
||||
$settings['render_callback'] = function ( $attributes, $content ) use ( $settings ) {
|
||||
// The shortcode has already been rendered, so look for the cart/checkout HTML.
|
||||
if ( strstr( $content, 'woocommerce-cart-form' ) || strstr( $content, 'wc-empty-cart-message' ) || strstr( $content, 'woocommerce-checkout-form' ) ) {
|
||||
// Return early before wpautop runs again.
|
||||
return $content;
|
||||
}
|
||||
|
||||
$render_callback = $settings['original_render_callback'];
|
||||
|
||||
return $render_callback( $attributes, $content );
|
||||
};
|
||||
}
|
||||
return $settings;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
/**
|
||||
* Prevents the pages that are assigned as cart/checkout from showing the "template" selector in the page-editor.
|
||||
* We want to avoid this flow and point users towards the site editor instead.
|
||||
*/
|
||||
add_action(
|
||||
'current_screen',
|
||||
function () {
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( $current_screen && 'page' === $current_screen->id && ! empty( $_GET['post'] ) && in_array( absint( $_GET['post'] ), array( wc_get_page_id( 'cart' ), wc_get_page_id( 'checkout' ) ), true ) ) {
|
||||
wp_add_inline_style( 'wc-blocks-editor-style', '.edit-post-post-template { display: none; }' );
|
||||
}
|
||||
},
|
||||
10
|
||||
);
|
||||
}
|
||||
add_filter( 'block_type_metadata_settings', array( $this, 'add_plugin_templates_parts_support' ), 10, 2 );
|
||||
add_filter( 'block_type_metadata_settings', array( $this, 'prevent_shortcodes_html_breakage' ), 10, 2 );
|
||||
add_action( 'current_screen', array( $this, 'hide_template_selector_in_cart_checkout_pages' ), 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,22 +147,69 @@ class BlockTemplatesController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the old and current themes and determines if the "wc_blocks_use_blockified_product_grid_block_as_template"
|
||||
* option need to be updated accordingly.
|
||||
* By default, the Template Part Block only supports template parts that are in the current theme directory.
|
||||
* This render_callback wrapper allows us to add support for plugin-housed template parts.
|
||||
*
|
||||
* @param array $settings Array of determined settings for registering a block type.
|
||||
* @param array $metadata Metadata provided for registering a block type.
|
||||
*/
|
||||
public function add_plugin_templates_parts_support( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/template-part' === $metadata['name'] &&
|
||||
in_array( $settings['render_callback'], array( 'render_block_core_template_part', 'gutenberg_render_block_core_template_part' ), true )
|
||||
) {
|
||||
$settings['render_callback'] = array( $this, 'render_woocommerce_template_part' );
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevents shortcodes in templates having their HTML content broken by wpautop.
|
||||
*
|
||||
* @see https://core.trac.wordpress.org/ticket/58366 for more info.
|
||||
*
|
||||
* @param array $settings Array of determined settings for registering a block type.
|
||||
* @param array $metadata Metadata provided for registering a block type.
|
||||
*/
|
||||
public function prevent_shortcodes_html_breakage( $settings, $metadata ) {
|
||||
if (
|
||||
isset( $metadata['name'], $settings['render_callback'] ) &&
|
||||
'core/shortcode' === $metadata['name']
|
||||
) {
|
||||
$settings['original_render_callback'] = $settings['render_callback'];
|
||||
$settings['render_callback'] = function ( $attributes, $content ) use ( $settings ) {
|
||||
// The shortcode has already been rendered, so look for the cart/checkout HTML.
|
||||
if ( strstr( $content, 'woocommerce-cart-form' ) || strstr( $content, 'wc-empty-cart-message' ) || strstr( $content, 'woocommerce-checkout-form' ) ) {
|
||||
// Return early before wpautop runs again.
|
||||
return $content;
|
||||
}
|
||||
|
||||
$render_callback = $settings['original_render_callback'];
|
||||
|
||||
return $render_callback( $attributes, $content );
|
||||
};
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the pages that are assigned as Cart/Checkout from showing the "template" selector in the page-editor.
|
||||
* We want to avoid this flow and point users towards the Site Editor instead.
|
||||
*
|
||||
* @param string $old_name Old theme name.
|
||||
* @param \WP_Theme $old_theme Instance of the old theme.
|
||||
* @return void
|
||||
*/
|
||||
public function check_should_use_blockified_product_grid_templates( $old_name, $old_theme ) {
|
||||
if ( ! wc_current_theme_is_fse_theme() ) {
|
||||
update_option( Options::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE, wc_bool_to_string( false ) );
|
||||
public function hide_template_selector_in_cart_checkout_pages() {
|
||||
if ( ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $old_theme->is_block_theme() && wc_current_theme_is_fse_theme() ) {
|
||||
update_option( Options::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE, wc_bool_to_string( true ) );
|
||||
return;
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( $current_screen && 'page' === $current_screen->id && ! empty( $_GET['post'] ) && in_array( absint( $_GET['post'] ), array( wc_get_page_id( 'cart' ), wc_get_page_id( 'checkout' ) ), true ) ) {
|
||||
wp_add_inline_style( 'wc-blocks-editor-style', '.edit-post-post-template { display: none; }' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,16 +335,26 @@ class BlockTemplatesController {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_not_custom = false === array_search(
|
||||
$possible_template_ids = [
|
||||
$theme_slug . '//' . $template_file->slug,
|
||||
array_column( $query_result, 'id' ),
|
||||
true
|
||||
);
|
||||
$theme_slug . '//' . BlockTemplateUtils::DIRECTORY_NAMES['TEMPLATE_PARTS'] . '/' . $template_file->slug,
|
||||
$theme_slug . '//' . BlockTemplateUtils::DIRECTORY_NAMES['DEPRECATED_TEMPLATE_PARTS'] . '/' . $template_file->slug,
|
||||
];
|
||||
|
||||
$is_custom = false;
|
||||
$query_result_template_ids = array_column( $query_result, 'id' );
|
||||
|
||||
foreach ( $possible_template_ids as $template_id ) {
|
||||
if ( in_array( $template_id, $query_result_template_ids, true ) ) {
|
||||
$is_custom = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$fits_slug_query =
|
||||
! isset( $query['slug__in'] ) || in_array( $template_file->slug, $query['slug__in'], true );
|
||||
$fits_area_query =
|
||||
! isset( $query['area'] ) || ( property_exists( $template_file, 'area' ) && $template_file->area === $query['area'] );
|
||||
$should_include = $is_not_custom && $fits_slug_query && $fits_area_query;
|
||||
$should_include = ! $is_custom && $fits_slug_query && $fits_area_query;
|
||||
if ( $should_include ) {
|
||||
$template = BlockTemplateUtils::build_template_result_from_file( $template_file, $template_type );
|
||||
$query_result[] = $template;
|
||||
@@ -469,14 +459,6 @@ class BlockTemplatesController {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point the template only exists in the Blocks filesystem, if is a taxonomy-product_cat/tag/attribute.html template
|
||||
// let's use the archive-product.html template from Blocks.
|
||||
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug ) ) {
|
||||
$template_file = $this->get_template_path_from_woocommerce( ProductCatalogTemplate::SLUG );
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, false );
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point the template only exists in the Blocks filesystem and has not been saved in the DB,
|
||||
// or superseded by the theme.
|
||||
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug );
|
||||
@@ -500,18 +482,6 @@ class BlockTemplatesController {
|
||||
return array_merge( $templates_from_db, $templates_from_woo );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of a template on the Blocks template folder.
|
||||
*
|
||||
* @param string $template_slug Block template slug e.g. single-product.
|
||||
* @param string $template_type wp_template or wp_template_part.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_path_from_woocommerce( $template_slug, $template_type = 'wp_template' ) {
|
||||
return BlockTemplateUtils::get_templates_directory( $template_type ) . '/' . $template_slug . '.html';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a block template with that name exists in Woo Blocks
|
||||
*
|
||||
|
||||
@@ -19,6 +19,7 @@ use Automattic\WooCommerce\Blocks\Templates\ProductCategoryTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductTagTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductFiltersTemplate;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ProductFiltersOverlayTemplate;
|
||||
|
||||
/**
|
||||
@@ -59,10 +60,14 @@ class BlockTemplatesRegistry {
|
||||
}
|
||||
if ( BlockTemplateUtils::supports_block_templates( 'wp_template_part' ) ) {
|
||||
$template_parts = array(
|
||||
MiniCartTemplate::SLUG => new MiniCartTemplate(),
|
||||
CheckoutHeaderTemplate::SLUG => new CheckoutHeaderTemplate(),
|
||||
ProductFiltersOverlayTemplate::SLUG => new ProductFiltersOverlayTemplate(),
|
||||
MiniCartTemplate::SLUG => new MiniCartTemplate(),
|
||||
CheckoutHeaderTemplate::SLUG => new CheckoutHeaderTemplate(),
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'experimental-blocks' ) ) {
|
||||
$template_parts[ ProductFiltersTemplate::SLUG ] = new ProductFiltersTemplate();
|
||||
$template_parts[ ProductFiltersOverlayTemplate::SLUG ] = new ProductFiltersOverlayTemplate();
|
||||
}
|
||||
} else {
|
||||
$template_parts = array();
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ class Checkout extends AbstractBlock {
|
||||
// This prevents the page redirecting when the cart is empty. This is so the editor still loads the page preview.
|
||||
add_filter(
|
||||
'woocommerce_checkout_redirect_empty_cart',
|
||||
function( $return ) {
|
||||
function ( $redirect_empty_cart ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return isset( $_GET['_wp-find-template'] ) ? false : $return;
|
||||
return isset( $_GET['_wp-find-template'] ) ? false : $redirect_empty_cart;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -94,10 +94,17 @@ class Checkout extends AbstractBlock {
|
||||
* @return array|string
|
||||
*/
|
||||
protected function get_block_type_script( $key = null ) {
|
||||
$dependencies = [];
|
||||
|
||||
// Load password strength meter script asynchronously if needed.
|
||||
if ( ! is_user_logged_in() && 'no' === get_option( 'woocommerce_registration_generate_password' ) ) {
|
||||
$dependencies[] = 'zxcvbn-async';
|
||||
}
|
||||
|
||||
$script = [
|
||||
'handle' => 'wc-' . $this->block_name . '-block-frontend',
|
||||
'path' => $this->asset_api->get_block_asset_build_path( $this->block_name . '-frontend' ),
|
||||
'dependencies' => [],
|
||||
'dependencies' => $dependencies,
|
||||
];
|
||||
return $key ? $script[ $key ] : $script;
|
||||
}
|
||||
@@ -354,6 +361,7 @@ class Checkout extends AbstractBlock {
|
||||
$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ) );
|
||||
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ) );
|
||||
$this->asset_data_registry->add( 'forcedBillingAddress', 'billing_only' === get_option( 'woocommerce_ship_to_destination' ) );
|
||||
$this->asset_data_registry->add( 'generatePassword', filter_var( get_option( 'woocommerce_registration_generate_password' ), FILTER_VALIDATE_BOOLEAN ) );
|
||||
$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled() );
|
||||
$this->asset_data_registry->add( 'couponsEnabled', wc_coupons_enabled() );
|
||||
$this->asset_data_registry->add( 'shippingEnabled', wc_shipping_enabled() );
|
||||
@@ -377,7 +385,7 @@ class Checkout extends AbstractBlock {
|
||||
$shipping_methods = WC()->shipping()->get_shipping_methods();
|
||||
$formatted_shipping_methods = array_reduce(
|
||||
$shipping_methods,
|
||||
function( $acc, $method ) {
|
||||
function ( $acc, $method ) {
|
||||
if ( in_array( $method->id, LocalPickupUtils::get_local_pickup_method_ids(), true ) ) {
|
||||
return $acc;
|
||||
}
|
||||
@@ -405,7 +413,7 @@ class Checkout extends AbstractBlock {
|
||||
$payment_methods = $this->get_enabled_payment_gateways();
|
||||
$formatted_payment_methods = array_reduce(
|
||||
$payment_methods,
|
||||
function( $acc, $method ) {
|
||||
function ( $acc, $method ) {
|
||||
$acc[] = [
|
||||
'id' => $method->id,
|
||||
'title' => $method->method_title,
|
||||
@@ -427,7 +435,7 @@ class Checkout extends AbstractBlock {
|
||||
$all_plugins = \get_plugins(); // Note that `get_compatible_plugins_for_feature` calls `get_plugins` internally, so this is already in cache.
|
||||
$incompatible_extensions = array_reduce(
|
||||
$declared_extensions['incompatible'],
|
||||
function( $acc, $item ) use ( $all_plugins ) {
|
||||
function ( $acc, $item ) use ( $all_plugins ) {
|
||||
$plugin = $all_plugins[ $item ] ?? null;
|
||||
$plugin_id = $plugin['TextDomain'] ?? dirname( $item, 2 );
|
||||
$plugin_name = $plugin['Name'] ?? $plugin_id;
|
||||
@@ -465,7 +473,7 @@ class Checkout extends AbstractBlock {
|
||||
$payment_gateways = WC()->payment_gateways->payment_gateways();
|
||||
return array_filter(
|
||||
$payment_gateways,
|
||||
function( $payment_gateway ) {
|
||||
function ( $payment_gateway ) {
|
||||
return 'yes' === $payment_gateway->enabled;
|
||||
}
|
||||
);
|
||||
@@ -500,7 +508,7 @@ class Checkout extends AbstractBlock {
|
||||
$payment_methods[ $payment_method_group ] = array_values(
|
||||
array_filter(
|
||||
$saved_payment_methods,
|
||||
function( $saved_payment_method ) use ( $payment_gateways ) {
|
||||
function ( $saved_payment_method ) use ( $payment_gateways ) {
|
||||
return in_array( $saved_payment_method['method']['gateway'], array_keys( $payment_gateways ), true );
|
||||
}
|
||||
)
|
||||
|
||||
@@ -21,12 +21,24 @@ class ComingSoon extends AbstractBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend style handle for this block type.
|
||||
* Enqueue frontend assets for this block, just in time for rendering.
|
||||
*
|
||||
* @return null
|
||||
* @internal This prevents the block script being enqueued on all pages. It is only enqueued as needed. Note that
|
||||
* we intentionally do not pass 'script' to register_block_type.
|
||||
*
|
||||
* @param array $attributes Any attributes that currently are available from the block.
|
||||
* @param string $content The block content.
|
||||
* @param WP_Block $block The block object.
|
||||
*/
|
||||
protected function get_block_type_style() {
|
||||
return null;
|
||||
protected function enqueue_assets( array $attributes, $content, $block ) {
|
||||
parent::enqueue_assets( $attributes, $content, $block );
|
||||
|
||||
if ( isset( $attributes['color'] ) ) {
|
||||
wp_add_inline_style(
|
||||
'wc-blocks-style',
|
||||
':root{--woocommerce-coming-soon-color: ' . esc_html( $attributes['color'] ) . '}'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,9 +11,10 @@ use Automattic\WooCommerce\Blocks\Utils\BlockHooksTrait;
|
||||
class CustomerAccount extends AbstractBlock {
|
||||
use BlockHooksTrait;
|
||||
|
||||
const TEXT_ONLY = 'text_only';
|
||||
const ICON_ONLY = 'icon_only';
|
||||
const DISPLAY_ALT = 'alt';
|
||||
const TEXT_ONLY = 'text_only';
|
||||
const ICON_ONLY = 'icon_only';
|
||||
const DISPLAY_ALT = 'alt';
|
||||
const DISPLAY_LINE = 'line';
|
||||
|
||||
/**
|
||||
* Block name.
|
||||
@@ -33,6 +34,7 @@ class CustomerAccount extends AbstractBlock {
|
||||
'anchor' => 'core/navigation',
|
||||
'area' => 'header',
|
||||
'callback' => 'should_unhook_block',
|
||||
'version' => '8.4.0',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -65,6 +67,7 @@ class CustomerAccount extends AbstractBlock {
|
||||
*/
|
||||
public function modify_hooked_block_attributes( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) {
|
||||
$parsed_hooked_block['attrs']['displayStyle'] = 'icon_only';
|
||||
$parsed_hooked_block['attrs']['iconStyle'] = 'line';
|
||||
|
||||
/*
|
||||
* The Mini Cart block (which is hooked into the header) has a margin of 0.5em on the left side.
|
||||
@@ -116,16 +119,26 @@ class CustomerAccount extends AbstractBlock {
|
||||
$account_link = get_option( 'woocommerce_myaccount_page_id' ) ? wc_get_account_endpoint_url( 'dashboard' ) : wp_login_url();
|
||||
|
||||
$allowed_svg = array(
|
||||
'svg' => array(
|
||||
'svg' => array(
|
||||
'class' => true,
|
||||
'xmlns' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'viewbox' => true,
|
||||
),
|
||||
'path' => array(
|
||||
'd' => true,
|
||||
'fill' => true,
|
||||
'path' => array(
|
||||
'd' => true,
|
||||
'fill' => true,
|
||||
'fill-rule' => true,
|
||||
'clip-rule' => true,
|
||||
),
|
||||
'circle' => array(
|
||||
'cx' => true,
|
||||
'cy' => true,
|
||||
'r' => true,
|
||||
'stroke' => true,
|
||||
'stroke-width' => true,
|
||||
'fill' => true,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -153,8 +166,27 @@ class CustomerAccount extends AbstractBlock {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( self::DISPLAY_LINE === $attributes['iconStyle'] ) {
|
||||
return '<svg class="' . $attributes['iconClass'] . '" viewBox="5 5 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle
|
||||
cx="16"
|
||||
cy="10.5"
|
||||
r="3.5"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
fill="none"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.5 18.5H20.5C21.8807 18.5 23 19.6193 23 21V25.5H25V21C25 18.5147 22.9853 16.5 20.5 16.5H11.5C9.01472 16.5 7 18.5147 7 21V25.5H9V21C9 19.6193 10.1193 18.5 11.5 18.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>';
|
||||
}
|
||||
|
||||
if ( self::DISPLAY_ALT === $attributes['iconStyle'] ) {
|
||||
return '<svg class="' . $attributes['iconClass'] . '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" width="18" height="18">
|
||||
return '<svg class="' . $attributes['iconClass'] . '" xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 19 19" width="18" height="18">
|
||||
<path
|
||||
d="M9 0C4.03579 0 0 4.03579 0 9C0 13.9642 4.03579 18 9 18C13.9642 18 18 13.9642 18 9C18 4.03579 13.9642 0 9
|
||||
0ZM9 4.32C10.5347 4.32 11.7664 5.57056 11.7664 7.08638C11.7664 8.62109 10.5158 9.85277 9 9.85277C7.4653
|
||||
@@ -166,7 +198,7 @@ class CustomerAccount extends AbstractBlock {
|
||||
</svg>';
|
||||
}
|
||||
|
||||
return '<svg class="' . $attributes['iconClass'] . '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
|
||||
return '<svg class="' . $attributes['iconClass'] . '" xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 17 17" width="16" height="16">
|
||||
<path
|
||||
d="M8.00009 8.34785C10.3096 8.34785 12.1819 6.47909 12.1819 4.17393C12.1819 1.86876 10.3096 0 8.00009 0C5.69055
|
||||
0 3.81824 1.86876 3.81824 4.17393C3.81824 6.47909 5.69055 8.34785 8.00009 8.34785ZM0.333496 15.6522C0.333496
|
||||
|
||||
@@ -65,6 +65,7 @@ class MiniCart extends AbstractBlock {
|
||||
'position' => 'after',
|
||||
'anchor' => 'core/navigation',
|
||||
'area' => 'header',
|
||||
'version' => '8.4.0',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -459,7 +460,7 @@ class MiniCart extends AbstractBlock {
|
||||
|
||||
// It is not necessary to load the Mini-Cart Block on Cart and Checkout page.
|
||||
return '<div class="' . esc_attr( $wrapper_classes ) . '" style="visibility:hidden" aria-hidden="true">
|
||||
<button class="wc-block-mini-cart__button" disabled>' . $button_html . '</button>
|
||||
<button class="wc-block-mini-cart__button" disabled aria-label="' . __( 'Cart', 'woocommerce' ) . '">' . $button_html . '</button>
|
||||
</div>';
|
||||
}
|
||||
|
||||
@@ -487,7 +488,7 @@ class MiniCart extends AbstractBlock {
|
||||
}
|
||||
|
||||
return '<div class="' . esc_attr( $wrapper_classes ) . '" style="' . esc_attr( $wrapper_styles ) . '">
|
||||
<button class="wc-block-mini-cart__button">' . $button_html . '</button>
|
||||
<button class="wc-block-mini-cart__button" aria-label="' . __( 'Cart', 'woocommerce' ) . '">' . $button_html . '</button>
|
||||
<div class="is-loading wc-block-components-drawer__screen-overlay wc-block-components-drawer__screen-overlay--is-hidden" aria-hidden="true">
|
||||
<div class="wc-block-mini-cart__drawer wc-block-components-drawer">
|
||||
<div class="wc-block-components-drawer__content">
|
||||
@@ -546,7 +547,7 @@ class MiniCart extends AbstractBlock {
|
||||
'tax_label' => $tax_label,
|
||||
'display_cart_prices_including_tax' => false,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return array(
|
||||
'tax_label' => '',
|
||||
|
||||
@@ -40,13 +40,27 @@ class Status extends AbstractOrderConfirmationBlock {
|
||||
return '';
|
||||
}
|
||||
|
||||
$account_notice = $this->render_account_notice( $order );
|
||||
|
||||
if ( $account_notice ) {
|
||||
$block = sprintf(
|
||||
'<div class="wc-block-order-confirmation-status-notices %1$s">%2$s</div>',
|
||||
esc_attr( trim( $classname ) ),
|
||||
$account_notice
|
||||
) . $block;
|
||||
}
|
||||
|
||||
$additional_content = $this->render_confirmation_notice( $order );
|
||||
|
||||
return $additional_content ? $block . sprintf(
|
||||
'<div class="wc-block-order-confirmation-status-description %1$s">%2$s</div>',
|
||||
esc_attr( trim( $classname ) ),
|
||||
$additional_content
|
||||
) : $block;
|
||||
if ( $additional_content ) {
|
||||
$block = $block . sprintf(
|
||||
'<div class="wc-block-order-confirmation-status-description %1$s">%2$s</div>',
|
||||
esc_attr( trim( $classname ) ),
|
||||
$additional_content
|
||||
);
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +156,33 @@ class Status extends AbstractOrderConfirmationBlock {
|
||||
return '<p>' . esc_html__( 'Please check your email for the order confirmation.', 'woocommerce' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user associated with the order needs to set a password (new account) show a notice.
|
||||
*
|
||||
* @param \WC_Order|null $order Order object.
|
||||
* @return string
|
||||
*/
|
||||
protected function render_account_notice( $order = null ) {
|
||||
if ( $order && $order->get_customer_id() && 'store-api' === $order->get_created_via() ) {
|
||||
$nag = get_user_option( 'default_password_nag', $order->get_customer_id() );
|
||||
$generate = filter_var( get_option( 'woocommerce_registration_generate_password', false ), FILTER_VALIDATE_BOOLEAN );
|
||||
|
||||
if ( $nag && $generate ) {
|
||||
return wc_print_notice(
|
||||
sprintf(
|
||||
// translators: %s: site name.
|
||||
__( 'Your account with %s has been successfully created. We emailed you a link to set your account password.', 'woocommerce' ),
|
||||
esc_html( wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) )
|
||||
),
|
||||
'notice',
|
||||
array(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* If the order is invalid or there is no permission to view the details, tell the user to check email or log-in.
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\ProductCollectionUtils;
|
||||
use WP_Query;
|
||||
use WC_Tax;
|
||||
|
||||
@@ -76,6 +77,9 @@ class ProductCollection extends AbstractBlock {
|
||||
// Extend allowed `collection_params` for the REST API.
|
||||
add_filter( 'rest_product_collection_params', array( $this, 'extend_rest_query_allowed_params' ), 10, 1 );
|
||||
|
||||
// Provide location context into block's context.
|
||||
add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 );
|
||||
|
||||
// Interactivity API: Add navigation directives to the product collection block.
|
||||
add_filter( 'render_block_woocommerce/product-collection', array( $this, 'enhance_product_collection_with_interactivity' ), 10, 2 );
|
||||
add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 );
|
||||
@@ -86,6 +90,71 @@ class ProductCollection extends AbstractBlock {
|
||||
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the location context to each inner block of the product collection block.
|
||||
* Hint: Only blocks using the 'query' context will be affected.
|
||||
*
|
||||
* The sourceData structure depends on the context type as follows:
|
||||
* - site: [ ]
|
||||
* - order: [ 'orderId' => int ]
|
||||
* - cart: [ 'productIds' => int[] ]
|
||||
* - archive: [ 'taxonomy' => string, 'termId' => int ]
|
||||
* - product: [ 'productId' => int ]
|
||||
*
|
||||
* @example array(
|
||||
* 'type' => 'product',
|
||||
* 'sourceData' => array( 'productId' => 123 ),
|
||||
* )
|
||||
*
|
||||
* @param array $context The block context.
|
||||
* @return array $context {
|
||||
* The block context including the product collection location context.
|
||||
*
|
||||
* @type array $productCollectionLocation {
|
||||
* @type string $type The context type. Possible values are 'site', 'order', 'cart', 'archive', 'product'.
|
||||
* @type array $sourceData The context source data. Can be the product ID of the viewed product, the order ID of the current order viewed, etc. See structure above for more details.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function provide_location_context_for_inner_blocks( $context ) {
|
||||
// Run only on frontend.
|
||||
// This is needed to avoid SSR renders while in editor. @see https://github.com/woocommerce/woocommerce/issues/45181.
|
||||
if ( is_admin() || \WC()->is_rest_api_request() ) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
// Target only product collection's inner blocks that use the 'query' context.
|
||||
if ( ! isset( $context['query'] ) || ! isset( $context['query']['isProductCollectionBlock'] ) || ! $context['query']['isProductCollectionBlock'] ) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
$is_in_single_product = isset( $context['singleProduct'] ) && ! empty( $context['postId'] );
|
||||
$context['productCollectionLocation'] = $is_in_single_product ? array(
|
||||
'type' => 'product',
|
||||
'sourceData' => array(
|
||||
'productId' => absint( $context['postId'] ),
|
||||
),
|
||||
) : $this->get_location_context();
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global location context.
|
||||
* Serve as a runtime cache for the location context.
|
||||
*
|
||||
* @see ProductCollectionUtils::parse_frontend_location_context()
|
||||
*
|
||||
* @return array The location context.
|
||||
*/
|
||||
private function get_location_context() {
|
||||
static $location_context = null;
|
||||
if ( null === $location_context ) {
|
||||
$location_context = ProductCollectionUtils::parse_frontend_location_context();
|
||||
}
|
||||
return $location_context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the Product Collection block with client-side pagination.
|
||||
*
|
||||
@@ -451,10 +520,14 @@ class ProductCollection extends AbstractBlock {
|
||||
}
|
||||
|
||||
$block_context_query = $block->context['query'];
|
||||
|
||||
// phpcs:ignore WordPress.DB.SlowDBQuery
|
||||
$block_context_query['tax_query'] = ! empty( $query['tax_query'] ) ? $query['tax_query'] : array();
|
||||
|
||||
$is_exclude_applied_filters = ! ( $block->context['query']['inherit'] ?? false );
|
||||
$inherit = $block->context['query']['inherit'] ?? false;
|
||||
$filterable = $block->context['query']['filterable'] ?? false;
|
||||
|
||||
$is_exclude_applied_filters = ! ( $inherit || $filterable );
|
||||
|
||||
return $this->get_final_frontend_query( $block_context_query, $page, $is_exclude_applied_filters );
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ class ProductFilters extends AbstractBlock {
|
||||
* @return string Rendered block type output.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
return <<<HTML
|
||||
<div>Product Filters</div>
|
||||
HTML;
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
|
||||
/**
|
||||
* ProductFiltersOverlay class.
|
||||
*/
|
||||
@@ -30,10 +33,49 @@ class ProductFiltersOverlay extends AbstractBlock {
|
||||
* @return string Rendered block type output.
|
||||
*/
|
||||
protected function render( $attributes, $content, $block ) {
|
||||
ob_start();
|
||||
printf( '<div>%s</div>', esc_html__( 'Filters Overlay', 'woocommerce' ) );
|
||||
$html = ob_get_clean();
|
||||
return $content;
|
||||
}
|
||||
|
||||
return $html;
|
||||
/**
|
||||
* Extra data passed through from server to client for block.
|
||||
*
|
||||
* @param array $attributes Any attributes that currently are available from the block.
|
||||
* Note, this will be empty in the editor context when the block is
|
||||
* not in the post content on editor load.
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
$template_part_edit_uri = '';
|
||||
|
||||
if (
|
||||
current_user_can( 'edit_theme_options' ) &&
|
||||
( wc_current_theme_is_fse_theme() || current_theme_supports( 'block-template-parts' ) )
|
||||
) {
|
||||
$theme_slug = BlockTemplateUtils::theme_has_template_part( 'product-filters-overlay' ) ? wp_get_theme()->get_stylesheet() : BlockTemplateUtils::PLUGIN_SLUG;
|
||||
|
||||
$site_editor_uri = add_query_arg(
|
||||
array(
|
||||
'canvas' => 'edit',
|
||||
'path' => '/template-parts/single',
|
||||
),
|
||||
admin_url( 'site-editor.php' )
|
||||
);
|
||||
|
||||
$template_part_edit_uri = esc_url_raw(
|
||||
add_query_arg(
|
||||
array(
|
||||
'postId' => sprintf( '%s//%s', $theme_slug, 'product-filters-overlay' ),
|
||||
'postType' => 'wp_template_part',
|
||||
),
|
||||
$site_editor_uri
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'templatePartProductFiltersOverlayEditUri',
|
||||
$template_part_edit_uri
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* ProductFilters class.
|
||||
*/
|
||||
class ProductFiltersOverlayNavigation extends AbstractBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'product-filters-overlay-navigation';
|
||||
}
|
||||
@@ -48,6 +48,8 @@ class ProductTemplate extends AbstractBlock {
|
||||
|
||||
$classnames = '';
|
||||
if ( isset( $block->context['displayLayout'] ) && isset( $block->context['query'] ) ) {
|
||||
$classnames = 'is-product-collection-layout-' . $block->context['displayLayout']['type'] . ' ';
|
||||
|
||||
if ( isset( $block->context['displayLayout']['type'] ) && 'flex' === $block->context['displayLayout']['type'] ) {
|
||||
if ( isset( $block->context['displayLayout']['shrinkColumns'] ) && $block->context['displayLayout']['shrinkColumns'] ) {
|
||||
$classnames = "wc-block-product-template__responsive columns-{$block->context['displayLayout']['columns']}";
|
||||
@@ -56,6 +58,7 @@ class ProductTemplate extends AbstractBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
|
||||
$classnames .= ' has-link-color';
|
||||
}
|
||||
|
||||
@@ -31,6 +31,13 @@ final class BlockTypesController {
|
||||
*/
|
||||
protected $asset_data_registry;
|
||||
|
||||
/**
|
||||
* Holds the registered blocks that have WooCommerce blocks as their parents.
|
||||
*
|
||||
* @var array List of registered blocks.
|
||||
*/
|
||||
private $registered_blocks_with_woocommerce_parents;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -67,6 +74,45 @@ final class BlockTypesController {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered blocks that have WooCommerce blocks as their parents. Adds the value to the
|
||||
* `registered_blocks_with_woocommerce_parents` cache if `init` has been fired.
|
||||
*
|
||||
* @return array Registered blocks with WooCommerce blocks as parents.
|
||||
*/
|
||||
public function get_registered_blocks_with_woocommerce_parent() {
|
||||
// If init has run and the cache is already set, return it.
|
||||
if ( did_action( 'init' ) && ! empty( $this->registered_blocks_with_woocommerce_parents ) ) {
|
||||
return $this->registered_blocks_with_woocommerce_parents;
|
||||
}
|
||||
|
||||
$registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
|
||||
|
||||
if ( ! is_array( $registered_blocks ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->registered_blocks_with_woocommerce_parents = array_filter(
|
||||
$registered_blocks,
|
||||
function ( $block ) {
|
||||
if ( empty( $block->parent ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! is_array( $block->parent ) ) {
|
||||
$block->parent = array( $block->parent );
|
||||
}
|
||||
$woocommerce_blocks = array_filter(
|
||||
$block->parent,
|
||||
function ( $parent_block_name ) {
|
||||
return 'woocommerce' === strtok( $parent_block_name, '/' );
|
||||
}
|
||||
);
|
||||
return ! empty( $woocommerce_blocks );
|
||||
}
|
||||
);
|
||||
return $this->registered_blocks_with_woocommerce_parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current post has a block with a specific attribute value.
|
||||
*
|
||||
@@ -132,14 +178,15 @@ final class BlockTypesController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data- attributes to blocks when rendered if the block is under the woocommerce/ namespace.
|
||||
* Check if a block should have data attributes appended on render. If it's in an allowed namespace, or the block
|
||||
* has explicitly been added to the allowed block list, or if one of the block's parents is in the WooCommerce
|
||||
* namespace it can have data attributes.
|
||||
*
|
||||
* @param string $content Block content.
|
||||
* @param array $block Parsed block data.
|
||||
* @return string
|
||||
* @param string $block_name Name of the block to check.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_data_attributes( $content, $block ) {
|
||||
$block_name = $block['blockName'];
|
||||
public function block_should_have_data_attributes( $block_name ) {
|
||||
$block_namespace = strtok( $block_name ?? '', '/' );
|
||||
|
||||
/**
|
||||
@@ -164,18 +211,44 @@ final class BlockTypesController {
|
||||
*/
|
||||
$allowed_blocks = (array) apply_filters( '__experimental_woocommerce_blocks_add_data_attributes_to_block', array() );
|
||||
|
||||
if ( ! in_array( $block_namespace, $allowed_namespaces, true ) && ! in_array( $block_name, $allowed_blocks, true ) ) {
|
||||
$blocks_with_woo_parents = $this->get_registered_blocks_with_woocommerce_parent();
|
||||
$block_has_woo_parent = in_array( $block_name, array_keys( $blocks_with_woo_parents ), true );
|
||||
$in_allowed_namespace_list = in_array( $block_namespace, $allowed_namespaces, true );
|
||||
$in_allowed_block_list = in_array( $block_name, $allowed_blocks, true );
|
||||
|
||||
return $block_has_woo_parent || $in_allowed_block_list || $in_allowed_namespace_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data- attributes to blocks when rendered if the block is under the woocommerce/ namespace.
|
||||
*
|
||||
* @param string $content Block content.
|
||||
* @param array $block Parsed block data.
|
||||
* @return string
|
||||
*/
|
||||
public function add_data_attributes( $content, $block ) {
|
||||
|
||||
$content = trim( $content );
|
||||
|
||||
if ( ! $this->block_should_have_data_attributes( $block['blockName'] ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$attributes = (array) $block['attrs'];
|
||||
$exclude_attributes = array( 'className', 'align' );
|
||||
$escaped_data_attributes = array(
|
||||
'data-block-name="' . esc_attr( $block['blockName'] ) . '"',
|
||||
);
|
||||
$attributes = (array) $block['attrs'];
|
||||
$exclude_attributes = array( 'className', 'align' );
|
||||
|
||||
foreach ( $attributes as $key => $value ) {
|
||||
if ( in_array( $key, $exclude_attributes, true ) ) {
|
||||
$processor = new \WP_HTML_Tag_Processor( $content );
|
||||
|
||||
if (
|
||||
false === $processor->next_token() ||
|
||||
'DIV' !== $processor->get_token_name() ||
|
||||
$processor->is_tag_closer()
|
||||
) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
foreach ( $attributes as $key => $value ) {
|
||||
if ( ! is_string( $key ) || in_array( $key, $exclude_attributes, true ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( is_bool( $value ) ) {
|
||||
@@ -184,10 +257,16 @@ final class BlockTypesController {
|
||||
if ( ! is_scalar( $value ) ) {
|
||||
$value = wp_json_encode( $value );
|
||||
}
|
||||
$escaped_data_attributes[] = 'data-' . esc_attr( strtolower( preg_replace( '/(?<!\ )[A-Z]/', '-$0', $key ) ) ) . '="' . esc_attr( $value ) . '"';
|
||||
|
||||
// For output consistency, we convert camelCase to kebab-case and output in lowercase.
|
||||
$key = strtolower( preg_replace( '/(?<!^|\ )[A-Z]/', '-$0', $key ) );
|
||||
|
||||
$processor->set_attribute( "data-{$key}", $value );
|
||||
}
|
||||
|
||||
return preg_replace( '/^<div /', '<div ' . implode( ' ', $escaped_data_attributes ) . ' ', trim( $content ) );
|
||||
// Set this last to prevent user-input from overriding it.
|
||||
$processor->set_attribute( 'data-block-name', $block['blockName'] );
|
||||
return $processor->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,6 +406,7 @@ final class BlockTypesController {
|
||||
$block_types[] = 'ProductFilter';
|
||||
$block_types[] = 'ProductFilters';
|
||||
$block_types[] = 'ProductFiltersOverlay';
|
||||
$block_types[] = 'ProductFiltersOverlayNavigation';
|
||||
$block_types[] = 'ProductFilterStockStatus';
|
||||
$block_types[] = 'ProductFilterPrice';
|
||||
$block_types[] = 'ProductFilterAttribute';
|
||||
|
||||
@@ -41,6 +41,8 @@ use Automattic\WooCommerce\Blocks\Shipping\ShippingController;
|
||||
use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility;
|
||||
use Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks\TasksController;
|
||||
use Automattic\WooCommerce\Blocks\TemplateOptions;
|
||||
|
||||
|
||||
/**
|
||||
* Takes care of bootstrapping the plugin.
|
||||
@@ -122,9 +124,23 @@ class Bootstrap {
|
||||
0
|
||||
);
|
||||
|
||||
$is_rest = wc()->is_rest_api_request();
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$is_store_api_request = $is_rest && ! empty( $_SERVER['REQUEST_URI'] ) && ( false !== strpos( $_SERVER['REQUEST_URI'], trailingslashit( rest_get_url_prefix() ) . 'wc/store/' ) );
|
||||
// We need to initialize BlockTemplatesController and BlockTemplatesRegistry at the end of `after_setup_theme`
|
||||
// so themes had the opportunity to declare support for template parts.
|
||||
add_action(
|
||||
'after_setup_theme',
|
||||
function () {
|
||||
$is_store_api_request = wc()->is_store_api_request();
|
||||
|
||||
if ( ! $is_store_api_request && ( wc_current_theme_is_fse_theme() || current_theme_supports( 'block-template-parts' ) ) ) {
|
||||
$this->container->get( BlockTemplatesRegistry::class )->init();
|
||||
$this->container->get( BlockTemplatesController::class )->init();
|
||||
}
|
||||
},
|
||||
999
|
||||
);
|
||||
|
||||
$is_rest = wc()->is_rest_api_request();
|
||||
$is_store_api_request = wc()->is_store_api_request();
|
||||
|
||||
// Load and init assets.
|
||||
$this->container->get( StoreApi::class )->init();
|
||||
@@ -152,13 +168,12 @@ class Bootstrap {
|
||||
$this->container->get( AIPatterns::class );
|
||||
$this->container->get( BlockPatterns::class );
|
||||
$this->container->get( BlockTypesController::class );
|
||||
$this->container->get( BlockTemplatesRegistry::class )->init();
|
||||
$this->container->get( BlockTemplatesController::class )->init();
|
||||
$this->container->get( ClassicTemplatesCompatibility::class );
|
||||
$this->container->get( ArchiveProductTemplatesCompatibility::class )->init();
|
||||
$this->container->get( SingleProductTemplateCompatibility::class )->init();
|
||||
$this->container->get( Notices::class )->init();
|
||||
$this->container->get( PTKPatternsStore::class );
|
||||
$this->container->get( TemplateOptions::class )->init();
|
||||
}
|
||||
|
||||
$this->container->get( QueryFilters::class )->init();
|
||||
@@ -254,18 +269,6 @@ class Bootstrap {
|
||||
return new BlockTypesController( $asset_api, $asset_data_registry );
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesRegistry::class,
|
||||
function () {
|
||||
return new BlockTemplatesRegistry();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesController::class,
|
||||
function () {
|
||||
return new BlockTemplatesController();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
ClassicTemplatesCompatibility::class,
|
||||
function ( Container $container ) {
|
||||
@@ -350,6 +353,12 @@ class Bootstrap {
|
||||
return new StoreApi();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
TemplateOptions::class,
|
||||
function () {
|
||||
return new TemplateOptions();
|
||||
}
|
||||
);
|
||||
// Maintains backwards compatibility with previous Store API namespace.
|
||||
$this->container->register(
|
||||
'Automattic\WooCommerce\Blocks\StoreApi\Formatters',
|
||||
@@ -427,6 +436,18 @@ class Bootstrap {
|
||||
return new QueryFilters();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesRegistry::class,
|
||||
function () {
|
||||
return new BlockTemplatesRegistry();
|
||||
}
|
||||
);
|
||||
$this->container->register(
|
||||
BlockTemplatesController::class,
|
||||
function () {
|
||||
return new BlockTemplatesController();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,13 +13,6 @@ use WP_Error;
|
||||
*/
|
||||
class CheckoutFields {
|
||||
|
||||
/**
|
||||
* Core checkout fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $core_fields;
|
||||
|
||||
/**
|
||||
* Additional checkout fields.
|
||||
*
|
||||
@@ -91,144 +84,10 @@ class CheckoutFields {
|
||||
*/
|
||||
public function __construct( AssetDataRegistry $asset_data_registry ) {
|
||||
$this->asset_data_registry = $asset_data_registry;
|
||||
$this->core_fields = [
|
||||
'email' => [
|
||||
'label' => __( 'Email address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Email address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'email',
|
||||
'autocapitalize' => 'none',
|
||||
'index' => 0,
|
||||
],
|
||||
'country' => [
|
||||
'label' => __( 'Country/Region', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Country/Region (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'country',
|
||||
'index' => 1,
|
||||
],
|
||||
'first_name' => [
|
||||
'label' => __( 'First name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'First name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'given-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 10,
|
||||
],
|
||||
'last_name' => [
|
||||
'label' => __( 'Last name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Last name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'family-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 20,
|
||||
],
|
||||
'company' => [
|
||||
'label' => __( 'Company', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Company (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'organization',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 30,
|
||||
],
|
||||
'address_1' => [
|
||||
'label' => __( 'Address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 40,
|
||||
],
|
||||
'address_2' => [
|
||||
'label' => __( 'Apartment, suite, etc.', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Apartment, suite, etc. (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 50,
|
||||
],
|
||||
'city' => [
|
||||
'label' => __( 'City', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'City (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 70,
|
||||
],
|
||||
'state' => [
|
||||
'label' => __( 'State/County', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'State/County (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 80,
|
||||
],
|
||||
'postcode' => [
|
||||
'label' => __( 'Postal code', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Postal code (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'postal-code',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 90,
|
||||
],
|
||||
'phone' => [
|
||||
'label' => __( 'Phone', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Phone (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'type' => 'tel',
|
||||
'autocomplete' => 'tel',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 100,
|
||||
],
|
||||
];
|
||||
|
||||
$this->fields_locations = [
|
||||
// omit email from shipping and billing fields.
|
||||
'address' => array_merge( \array_diff_key( array_keys( $this->core_fields ), array( 'email' ) ) ),
|
||||
'address' => array_merge( \array_diff_key( $this->get_core_fields_keys(), array( 'email' ) ) ),
|
||||
'contact' => array( 'email' ),
|
||||
'order' => [],
|
||||
];
|
||||
@@ -527,17 +386,8 @@ class CheckoutFields {
|
||||
|
||||
$field_data['options'] = $cleaned_options;
|
||||
|
||||
// If the field is not required, inject an empty option at the start.
|
||||
if ( isset( $field_data['required'] ) && false === $field_data['required'] && ! in_array( '', $added_values, true ) ) {
|
||||
$field_data['options'] = array_merge(
|
||||
[
|
||||
[
|
||||
'value' => '',
|
||||
'label' => '',
|
||||
],
|
||||
],
|
||||
$field_data['options']
|
||||
);
|
||||
if ( isset( $field_data['placeholder'] ) ) {
|
||||
$field_data['placeholder'] = sanitize_text_field( $field_data['placeholder'] );
|
||||
}
|
||||
|
||||
return $field_data;
|
||||
@@ -620,13 +470,167 @@ class CheckoutFields {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keys of all core fields.
|
||||
*
|
||||
* @return array An array of field keys.
|
||||
*/
|
||||
public function get_core_fields_keys() {
|
||||
return [
|
||||
'email',
|
||||
'country',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'company',
|
||||
'address_1',
|
||||
'address_2',
|
||||
'city',
|
||||
'state',
|
||||
'postcode',
|
||||
'phone',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all core fields.
|
||||
*
|
||||
* @return array An array of fields.
|
||||
*/
|
||||
public function get_core_fields() {
|
||||
return $this->core_fields;
|
||||
return [
|
||||
'email' => [
|
||||
'label' => __( 'Email address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Email address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'email',
|
||||
'autocapitalize' => 'none',
|
||||
'index' => 0,
|
||||
],
|
||||
'country' => [
|
||||
'label' => __( 'Country/Region', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Country/Region (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'country',
|
||||
'index' => 1,
|
||||
],
|
||||
'first_name' => [
|
||||
'label' => __( 'First name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'First name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'given-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 10,
|
||||
],
|
||||
'last_name' => [
|
||||
'label' => __( 'Last name', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Last name (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'family-name',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 20,
|
||||
],
|
||||
'company' => [
|
||||
'label' => __( 'Company', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Company (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'organization',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 30,
|
||||
],
|
||||
'address_1' => [
|
||||
'label' => __( 'Address', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Address (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 40,
|
||||
],
|
||||
'address_2' => [
|
||||
'label' => __( 'Apartment, suite, etc.', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Apartment, suite, etc. (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-line2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 50,
|
||||
],
|
||||
'city' => [
|
||||
'label' => __( 'City', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'City (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level2',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 70,
|
||||
],
|
||||
'state' => [
|
||||
'label' => __( 'State/County', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'State/County (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'address-level1',
|
||||
'autocapitalize' => 'sentences',
|
||||
'index' => 80,
|
||||
],
|
||||
'postcode' => [
|
||||
'label' => __( 'Postal code', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Postal code (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => true,
|
||||
'hidden' => false,
|
||||
'autocomplete' => 'postal-code',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 90,
|
||||
],
|
||||
'phone' => [
|
||||
'label' => __( 'Phone', 'woocommerce' ),
|
||||
'optionalLabel' => __(
|
||||
'Phone (optional)',
|
||||
'woocommerce'
|
||||
),
|
||||
'required' => false,
|
||||
'hidden' => false,
|
||||
'type' => 'tel',
|
||||
'autocomplete' => 'tel',
|
||||
'autocapitalize' => 'characters',
|
||||
'index' => 100,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ class Notices {
|
||||
public function init() {
|
||||
add_action(
|
||||
'after_setup_theme',
|
||||
function() {
|
||||
function () {
|
||||
/**
|
||||
* Allow classic theme developers to opt-in to using block notices.
|
||||
*
|
||||
@@ -104,13 +104,14 @@ class Notices {
|
||||
* @return string
|
||||
*/
|
||||
public function get_notices_template( $template, $template_name, $args, $template_path, $default_path ) {
|
||||
$directory = get_stylesheet_directory();
|
||||
$file = $directory . '/woocommerce/' . $template_name;
|
||||
if ( file_exists( $file ) ) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
if ( in_array( $template_name, $this->notice_templates, true ) ) {
|
||||
$directory = get_stylesheet_directory();
|
||||
$file = $directory . '/woocommerce/' . $template_name;
|
||||
|
||||
if ( file_exists( $file ) ) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$template = $this->package->get_path( 'templates/block-' . $template_name );
|
||||
wp_enqueue_style( 'wc-blocks-style' );
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ use WP_Upgrader;
|
||||
class PTKPatternsStore {
|
||||
const TRANSIENT_NAME = 'ptk_patterns';
|
||||
|
||||
// Some patterns need to be excluded because they have dependencies which
|
||||
// are not installed by default (like Jetpack). Otherwise, the user
|
||||
// would see an error when trying to insert them in the editor.
|
||||
const EXCLUDED_PATTERNS = array( '13923', '14781', '14779', '13666', '13664', '13660', '13588', '14922', '14880', '13596', '13967', '13958', '15050', '15027' );
|
||||
const CATEGORY_MAPPING = array(
|
||||
'testimonials' => 'reviews',
|
||||
);
|
||||
|
||||
/**
|
||||
* PatternsToolkit instance.
|
||||
@@ -92,11 +91,13 @@ class PTKPatternsStore {
|
||||
* @return void
|
||||
*/
|
||||
private function schedule_action_if_not_pending( $action ) {
|
||||
if ( as_has_scheduled_action( $action ) ) {
|
||||
$last_request = get_transient( 'last_fetch_patterns_request' );
|
||||
if ( as_has_scheduled_action( $action ) || false !== $last_request ) {
|
||||
return;
|
||||
}
|
||||
|
||||
as_schedule_single_action( time(), $action );
|
||||
set_transient( 'last_fetch_patterns_request', time(), HOUR_IN_SECONDS );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,26 +118,31 @@ class PTKPatternsStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter patterns to exclude those with the given IDs.
|
||||
* Filter the patterns that have external dependencies.
|
||||
*
|
||||
* @param array $patterns The patterns to filter.
|
||||
* @param array $pattern_ids The pattern IDs to exclude.
|
||||
* @return array
|
||||
*/
|
||||
private function filter_patterns( array $patterns, array $pattern_ids ) {
|
||||
return array_filter(
|
||||
$patterns,
|
||||
function ( $pattern ) use ( $pattern_ids ) {
|
||||
if ( ! isset( $pattern['ID'] ) ) {
|
||||
private function filter_patterns( array $patterns ) {
|
||||
return array_values(
|
||||
array_filter(
|
||||
$patterns,
|
||||
function ( $pattern ) {
|
||||
if ( ! isset( $pattern['ID'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( isset( $pattern['post_type'] ) && 'wp_block' !== $pattern['post_type'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->has_external_dependencies( $pattern ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( isset( $pattern['post_type'] ) && 'wp_block' !== $pattern['post_type'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! in_array( (string) $pattern['ID'], $pattern_ids, true );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,22 +187,35 @@ class PTKPatternsStore {
|
||||
|
||||
$patterns = $this->ptk_client->fetch_patterns(
|
||||
array(
|
||||
'categories' => array( 'intro', 'about', 'services', 'testimonials' ),
|
||||
// This is the site where the patterns are stored. Despite the 'wpcomstaging.com' domain suggesting a staging environment, this URL points to the production environment where stable versions of the patterns are maintained.
|
||||
'site' => 'wooblockpatterns.wpcomstaging.com',
|
||||
'categories' => array(
|
||||
'_woo_intro',
|
||||
'_woo_featured_selling',
|
||||
'_woo_about',
|
||||
'_woo_reviews',
|
||||
'_woo_social_media',
|
||||
'_woo_woocommerce',
|
||||
'_dotcom_imported_intro',
|
||||
'_dotcom_imported_about',
|
||||
'_dotcom_imported_services',
|
||||
'_dotcom_imported_reviews',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $patterns ) ) {
|
||||
wc_get_logger()->warning(
|
||||
sprintf(
|
||||
// translators: %s is a generated error message.
|
||||
__( 'Failed to get the patterns from the PTK: "%s"', 'woocommerce' ),
|
||||
__( 'Failed to get WooCommerce patterns from the PTK: "%s"', 'woocommerce' ),
|
||||
$patterns->get_error_message()
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$patterns = $this->filter_patterns( $patterns, self::EXCLUDED_PATTERNS );
|
||||
$patterns = $this->filter_patterns( $patterns );
|
||||
$patterns = $this->map_categories( $patterns );
|
||||
|
||||
set_transient( self::TRANSIENT_NAME, $patterns );
|
||||
}
|
||||
@@ -209,4 +228,51 @@ class PTKPatternsStore {
|
||||
private function allowed_tracking_is_enabled(): bool {
|
||||
return 'yes' === get_option( 'woocommerce_allow_tracking' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the categories of the patterns to match the ones used in the CYS flow
|
||||
*
|
||||
* @param array $patterns The patterns to map categories for.
|
||||
* @return array The patterns with the categories mapped.
|
||||
*/
|
||||
private function map_categories( array $patterns ) {
|
||||
return array_map(
|
||||
function ( $pattern ) {
|
||||
if ( isset( $pattern['categories'] ) ) {
|
||||
foreach ( $pattern['categories'] as $key => $category ) {
|
||||
if ( isset( $category['slug'] ) && isset( self::CATEGORY_MAPPING[ $key ] ) ) {
|
||||
$new_category = self::CATEGORY_MAPPING[ $key ];
|
||||
unset( $pattern['categories'][ $key ] );
|
||||
$pattern['categories'][ $new_category ]['slug'] = $new_category;
|
||||
$pattern['categories'][ $new_category ]['title'] = ucfirst( $new_category );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
},
|
||||
$patterns
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the pattern has external dependencies.
|
||||
*
|
||||
* @param array $pattern The pattern to check.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function has_external_dependencies( $pattern ) {
|
||||
if ( ! isset( $pattern['dependencies'] ) || ! is_array( $pattern['dependencies'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $pattern['dependencies'] as $dependency ) {
|
||||
if ( 'woocommerce' !== $dependency ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,30 @@ class PatternRegistry {
|
||||
const SLUG_REGEX = '/^[A-z0-9\/_-]+$/';
|
||||
const COMMA_SEPARATED_REGEX = '/[\s,]+/';
|
||||
|
||||
|
||||
/**
|
||||
* Associates pattern slugs with their localized labels for categorization.
|
||||
* Each key represents a unique pattern slug, while the value is the localized label.
|
||||
*
|
||||
* @var array $category_labels
|
||||
*/
|
||||
private $category_labels;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->category_labels = [
|
||||
'woo-commerce' => __( 'WooCommerce', 'woocommerce' ),
|
||||
'intro' => __( 'Intro', 'woocommerce' ),
|
||||
'featured-selling' => __( 'Featured Selling', 'woocommerce' ),
|
||||
'about' => __( 'About', 'woocommerce' ),
|
||||
'social-media' => __( 'Social Media', 'woocommerce' ),
|
||||
'services' => __( 'Services', 'woocommerce' ),
|
||||
'reviews' => __( 'Reviews', 'woocommerce' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a block pattern.
|
||||
*
|
||||
@@ -166,10 +190,13 @@ class PatternRegistry {
|
||||
|
||||
$pattern_data['categories'][ $key ] = $category_slug;
|
||||
|
||||
$label = isset( $this->category_labels[ $category_slug ] ) ? $this->category_labels[ $category_slug ] : self::kebab_to_capital_case( $category_slug );
|
||||
|
||||
register_block_pattern_category(
|
||||
$category_slug,
|
||||
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
|
||||
array( 'label' => __( $category, 'woocommerce' ) )
|
||||
array(
|
||||
'label' => $label,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -194,4 +221,18 @@ class PatternRegistry {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a kebab-case string to capital case.
|
||||
*
|
||||
* @param string $value The kebab-case string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function kebab_to_capital_case( $value ) {
|
||||
$string = str_replace( '-', ' ', $value );
|
||||
$string = ucwords( $string );
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare( strict_types = 1 );
|
||||
|
||||
namespace Automattic\WooCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Options;
|
||||
|
||||
/**
|
||||
* TemplateOptions class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TemplateOptions {
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the old and current themes and determines if the "wc_blocks_use_blockified_product_grid_block_as_template"
|
||||
* option need to be updated accordingly.
|
||||
*
|
||||
* @param string $old_name Old theme name.
|
||||
* @param \WP_Theme $old_theme Instance of the old theme.
|
||||
* @return void
|
||||
*/
|
||||
public function check_should_use_blockified_product_grid_templates( $old_name, $old_theme ) {
|
||||
if ( ! $old_theme->is_block_theme() && wc_current_theme_is_fse_theme() ) {
|
||||
$option_name = Options::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE;
|
||||
// We previously stored "yes" or "no" values. This will convert them to true or false.
|
||||
$option_value = wc_string_to_bool( get_option( $option_name ) );
|
||||
|
||||
// We don't need to do anything if the option is already set to true.
|
||||
if ( ! $option_value ) {
|
||||
update_option( $option_name, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,12 @@ class ProductFiltersOverlayTemplate extends AbstractTemplatePart {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template_area = 'product-filters-overlay';
|
||||
public $template_area = 'uncategorized';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'default_wp_template_part_areas', array( $this, 'register_product_filters_overlay_template_part_area' ), 10, 1 );
|
||||
}
|
||||
public function init() {}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
@@ -46,21 +44,4 @@ class ProductFiltersOverlayTemplate extends AbstractTemplatePart {
|
||||
public function get_template_description() {
|
||||
return __( 'Template used to display the Product Filters Overlay.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Filters Overlay to the default template part areas.
|
||||
*
|
||||
* @param array $default_area_definitions An array of supported area objects.
|
||||
* @return array The supported template part areas including the Filters Overlay one.
|
||||
*/
|
||||
public function register_product_filters_overlay_template_part_area( $default_area_definitions ) {
|
||||
$product_filters_overlay_template_part_area = array(
|
||||
'area' => 'product-filters-overlay',
|
||||
'label' => $this->get_template_title(),
|
||||
'description' => $this->get_template_description(),
|
||||
'icon' => 'filter',
|
||||
'area_tag' => 'product-filters-overlay',
|
||||
);
|
||||
return array_merge( $default_area_definitions, array( $product_filters_overlay_template_part_area ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace Automattic\WooCommerce\Blocks\Templates;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
|
||||
/**
|
||||
* ProductFiltersTemplate class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductFiltersTemplate extends AbstractTemplatePart {
|
||||
|
||||
/**
|
||||
* The slug of the template.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SLUG = 'product-filters';
|
||||
|
||||
/**
|
||||
* The template part area where the template part belongs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template_area = 'uncategorized';
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'get_block_type_variations', array( $this, 'register_block_type_variation' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_title() {
|
||||
return _x( 'Product Filters (Experimental)', 'Template name', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the template.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_template_description() {
|
||||
return __(
|
||||
'This is the template part for the product filters displayed on different pages across your store.',
|
||||
'woocommerce'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add variation for this template part to make it available in the block inserter.
|
||||
*
|
||||
* @param array $variations Array of registered variations for a block type.
|
||||
* @param WP_Block_Type $block_type The full block type object.
|
||||
*/
|
||||
public function register_block_type_variation( $variations, $block_type ) {
|
||||
if ( 'core/template-part' !== $block_type->name ) {
|
||||
return $variations;
|
||||
}
|
||||
|
||||
// If template part is modified, Core will pick it up and register a variation
|
||||
// for it. Check if the variation already exists before adding it.
|
||||
foreach ( $variations as $variation ) {
|
||||
if ( ! empty( $variation['attributes']['slug'] ) && 'product-filters' === $variation['attributes']['slug'] ) {
|
||||
return $variations;
|
||||
}
|
||||
}
|
||||
|
||||
$theme = 'woocommerce/woocommerce';
|
||||
// Check if current theme overrides this template part.
|
||||
if ( BlockTemplateUtils::theme_has_template_part( 'product-filters' ) ) {
|
||||
$theme = wp_get_theme()->get( 'TextDomain' );
|
||||
}
|
||||
|
||||
$variations[] = array(
|
||||
'name' => 'file_' . self::SLUG,
|
||||
'title' => $this->get_template_title(),
|
||||
'description' => true,
|
||||
'attributes' => array(
|
||||
'slug' => self::SLUG,
|
||||
'theme' => $theme,
|
||||
'area' => $this->template_area,
|
||||
),
|
||||
'scope' => array( 'inserter' ),
|
||||
'icon' => 'layout',
|
||||
);
|
||||
return $variations;
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,6 @@ class ProductSearchResultsTemplate extends AbstractTemplate {
|
||||
*/
|
||||
const SLUG = 'product-search-results';
|
||||
|
||||
/**
|
||||
* The template used as a fallback if that one is customized.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $fallback_template = ProductCatalogTemplate::SLUG;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
*/
|
||||
|
||||
@@ -18,31 +18,34 @@ trait BlockHooksTrait {
|
||||
* @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 the block has no hook placements, return early.
|
||||
if ( ! isset( $this->hooked_block_placements ) || empty( $this->hooked_block_placements ) ) {
|
||||
return $hooked_blocks;
|
||||
}
|
||||
|
||||
// Cache for active theme.
|
||||
static $active_theme_name = null;
|
||||
if ( is_null( $active_theme_name ) ) {
|
||||
$active_theme_name = wp_get_theme()->get( 'Name' );
|
||||
// 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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of theme slugs to execute this with. This is a temporary
|
||||
* measure until improvements to the Block Hooks API allow for exposing
|
||||
* to all block themes.
|
||||
*
|
||||
* @since 8.4.0
|
||||
*/
|
||||
$theme_include_list = apply_filters( 'woocommerce_hooked_blocks_theme_include_list', array( 'Twenty Twenty-Four', 'Twenty Twenty-Three', 'Twenty Twenty-Two', 'Tsubaki', 'Zaino', 'Thriving Artist', 'Amulet', 'Tazza' ) );
|
||||
// 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;
|
||||
}
|
||||
|
||||
if ( $context && in_array( $active_theme_name, $theme_include_list, true ) ) {
|
||||
foreach ( $this->hooked_block_placements as $placement ) {
|
||||
// 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.
|
||||
|
||||
@@ -316,9 +316,13 @@ class BlockTemplateUtils {
|
||||
$wp_template_part_filenames = array(
|
||||
'checkout-header.html',
|
||||
'mini-cart.html',
|
||||
'product-filters-overlay.html',
|
||||
);
|
||||
|
||||
if ( Features::is_enabled( 'experimental-blocks' ) ) {
|
||||
$wp_template_part_filenames[] = 'product-filters.html';
|
||||
$wp_template_part_filenames[] = 'product-filters-overlay.html';
|
||||
}
|
||||
|
||||
/*
|
||||
* This may return the blockified directory for wp_templates.
|
||||
* At the moment every template file has a corresponding blockified file.
|
||||
|
||||
@@ -8,6 +8,7 @@ use WP_Query;
|
||||
* {@internal This class and its methods are not intended for public use.}
|
||||
*/
|
||||
class ProductCollectionUtils {
|
||||
|
||||
/**
|
||||
* Prepare and execute a query for the Product Collection block.
|
||||
* This method is used by the Product Collection block and the No Results block.
|
||||
@@ -78,6 +79,87 @@ class ProductCollectionUtils {
|
||||
return self::remove_empty_array_recursive( $queries );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse WP Query's front-end context for the Product Collection block.
|
||||
*
|
||||
* The sourceData structure depends on the context type as follows:
|
||||
* - site: [ ]
|
||||
* - order: [ 'orderId' => int ]
|
||||
* - cart: [ 'productIds' => int[] ]
|
||||
* - archive: [ 'taxonomy' => string, 'termId' => int ]
|
||||
* - product: [ 'productId' => int ]
|
||||
*
|
||||
* @return array $context {
|
||||
* @type string $type The context type. Possible values are 'site', 'order', 'cart', 'archive', 'product'.
|
||||
* @type array $sourceData The context source data. Can be the product ID of the viewed product, the order ID of the current order, etc.
|
||||
* }
|
||||
*/
|
||||
public static function parse_frontend_location_context() {
|
||||
global $wp_query;
|
||||
|
||||
// Default context.
|
||||
// Hint: The Shop page uses the default context.
|
||||
$type = 'site';
|
||||
$source_data = array();
|
||||
|
||||
if ( ! ( $wp_query instanceof WP_Query ) ) {
|
||||
|
||||
return array(
|
||||
'type' => $type,
|
||||
'sourceData' => $source_data,
|
||||
);
|
||||
}
|
||||
|
||||
// As more areas are blockified, expected future contexts include:
|
||||
// - is_checkout_pay_page().
|
||||
// - is_view_order_page().
|
||||
if ( is_order_received_page() ) {
|
||||
|
||||
$type = 'order';
|
||||
$source_data = array( 'orderId' => absint( $wp_query->query_vars['order-received'] ) );
|
||||
|
||||
} elseif ( ( is_cart() || is_checkout() ) && isset( WC()->cart ) && is_a( WC()->cart, 'WC_Cart' ) ) {
|
||||
|
||||
$type = 'cart';
|
||||
$items = array();
|
||||
foreach ( WC()->cart->get_cart() as $cart_item ) {
|
||||
if ( ! isset( $cart_item['product_id'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items[] = absint( $cart_item['product_id'] );
|
||||
}
|
||||
$items = array_unique( array_filter( $items ) );
|
||||
$source_data = array( 'productIds' => $items );
|
||||
|
||||
} elseif ( is_product_taxonomy() ) {
|
||||
|
||||
$source = $wp_query->get_queried_object();
|
||||
$is_valid = is_a( $source, 'WP_Term' );
|
||||
$taxonomy = $is_valid ? $source->taxonomy : '';
|
||||
$term_id = $is_valid ? $source->term_id : '';
|
||||
$type = 'archive';
|
||||
$source_data = array(
|
||||
'taxonomy' => wc_clean( $taxonomy ),
|
||||
'termId' => absint( $term_id ),
|
||||
);
|
||||
|
||||
} elseif ( is_product() ) {
|
||||
|
||||
$source = $wp_query->get_queried_object();
|
||||
$product_id = is_a( $source, 'WP_Post' ) ? absint( $source->ID ) : 0;
|
||||
$type = 'product';
|
||||
$source_data = array( 'productId' => $product_id );
|
||||
}
|
||||
|
||||
$context = array(
|
||||
'type' => $type,
|
||||
'sourceData' => $source_data,
|
||||
);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove falsy item from array, recursively.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user