File manager - Edit - /home/monara/public_html/test.athavaneng.com/Patterns.tar
Back
PatternsHelper.php 0000644 00000010530 15073235204 0010215 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Patterns; use WP_Error; /** * Pattern Images Helper class. */ class PatternsHelper { /** * Returns the image for the given pattern. * * @param array $images The array of images. * @param int $index The index of the image to return. * @param string $default_image The default image to return. * * @return string The image. */ public static function get_image_url( array $images, int $index, string $default_image ): string { $image = filter_var( $default_image, FILTER_VALIDATE_URL ) ? $default_image : plugins_url( $default_image, dirname( __DIR__, 2 ) ); if ( isset( $images[ $index ] ) ) { $image = $images[ $index ]; } return $image; } /** * Returns the post that has the generated data by the AI for the patterns. * * @return \WP_Post|null */ public static function get_patterns_ai_data_post() { $arg = array( 'post_type' => 'patterns_ai_data', 'posts_per_page' => 1, 'no_found_rows' => true, 'cache_results' => true, ); $query = new \WP_Query( $arg ); $posts = $query->get_posts(); return $posts[0] ?? null; } /** * Delete the post that has the generated data by the AI for the patterns. * * @return \WP_Post|null */ public static function delete_patterns_ai_data_post() { $patterns_ai_data_post = self::get_patterns_ai_data_post(); if ( isset( $patterns_ai_data_post ) ) { return wp_delete_post( $patterns_ai_data_post->ID, true ); } } /** * Upsert the patterns AI data. * * @param array $patterns_dictionary The patterns dictionary. * * @return WP_Error|null */ public static function upsert_patterns_ai_data_post( $patterns_dictionary ) { $patterns_ai_data_post = self::get_patterns_ai_data_post(); if ( isset( $patterns_ai_data_post ) ) { $patterns_ai_data_post->post_content = wp_json_encode( $patterns_dictionary ); return wp_update_post( $patterns_ai_data_post, true ); } else { $patterns_ai_data_post = array( 'post_title' => 'Patterns AI Data', 'post_content' => wp_json_encode( $patterns_dictionary ), 'post_status' => 'publish', 'post_type' => 'patterns_ai_data', ); return wp_insert_post( $patterns_ai_data_post, true ); } } /** * Get the Patterns Dictionary. * * @param string|null $pattern_slug The pattern slug. * * @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'; if ( ! file_exists( $patterns_dictionary_file ) ) { 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_ai_data_post = self::get_patterns_ai_data_post(); $patterns_dictionary = ''; if ( ! empty( $patterns_ai_data_post->post_content ) ) { $patterns_dictionary = json_decode( $patterns_ai_data_post->post_content, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { return new WP_Error( 'json_decode_error', __( 'Error decoding JSON.', 'woocommerce' ) ); } } if ( ( $patterns_dictionary === $default_patterns_dictionary || empty( $patterns_dictionary ) ) && $pattern_slug ) { return self::find_pattern_by_slug( $default_patterns_dictionary, $pattern_slug ); } elseif ( $pattern_slug && is_array( $patterns_dictionary ) ) { return self::find_pattern_by_slug( $patterns_dictionary, $pattern_slug ); } elseif ( is_array( $patterns_dictionary ) ) { return $patterns_dictionary; } return $default_patterns_dictionary; } /** * Searches for a pattern by slug in a given dictionary. * * @param array $patterns_dictionary The patterns' dictionary. * @param string $slug The slug to search for. * * @return array|null Returns the pattern if found, otherwise null. */ private static function find_pattern_by_slug( $patterns_dictionary, $slug ) { foreach ( $patterns_dictionary as $pattern ) { if ( ! is_array( $pattern ) ) { continue; } if ( $pattern['slug'] === $slug ) { return $pattern; } } return null; } } PatternUpdater.php 0000644 00000035456 15073235204 0010235 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Patterns; use Automattic\WooCommerce\Blocks\AI\Connection; use WP_Error; /** * Pattern Images class. */ class PatternUpdater { /** * All patterns that are actively in use in the Assembler. */ const WC_PATTERNS_IN_THE_ASSEMBLER = [ 'woocommerce-blocks/featured-category-triple', 'woocommerce-blocks/hero-product-3-split', 'woocommerce-blocks/hero-product-chessboard', 'woocommerce-blocks/hero-product-split', 'woocommerce-blocks/product-collection-4-columns', 'woocommerce-blocks/product-collection-5-columns', 'woocommerce-blocks/social-follow-us-in-social-media', 'woocommerce-blocks/testimonials-3-columns', 'woocommerce-blocks/product-collection-featured-products-5-columns', ]; /** * Generate AI content and assign AI-managed images to Patterns. * * @param Connection $ai_connection The AI connection. * @param string|WP_Error $token The JWT token. * @param array|WP_Error $images The array of images. * @param string $business_description The business description. * * @return bool|WP_Error */ public function generate_content( $ai_connection, $token, $images, $business_description ) { if ( is_wp_error( $images ) ) { return $images; } if ( is_wp_error( $token ) ) { return $token; } if ( ! isset( $images['images'] ) ) { return new \WP_Error( 'images_not_found', __( 'No images provided for generating AI content.', 'woocommerce' ) ); } $last_business_description = get_option( 'last_business_description_with_ai_content_generated' ); if ( $last_business_description === $business_description ) { if ( is_string( $business_description ) && is_string( $last_business_description ) ) { return true; } else { return new \WP_Error( 'business_description_not_found', __( 'No business description provided for generating AI content.', 'woocommerce' ) ); } } if ( 0 === count( $images['images'] ) ) { $images = get_transient( 'woocommerce_ai_managed_images' ); } if ( empty( $images['images'] ) ) { return new WP_Error( 'no_images_found', __( 'No images found.', 'woocommerce' ) ); } // This is required in case something interrupts the execution of the script and the endpoint is called again on retry. set_transient( 'woocommerce_ai_managed_images', $images, 60 ); $patterns_dictionary = self::get_patterns_dictionary(); if ( is_wp_error( $patterns_dictionary ) ) { return $patterns_dictionary; } $patterns = $this->assign_selected_images_to_patterns( $patterns_dictionary, $images['images'] ); if ( is_wp_error( $patterns ) ) { return new WP_Error( 'failed_to_set_pattern_images', __( 'Failed to set the pattern images.', 'woocommerce' ) ); } $ai_generated_patterns_content = $this->generate_ai_content_for_patterns( $ai_connection, $token, $patterns, $business_description ); if ( is_wp_error( $ai_generated_patterns_content ) ) { return new WP_Error( 'failed_to_set_pattern_content', __( 'Failed to set the pattern content.', 'woocommerce' ) ); } $patterns_ai_data_post = PatternsHelper::get_patterns_ai_data_post(); if ( isset( $patterns_ai_data_post->post_content ) && json_decode( $patterns_ai_data_post->post_content ) === $ai_generated_patterns_content ) { return true; } $updated_content = PatternsHelper::upsert_patterns_ai_data_post( $ai_generated_patterns_content ); if ( is_wp_error( $updated_content ) ) { return new WP_Error( 'failed_to_update_patterns_content', __( 'Failed to update patterns content.', 'woocommerce' ) ); } return true; } /** * Returns the patterns with AI generated content. * * @param Connection $ai_connection The AI connection. * @param string|WP_Error $token The JWT token. * @param array $patterns The array of patterns. * @param string $business_description The business description. * * @return array|WP_Error The patterns with AI generated content. */ public function generate_ai_content_for_patterns( $ai_connection, $token, $patterns, $business_description ) { $prompts = $this->prepare_prompts( $patterns ); $expected_results_format = $this->prepare_expected_results_format( $prompts ); $formatted_prompts = $this->format_prompts_for_ai( $prompts, $business_description, $expected_results_format ); $ai_responses = $this->fetch_and_validate_ai_responses( $ai_connection, $token, $formatted_prompts, $expected_results_format ); if ( is_wp_error( $ai_responses ) ) { return $ai_responses; } return $this->apply_ai_responses_to_patterns( $patterns, $ai_responses ); } /** * Prepares the prompts for the AI. * * @param array $patterns The array of patterns. * * @return array */ private function prepare_prompts( array $patterns ) { $prompts = []; $result = []; $group_size = count( self::WC_PATTERNS_IN_THE_ASSEMBLER ); $i = 1; foreach ( $patterns as $pattern ) { $slug = $pattern['slug'] ?? ''; if ( ! in_array( $slug, self::WC_PATTERNS_IN_THE_ASSEMBLER, true ) ) { continue; } $content = $pattern['content'] ?? ''; $counter = 1; $result[ $slug ] = []; if ( isset( $content['titles'] ) ) { foreach ( $content['titles'] as $title ) { $result[ $slug ][ $counter ++ ] = $title['ai_prompt']; } } if ( isset( $content['descriptions'] ) ) { foreach ( $content['descriptions'] as $description ) { $result[ $slug ][ $counter ++ ] = $description['ai_prompt']; } } if ( isset( $content['buttons'] ) ) { foreach ( $content['buttons'] as $button ) { $result[ $slug ][ $counter ++ ] = $button['ai_prompt']; } } $i ++; if ( $i === $group_size ) { $prompts[] = $result; $result = []; $i = 1; } } return $prompts; } /** * Prepares the expected results format for the AI. * * @param array $prompts The array of prompts. * * @return array */ private function prepare_expected_results_format( array $prompts ) { $expected_results_format = []; foreach ( $prompts as $prompt ) { $expected_result_format = []; foreach ( $prompt as $key => $values ) { $expected_result_format[ $key ] = []; foreach ( $values as $sub_key => $sub_value ) { $expected_result_format[ $key ][ $sub_key ] = ''; } } $expected_results_format[] = $expected_result_format; } return $expected_results_format; } /** * Formats the prompts for the AI. * * @param array $prompts The array of prompts. * @param string $business_description The business description. * @param array $expected_results_format The expected results format. * * @return array */ private function format_prompts_for_ai( array $prompts, string $business_description, array $expected_results_format ) { $i = 0; $formatted_prompts = []; foreach ( $prompts as $prompt ) { $formatted_prompts[] = sprintf( "You are an experienced writer. Given a business described as: '%s', generate content for the sections using the following prompts for each one of them: `'%s'`, always making sure that the expected number of characters is respected. The response should be an array of data in JSON format. Each element should be an object with the pattern name as the key, and the generated content as values. Here's an example format: `'%s'`", $business_description, wp_json_encode( $prompt ), wp_json_encode( $expected_results_format[ $i ] ) ); $i ++; } return $formatted_prompts; } /** * Fetches and validates the AI responses. * * @param Connection $ai_connection The AI connection. * @param string|WP_Error $token The JWT token. * @param array $formatted_prompts The array of formatted prompts. * @param array $expected_results_format The array of expected results format. * * @return array|mixed */ private function fetch_and_validate_ai_responses( $ai_connection, $token, $formatted_prompts, $expected_results_format ) { $ai_request_retries = 0; $ai_responses = []; $success = false; while ( $ai_request_retries < 5 && ! $success ) { $ai_request_retries ++; $ai_responses = $ai_connection->fetch_ai_responses( $token, $formatted_prompts, 60, 'json_object' ); if ( is_wp_error( $ai_responses ) ) { continue; } if ( empty( $ai_responses ) ) { continue; } $loops_success = []; $i = 0; foreach ( $ai_responses as $ai_response ) { if ( ! isset( $ai_response['completion'] ) ) { $loops_success[] = false; continue; } $completion = json_decode( $ai_response['completion'], true ); if ( ! is_array( $completion ) ) { $loops_success[] = false; continue; } $diff = array_diff_key( $expected_results_format[ $i ], $completion ); $i ++; if ( ! empty( $diff ) ) { $loops_success[] = false; continue; } $empty_results = false; foreach ( $completion as $completion_item ) { foreach ( $completion_item as $value ) { if ( empty( $value ) ) { $empty_results = true; } } } if ( $empty_results ) { $loops_success[] = false; continue; } $loops_success[] = true; } if ( ! in_array( false, $loops_success, true ) ) { $success = true; } } if ( ! $success ) { return new WP_Error( 'failed_to_fetch_ai_responses', __( 'Failed to fetch AI responses.', 'woocommerce' ) ); } return $ai_responses; } /** * Applies the AI responses to the patterns. * * @param array $patterns The array of patterns. * @param array $ai_responses The array of AI responses. * * @return mixed */ private function apply_ai_responses_to_patterns( array $patterns, array $ai_responses ) { foreach ( $patterns as $i => $pattern ) { $pattern_slug = $pattern['slug']; if ( ! in_array( $pattern_slug, self::WC_PATTERNS_IN_THE_ASSEMBLER, true ) ) { continue; } foreach ( $ai_responses as $ai_response ) { $ai_response = json_decode( $ai_response['completion'], true ); if ( isset( $ai_response[ $pattern_slug ] ) ) { $ai_response_content = $ai_response[ $pattern_slug ]; $counter = 1; if ( isset( $patterns[ $i ]['content']['titles'] ) ) { foreach ( $patterns[ $i ]['content']['titles'] as $j => $title ) { if ( ! isset( $ai_response_content[ $counter ] ) ) { $ai_response_content[ $counter ] = $ai_response_content[ $counter - 1 ] ?? ''; } $patterns[ $i ]['content']['titles'][ $j ]['default'] = $ai_response_content[ $counter ]; $counter ++; } } if ( isset( $patterns[ $i ]['content']['descriptions'] ) ) { foreach ( $patterns[ $i ]['content']['descriptions'] as $k => $description ) { if ( ! isset( $ai_response_content[ $counter ] ) ) { $ai_response_content[ $counter ] = $ai_response_content[ $counter - 1 ] ?? ''; } $patterns[ $i ]['content']['descriptions'][ $k ]['default'] = $ai_response_content[ $counter ]; $counter ++; } } if ( isset( $patterns[ $i ]['content']['buttons'] ) ) { foreach ( $patterns[ $i ]['content']['buttons'] as $l => $button ) { if ( ! isset( $ai_response_content[ $counter ] ) ) { $ai_response_content[ $counter ] = $ai_response_content[ $counter - 1 ] ?? ''; } $patterns[ $i ]['content']['buttons'][ $l ]['default'] = $ai_response_content[ $counter ]; $counter ++; } } } } } return $patterns; } /** * Assign selected images to patterns. * * @param array $patterns_dictionary The array of patterns. * @param array $selected_images The array of images. * * @return array|WP_Error The patterns with images. */ private function assign_selected_images_to_patterns( $patterns_dictionary, $selected_images ) { $patterns_with_images = array(); foreach ( $patterns_dictionary as $pattern ) { if ( ! $this->pattern_has_images( $pattern ) ) { $patterns_with_images[] = $pattern; continue; } list( $images, $alts ) = $this->get_images_for_pattern( $pattern, $selected_images ); if ( empty( $images ) ) { $patterns_with_images[] = $pattern; continue; } $pattern['images'] = $images; $string = wp_json_encode( $pattern ); foreach ( $alts as $i => $alt ) { $alt = empty( $alt ) ? 'the text should be related to the store description but generic enough to adapt to any image' : $alt; $string = str_replace( "{image.$i}", $alt, $string ); } $pattern = json_decode( $string, true ); $patterns_with_images[] = $pattern; } return $patterns_with_images; } /** * Get the Patterns Dictionary. * * @return mixed|WP_Error|null */ public static function get_patterns_dictionary() { $patterns_dictionary = plugin_dir_path( __FILE__ ) . 'dictionary.json'; if ( ! file_exists( $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 ) ); } /** * Returns whether the pattern has images. * * @param array $pattern The array representing the pattern. * * @return bool True if the pattern has images, false otherwise. */ private function pattern_has_images( array $pattern ): bool { return isset( $pattern['images_total'] ) && $pattern['images_total'] > 0; } /** * Returns the images for the given pattern. * * @param array $pattern The array representing the pattern. * @param array $selected_images The array of images. * * @return array An array containing an array of the images in the first position and their alts in the second. */ private function get_images_for_pattern( array $pattern, array $selected_images ): array { $images = array(); $alts = array(); foreach ( $selected_images as $selected_image ) { if ( ! isset( $selected_image['title'] ) ) { continue; } if ( ! isset( $selected_image['URL'] ) ) { continue; } if ( str_contains( '.jpeg', $selected_image['title'] ) ) { continue; } $expected_image_format = $pattern['images_format'] ?? 'portrait'; $selected_image_format = $this->get_selected_image_format( $selected_image ); if ( $selected_image_format !== $expected_image_format ) { continue; } $images[] = $selected_image['URL']; $alts[] = $selected_image['title']; } return array( $images, $alts ); } /** * Returns the selected image format. Defaults to landscape. * * @param array $selected_image The selected image to be assigned to the pattern. * * @return string The selected image format. */ private function get_selected_image_format( $selected_image ) { if ( ! isset( $selected_image['width'], $selected_image['height'] ) ) { return 'portrait'; } return $selected_image['width'] === $selected_image['height'] ? 'square' : ( $selected_image['width'] > $selected_image['height'] ? 'landscape' : 'portrait' ); } } dictionary.json 0000644 00000047764 15073235204 0007627 0 ustar 00 [ { "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": "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" } ] } } ] ProductUpdater.php 0000644 00000047364 15073235204 0010241 0 ustar 00 <?php namespace Automattic\WooCommerce\Blocks\Patterns; use Automattic\WooCommerce\Blocks\AI\Connection; use WP_Error; /** * Pattern Images class. */ class ProductUpdater { /** * The dummy products. */ const DUMMY_PRODUCTS = [ [ 'title' => 'Vintage Typewriter', 'image' => 'assets/images/pattern-placeholders/writing-typing-keyboard-technology-white-vintage.jpg', 'description' => 'A hit spy novel or a love letter? Anything you type using this vintage typewriter from the 20s is bound to make a mark.', 'price' => 90, ], [ 'title' => 'Leather-Clad Leisure Chair', 'image' => 'assets/images/pattern-placeholders/table-wood-house-chair-floor-window.jpg', 'description' => 'Sit back and relax in this comfy designer chair. High-grain leather and steel frame add luxury to your your leisure.', 'price' => 249, ], [ 'title' => 'Black and White Summer Portrait', '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, ], [ 'title' => '3-Speed Bike', 'image' => 'assets/images/pattern-placeholders/road-sport-vintage-wheel-retro-old.jpg', 'description' => 'Zoom through the streets on this premium 3-speed bike. Manufactured and assembled in Germany in the 80s.', 'price' => 115, ], [ 'title' => 'Hi-Fi Headphones', 'image' => 'assets/images/pattern-placeholders/man-person-music-black-and-white-white-photography.jpg', 'description' => 'Experience your favorite songs in a new way with these premium hi-fi headphones.', 'price' => 125, ], [ 'title' => 'Retro Glass Jug (330 ml)', 'image' => 'assets/images/pattern-placeholders/drinkware-liquid-tableware-dishware-bottle-fluid.jpg', 'description' => 'Thick glass and a classic silhouette make this jug a must-have for any retro-inspired kitchen.', 'price' => 115, ], ]; /** * Generate AI content and assign AI-managed images to Products. * * @param Connection $ai_connection The AI connection. * @param string|WP_Error $token The JWT token. * @param array|WP_Error $images The array of images. * @param string $business_description The business description. * * @return array|WP_Error The generated content for the products. An error if the content could not be generated. */ public function generate_content( $ai_connection, $token, $images, $business_description ) { if ( is_wp_error( $images ) ) { return $images; } if ( is_wp_error( $token ) ) { return $token; } if ( ! isset( $images['images'] ) || ! isset( $images['search_term'] ) ) { $images = get_transient( 'woocommerce_ai_managed_images' ); } if ( ! isset( $images['images'] ) || ! isset( $images['search_term'] ) ) { return new \WP_Error( 'images_not_found', __( 'No images provided for generating AI content.', 'woocommerce' ) ); } // This is required in case something interrupts the execution of the script and the endpoint is called again on retry. set_transient( 'woocommerce_ai_managed_images', $images, 60 ); if ( empty( $business_description ) ) { return new \WP_Error( 'missing_business_description', __( 'No business description provided for generating AI content.', 'woocommerce' ) ); } $last_business_description = get_option( 'last_business_description_with_ai_content_generated' ); if ( $last_business_description === $business_description ) { if ( is_string( $business_description ) && is_string( $last_business_description ) ) { return array( 'product_content' => array(), ); } else { return new \WP_Error( 'business_description_not_found', __( 'No business description provided for generating AI content.', 'woocommerce' ) ); } } $dummy_products_to_update = $this->fetch_dummy_products_to_update(); if ( is_wp_error( $dummy_products_to_update ) ) { return $dummy_products_to_update; } if ( empty( $dummy_products_to_update ) ) { return array( 'product_content' => array(), ); } $products_information_list = $this->assign_ai_selected_images_to_dummy_products( $dummy_products_to_update, $images['images'] ); return $this->assign_ai_generated_content_to_dummy_products( $ai_connection, $token, $products_information_list, $business_description, $images['search_term'] ); } /** * Return all dummy products that were not modified by the store owner. * * @return array|WP_Error An array with the dummy products that need to have their content updated by AI. */ public function fetch_dummy_products_to_update() { $real_products = $this->fetch_product_ids(); $real_products_count = count( $real_products ); if ( is_array( $real_products ) && $real_products_count > 6 ) { return array( 'product_content' => array(), ); } $dummy_products = $this->fetch_product_ids( 'dummy' ); $dummy_products_count = count( $dummy_products ); $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--; } // Identify dummy products that need to have their content updated. $dummy_products_ids = $this->fetch_product_ids( 'dummy' ); if ( ! is_array( $dummy_products_ids ) ) { return new \WP_Error( 'failed_to_fetch_dummy_products', __( 'Failed to fetch dummy products.', 'woocommerce' ) ); } $dummy_products = array_map( function ( $product ) { return wc_get_product( $product->ID ); }, $dummy_products_ids ); $dummy_products_to_update = []; foreach ( $dummy_products as $dummy_product ) { if ( ! $dummy_product instanceof \WC_Product ) { continue; } $should_update_dummy_product = $this->should_update_dummy_product( $dummy_product ); if ( $should_update_dummy_product ) { $dummy_products_to_update[] = $dummy_product; } } return $dummy_products_to_update; } /** * Verify if the dummy product should have its content generated and managed by AI. * * @param \WC_Product $dummy_product The dummy product. * * @return bool */ public function should_update_dummy_product( $dummy_product ): bool { $current_product_hash = $this->get_hash_for_product( $dummy_product ); $ai_modified_product_hash = $this->get_hash_for_ai_modified_product( $dummy_product ); $date_created = $dummy_product->get_date_created(); $date_modified = $dummy_product->get_date_modified(); if ( ! $date_created instanceof \WC_DateTime || ! $date_modified instanceof \WC_DateTime ) { return false; } $formatted_date_created = $dummy_product->get_date_created()->date( 'Y-m-d H:i:s' ); $formatted_date_modified = $dummy_product->get_date_modified()->date( 'Y-m-d H:i:s' ); $timestamp_created = strtotime( $formatted_date_created ); $timestamp_modified = strtotime( $formatted_date_modified ); $timestamp_current = time(); $dummy_product_recently_modified = abs( $timestamp_current - $timestamp_modified ) < 10; $dummy_product_not_modified = abs( $timestamp_modified - $timestamp_created ) < 60; if ( $current_product_hash === $ai_modified_product_hash || $dummy_product_not_modified || $dummy_product_recently_modified ) { return true; } return false; } /** * Creates a new product and assigns the _headstart_post meta to it. * * @param array $product_data The product data. * * @return bool|int|\WP_Error */ public function create_new_product( $product_data ) { $product = new \WC_Product(); $image_src = plugins_url( $product_data['image'], dirname( __DIR__, 2 ) ); $image_alt = $product_data['title']; $product_image_id = $this->product_image_upload( $product->get_id(), $image_src, $image_alt ); if ( is_wp_error( $product_image_id ) ) { return new \WP_Error( 'error_uploading_image', $product_image_id->get_error_message() ); } $saved_product = $this->product_update( $product, $product_image_id, $product_data['title'], $product_data['description'], $product_data['price'] ); if ( is_wp_error( $saved_product ) ) { return $saved_product; } return update_post_meta( $saved_product, '_headstart_post', true ); } /** * Return all existing products that have the _headstart_post meta assigned to them. * * @param string $type The type of products to fetch. * * @return array|null */ public function fetch_product_ids( string $type = 'user_created' ) { global $wpdb; if ( 'user_created' === $type ) { return $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE ID NOT IN ( SELECT p.ID FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE pm.meta_key = %s AND p.post_type = 'product' AND p.post_status = 'publish' ) AND post_type = 'product' AND post_status = 'publish' LIMIT 6", '_headstart_post' ) ); } return $wpdb->get_results( $wpdb->prepare( "SELECT p.ID FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE pm.meta_key = %s AND p.post_type = 'product' AND p.post_status = 'publish'", '_headstart_post' ) ); } /** * Return the hash for a product based on its name, description and image_id. * * @param \WC_Product $product The product. * * @return false|string */ public function get_hash_for_product( $product ) { if ( ! $product instanceof \WC_Product ) { return false; } return md5( $product->get_name() . $product->get_description() . $product->get_image_id() ); } /** * Return the hash for a product that had its content AI-generated. * * @param \WC_Product $product The product. * * @return false|mixed */ public function get_hash_for_ai_modified_product( $product ) { if ( ! $product instanceof \WC_Product ) { return false; } return get_post_meta( $product->get_id(), '_ai_generated_content', true ); } /** * Create a hash with the AI-generated content and save it as a meta for the product. * * @param \WC_Product $product The product. * * @return bool|int */ public function create_hash_for_ai_modified_product( $product ) { if ( ! $product instanceof \WC_Product ) { return false; } $content = $this->get_hash_for_product( $product ); return update_post_meta( $product->get_id(), '_ai_generated_content', $content ); } /** * Update the product content with the AI-generated content. * * @param array $ai_generated_product_content The AI-generated product content. * * @return string|void */ public function update_product_content( $ai_generated_product_content ) { if ( ! isset( $ai_generated_product_content['product_id'] ) ) { return; } $product = wc_get_product( $ai_generated_product_content['product_id'] ); if ( ! $product instanceof \WC_Product ) { return; } if ( ! isset( $ai_generated_product_content['image']['src'] ) || ! isset( $ai_generated_product_content['image']['alt'] ) || ! isset( $ai_generated_product_content['title'] ) || ! isset( $ai_generated_product_content['description'] ) ) { return; } $product_image_id = $this->product_image_upload( $product->get_id(), $ai_generated_product_content['image']['src'], $ai_generated_product_content['image']['alt'] ); if ( is_wp_error( $product_image_id ) ) { return $product_image_id->get_error_message(); } $this->product_update( $product, $product_image_id, $ai_generated_product_content['title'], $ai_generated_product_content['description'], $ai_generated_product_content['price'] ); } /** * Upload the image for the product. * * @param int $product_id The product ID. * @param string $image_src The image source. * @param string $image_alt The image alt. * * @return int|string|WP_Error */ private function product_image_upload( $product_id, $image_src, $image_alt ) { require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; // Since the media_sideload_image function is expensive and can take longer to complete // the process of downloading the external image and uploading it to the media library, // here we are increasing the time limit to avoid any issues. set_time_limit( 150 ); wp_raise_memory_limit( 'image' ); $product_image_id = media_sideload_image( $image_src, $product_id, $image_alt, 'id' ); if ( is_wp_error( $product_image_id ) ) { return $product_image_id->get_error_message(); } return $product_image_id; } /** * Reduce the size of the image for the product to improve performance and * avoid memory exhaustion errors when uploading them to the media library. * * @param string $image_url The image URL. * * @return string */ private function adjust_image_size_for_products( $image_url ) { $parsed_url = wp_parse_url( $image_url ); if ( ! isset( $parsed_url['query'] ) ) { return $image_url; } parse_str( $parsed_url['query'], $query_params ); unset( $query_params['h'], $query_params['w'] ); $query_params['w'] = 300; $new_query_string = http_build_query( $query_params ); return $parsed_url['scheme'] . '://' . $parsed_url['host'] . $parsed_url['path'] . '?' . $new_query_string; } /** * Assigns the default content for the products. * * @param array $dummy_products_to_update The dummy products to update. * @param array $ai_selected_images The images' information. * * @return array[] */ 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 ++ ) { $image_src = $ai_selected_images[ $i ]['URL'] ?? ''; if ( wc_is_valid_url( $image_src ) ) { $image_src = $this->adjust_image_size_for_products( $ai_selected_images[ $i ]['URL'] ); } $image_alt = $ai_selected_images[ $i ]['title'] ?? ''; $products_information_list[] = [ 'title' => 'A product title', 'description' => 'A product description', 'price' => 'The product price', 'image' => [ 'src' => esc_url( $image_src ), 'alt' => esc_attr( $image_alt ), ], 'product_id' => $dummy_products_to_update[ $i ]->get_id(), ]; } return $products_information_list; } /** * Generate the product content. * * @param Connection $ai_connection The AI connection. * @param string $token The JWT token. * @param array $products_information_list The products information list. * @param string $business_description The business description. * @param string $search_term The search term. * * @return array|int|string|\WP_Error */ public function assign_ai_generated_content_to_dummy_products( $ai_connection, $token, $products_information_list, $business_description, $search_term ) { if ( empty( $business_description ) ) { return new \WP_Error( 'missing_store_description', __( 'The store description is required to generate content for your site.', 'woocommerce' ) ); } $prompts = []; foreach ( $products_information_list as $product_information ) { if ( ! empty( $product_information['image']['alt'] ) ) { $prompts[] = sprintf( 'Considering that you are the owner of a store with the following description "%s", create the title for a product that is related to "%s" and to an image described as "%s". Do not include any adjectives or descriptions of the qualities of the product and always refer to objects or services, not humans.', $business_description, $search_term, $product_information['image']['alt'] ); } else { $prompts[] = sprintf( 'You are the owner of a business described as: "%s". Create the title for a product that could be sold on your store. Do not include any adjectives or descriptions of the qualities of the product and always refer to objects or services, not humans.', $business_description ); } } $expected_results_format = []; foreach ( $products_information_list as $index => $product ) { $expected_results_format[ $index ] = [ 'title' => '', 'price' => '', ]; } $formatted_prompt = sprintf( "Generate two-words titles and price for products using the following prompts for each one of them: '%s'. Ensure each entry is unique and does not repeat the given examples. It should be a number and it's not too low or too high for the corresponding product title being advertised. Convert the price to this currency: '%s'. Do not include backticks or the word json in the response. Here's an example of the expected output format in JSON: '%s'.", wp_json_encode( $prompts ), get_woocommerce_currency(), wp_json_encode( $expected_results_format ) ); $ai_request_retries = 0; $success = false; while ( $ai_request_retries < 5 && ! $success ) { $ai_request_retries ++; $ai_response = $ai_connection->fetch_ai_response( $token, $formatted_prompt, 30 ); if ( is_wp_error( $ai_response ) ) { continue; } if ( empty( $ai_response ) ) { continue; } if ( ! isset( $ai_response['completion'] ) ) { continue; } $completion = json_decode( $ai_response['completion'], true ); if ( ! is_array( $completion ) ) { continue; } $diff = array_diff_key( $expected_results_format, $completion ); if ( ! empty( $diff ) ) { continue; } $empty_results = false; foreach ( $completion as $completion_item ) { if ( empty( $completion_item ) ) { $empty_results = true; break; } } if ( $empty_results ) { continue; } foreach ( $products_information_list as $index => $product_information ) { $products_information_list[ $index ]['title'] = str_replace( '"', '', $completion[ $index ]['title'] ); $products_information_list[ $index ]['price'] = $completion[ $index ]['price']; } $success = true; } if ( ! $success ) { return new WP_Error( 'failed_to_fetch_ai_responses', __( 'Failed to fetch AI responses for products.', 'woocommerce' ) ); } return array( 'product_content' => $products_information_list, ); } /** * Reset the products content. */ public function reset_products_content() { $dummy_products_to_update = $this->fetch_dummy_products_to_update(); $i = 0; foreach ( $dummy_products_to_update as $product ) { $image_src = plugins_url( self::DUMMY_PRODUCTS[ $i ]['image'], dirname( __DIR__, 2 ) ); $image_alt = self::DUMMY_PRODUCTS[ $i ]['title']; $product_image_id = $this->product_image_upload( $product->get_id(), $image_src, $image_alt ); if ( is_wp_error( $product_image_id ) ) { continue; } $this->product_update( $product, $product_image_id, self::DUMMY_PRODUCTS[ $i ]['title'], self::DUMMY_PRODUCTS[ $i ]['description'], self::DUMMY_PRODUCTS[ $i ]['price'] ); $i++; } } /** * Update the product with the new content. * * @param \WC_Product $product The product. * @param int $product_image_id The product image ID. * @param string $product_title The product title. * @param string $product_description The product description. * @param int $product_price The product price. * * @return int|\WP_Error */ private function product_update( $product, $product_image_id, $product_title, $product_description, $product_price ) { if ( ! $product instanceof \WC_Product ) { return new WP_Error( 'invalid_product', __( 'Invalid product.', 'woocommerce' ) ); } $product->set_image_id( $product_image_id ); $product->set_name( $product_title ); $product->set_description( $product_description ); $product->set_price( $product_price ); $product->set_regular_price( $product_price ); $product->set_slug( sanitize_title( $product_title ) ); $product->save(); $this->create_hash_for_ai_modified_product( $product ); return $product->get_id(); } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings