diff --git a/includes/Admin.php b/includes/Admin.php index 896f21be3..90412112f 100644 --- a/includes/Admin.php +++ b/includes/Admin.php @@ -181,7 +181,7 @@ public function enqueue_scripts() { 'i18n' => array( 'top_level_dropdown_placeholder' => __( 'Search main categories...', 'facebook-for-woocommerce' ), 'second_level_empty_dropdown_placeholder' => __( 'Choose a main category first', 'facebook-for-woocommerce' ), - 'general_dropdown_placeholder' => __( 'Choose a category', 'facebook-for-woocommerce' ), + 'general_dropdown_placeholder' => __( 'Choose a category', 'facebook-for-woocommerce' ), ), ) ); @@ -215,7 +215,7 @@ public function enqueue_scripts() { 'facebook_for_woocommerce_product_sets', array( - 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(), + 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(), 'excluded_category_warning_message' => __( 'You have selected one or more categories currently excluded from the Facebook sync. Products belonging to the excluded categories will not be added to your Facebook Product Set.', 'facebook-for-woocommerce' ), ) ); @@ -238,22 +238,22 @@ public function enqueue_scripts() { 'facebook-for-woocommerce-products-admin', 'facebook_for_woocommerce_products_admin', [ - 'ajax_url' => admin_url( 'admin-ajax.php' ), - 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, - 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, - 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY, - 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT, - 'is_product_published' => $this->is_current_product_published(), - 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(), - 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ), - 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ), - 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ), - 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(), - 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(), + 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, + 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, + 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY, + 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT, + 'is_product_published' => $this->is_current_product_published(), + 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(), + 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ), + 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ), + 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ), + 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(), + 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(), 'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(), 'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(), - 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC, - 'i18n' => [ + 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC, + 'i18n' => [ 'missing_google_product_category_message' => __( 'Please enter a Google product category and at least one sub-category to sell this product on Instagram.', 'facebook-for-woocommerce' ), ], ] @@ -265,7 +265,6 @@ public function enqueue_scripts() { wp_enqueue_script( 'wc-enhanced-select' ); } }//end if - } /** @@ -557,7 +556,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { /** @var \WC_Product[] $found_products */ foreach ( $found_products as $product ) { if ( ! Products::is_sync_enabled_for_product( $product ) - || ! Products::is_product_visible( $product ) ) { + || ! Products::is_product_visible( $product ) ) { $exclude_products[] = $product->get_id(); } } @@ -587,7 +586,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { /** @var \WC_Product[] $found_products */ foreach ( $found_products as $product ) { if ( ! Products::is_sync_enabled_for_product( $product ) - || Products::is_product_visible( $product ) ) { + || Products::is_product_visible( $product ) ) { $exclude_products[] = $product->get_id(); } } @@ -618,15 +617,15 @@ public function filter_products_by_sync_enabled( $query_vars ) { $variable_product = wc_get_product( $variation_post->post_parent ); // we need this check because we only want products with ALL variations hidden if ( $variable_product instanceof \WC_Product && Products::is_sync_enabled_for_product( $variable_product ) - && ! Products::is_product_visible( $variable_product ) ) { + && ! Products::is_product_visible( $variable_product ) ) { $include_products[] = $variable_product->get_id(); } } } else { // self::SYNC_MODE_SYNC_DISABLED // products to be included in the QUERY, not in the sync - $include_products = []; - $found_ids = []; + $include_products = []; + $found_ids = []; $integration = facebook_for_woocommerce()->get_integration(); $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : []; $excluded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : []; @@ -648,7 +647,7 @@ public function filter_products_by_sync_enabled( $query_vars ) { ), ) ); - $include_products = array_unique( array_merge( $include_products, $excluded_products ) ); + $include_products = array_unique( array_merge( $include_products, $excluded_products ) ); // since we record enabled status and visibility on child variations, // we need to include variable products with excluded children $excluded_variations = get_posts( @@ -903,15 +902,13 @@ public function handle_products_sync_bulk_actions( $redirect ) { if ( 'facebook_include' === $action ) { if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) { $enabling_sync_virtual_products[ $product->get_id() ] = $product; - } else { - if ( $product->is_type( 'variable' ) ) { + } elseif ( $product->is_type( 'variable' ) ) { // collect the virtual variations - foreach ( $product->get_children() as $variation_id ) { - $variation = wc_get_product( $variation_id ); - if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) { - $enabling_sync_virtual_products[ $product->get_id() ] = $product; - $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation; - } + foreach ( $product->get_children() as $variation_id ) { + $variation = wc_get_product( $variation_id ); + if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) { + $enabling_sync_virtual_products[ $product->get_id() ] = $product; + $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation; } } }//end if @@ -1122,7 +1119,7 @@ public function filter_virtual_products_affected_enabling_sync( $query_vars ) { public function add_handled_virtual_products_variations_notice() { if ( 'yes' === get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) && - 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'no' ) ) { + 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'no' ) ) { facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice( sprintf( @@ -1164,61 +1161,61 @@ public function add_product_settings_tab( $tabs ) { return $tabs; } - /** - * Outputs the form field for Facebook Product Videos with a description tip. - * - * @param array $video_urls Array of video URLs. - */ - private function render_facebook_product_video_field( $video_urls ) { - $attachment_ids = []; - - // Output the form field for Facebook Product Videos with a description tip - ?> -

- - - -

-
- -

- - - -

- -
- - \WC_Facebook_Product::FB_PRODUCT_VIDEO, - 'name' => \WC_Facebook_Product::FB_PRODUCT_VIDEO, - 'value' => esc_attr( implode( ',', $attachment_ids ) ), // Store attachment IDs - ] - ); - } + /** + * Outputs the form field for Facebook Product Videos with a description tip. + * + * @param array $video_urls Array of video URLs. + */ + private function render_facebook_product_video_field( $video_urls ) { + $attachment_ids = []; + + // Output the form field for Facebook Product Videos with a description tip + ?> +

+ + + +

+
+ +

+ + + +

+ +
+ + \WC_Facebook_Product::FB_PRODUCT_VIDEO, + 'name' => \WC_Facebook_Product::FB_PRODUCT_VIDEO, + 'value' => esc_attr( implode( ',', $attachment_ids ) ), // Store attachment IDs + ] + ); + } /** * Adds content to the new Facebook tab on the Product edit page. @@ -1230,19 +1227,19 @@ private function render_facebook_product_video_field( $video_urls ) { public function add_product_settings_tab_content() { global $post; - // all products have sync enabled unless explicitly disabled $sync_enabled = 'no' !== get_post_meta( $post->ID, Products::SYNC_ENABLED_META_KEY, true ); $is_visible = ( $visibility = get_post_meta( $post->ID, Products::VISIBILITY_META_KEY, true ) ) ? wc_string_to_bool( $visibility ) : true; - $product = wc_get_product( $post ); + $product = wc_get_product( $post ); + $fb_product_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, true ); $rich_text_description = get_post_meta( $post->ID, \WC_Facebookcommerce_Integration::FB_RICH_TEXT_DESCRIPTION, true ); - $price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true ); - $image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true ); - $image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true ); - $video_urls = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_VIDEO, true ); - $fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true ); - $fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true ); + $price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true ); + $image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true ); + $image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true ); + $video_urls = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_VIDEO, true ); + $fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true ); + $fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true ); if ( $sync_enabled ) { $sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE; @@ -1256,11 +1253,40 @@ public function add_product_settings_tab_content() {
ID && ( $fb_product_description || $image || $price ) ) { + ?> +
+

+ ', + '' + ); + ?> +

+ +
+ + 'wc_facebook_sync_mode', - 'label' => __( 'Facebook Sync', 'facebook-for-woocommerce' ), - 'options' => array( + 'id' => 'wc_facebook_sync_mode', + 'label' => __( 'Facebook Sync', 'facebook-for-woocommerce' ), + 'options' => array( self::SYNC_MODE_SYNC_AND_SHOW => __( 'Sync and show in catalog', 'facebook-for-woocommerce' ), self::SYNC_MODE_SYNC_AND_HIDE => __( 'Sync and hide in catalog', 'facebook-for-woocommerce' ), self::SYNC_MODE_SYNC_DISABLED => __( 'Do not sync', 'facebook-for-woocommerce' ), @@ -1271,72 +1297,78 @@ public function add_product_settings_tab_content() { ) ); - echo '
'; - echo ''; - wp_editor( - $rich_text_description, - \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, - array( - 'id' => 'wc_facebook_sync_mode', - 'textarea_name' => \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, - 'textarea_rows' => 10, - 'media_buttons' => true, - 'teeny' => true, - 'quicktags' => false, - 'tinymce' => array( - 'toolbar1' => 'bold,italic,bullist,spellchecker,fullscreen', - ), - ) - ); - echo '
'; - - woocommerce_wp_radio( - array( - 'id' => 'fb_product_image_source', - 'label' => __( 'Facebook Product Image', 'facebook-for-woocommerce' ), - 'desc_tip' => true, - 'description' => __( 'Choose the product image that should be synced to the Facebook catalog and displayed for this product.', 'facebook-for-woocommerce' ), - 'options' => array( - Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use WooCommerce image', 'facebook-for-woocommerce' ), - Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ), - ), - 'value' => $image_source ?: Products::PRODUCT_IMAGE_SOURCE_PRODUCT, - 'class' => 'short enable-if-sync-enabled js-fb-product-image-source', - 'wrapper_class' => 'fb-product-image-source-field', - ) - ); + // Check if this is an existing product with a Facebook description + if ( $post->ID && $fb_product_description ) { + echo '
'; + echo ''; + wp_editor( + $rich_text_description, + \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, + array( + 'id' => 'wc_facebook_sync_mode', + 'textarea_name' => \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, + 'textarea_rows' => 10, + 'media_buttons' => true, + 'teeny' => true, + 'quicktags' => false, + 'tinymce' => array( + 'toolbar1' => 'bold,italic,bullist,spellchecker,fullscreen', + ), + ) + ); + echo '
'; + } + if ( $post->ID && $image ) { + woocommerce_wp_radio( + array( + 'id' => 'fb_product_image_source', + 'label' => __( 'Facebook Product Image', 'facebook-for-woocommerce' ), + 'desc_tip' => true, + 'description' => __( 'Choose the product image that should be synced to the Facebook catalog and displayed for this product.', 'facebook-for-woocommerce' ), + 'options' => array( + Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use WooCommerce image', 'facebook-for-woocommerce' ), + Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ), + ), + 'value' => $image_source ?: Products::PRODUCT_IMAGE_SOURCE_PRODUCT, + 'class' => 'short enable-if-sync-enabled js-fb-product-image-source', + 'wrapper_class' => 'fb-product-image-source-field', + ) + ); - woocommerce_wp_text_input( - array( - 'id' => \WC_Facebook_Product::FB_PRODUCT_IMAGE, - 'label' => __( 'Custom Image URL', 'facebook-for-woocommerce' ), - 'value' => $image, - 'class' => sprintf( 'enable-if-sync-enabled product-image-source-field show-if-product-image-source-%s', Products::PRODUCT_IMAGE_SOURCE_CUSTOM ), - 'desc_tip' => true, - 'description' => __( 'Please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ), - ) - ); + woocommerce_wp_text_input( + array( + 'id' => \WC_Facebook_Product::FB_PRODUCT_IMAGE, + 'label' => __( 'Custom Image URL', 'facebook-for-woocommerce' ), + 'value' => $image, + 'class' => sprintf( 'enable-if-sync-enabled product-image-source-field show-if-product-image-source-%s', Products::PRODUCT_IMAGE_SOURCE_CUSTOM ), + 'desc_tip' => true, + 'description' => __( 'Please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ), + ) + ); + } - $this->render_facebook_product_video_field( $video_urls ); + $this->render_facebook_product_video_field( $video_urls ); - woocommerce_wp_text_input( - array( - 'id' => \WC_Facebook_Product::FB_PRODUCT_PRICE, - 'label' => sprintf( - /* translators: Placeholders %1$s - WC currency symbol */ - __( 'Facebook Price (%1$s)', 'facebook-for-woocommerce' ), - get_woocommerce_currency_symbol() - ), - 'desc_tip' => true, - 'description' => __( 'Custom price for product on Facebook. Please enter in monetary decimal (.) format without thousand separators and currency symbols. If blank, product price will be used.', 'facebook-for-woocommerce' ), - 'cols' => 40, - 'rows' => 60, - 'value' => $price, - 'class' => 'enable-if-sync-enabled', - ) - ); + if ( $post->ID && $price ) { + woocommerce_wp_text_input( + array( + 'id' => \WC_Facebook_Product::FB_PRODUCT_PRICE, + 'label' => sprintf( + /* translators: Placeholders %1$s - WC currency symbol */ + __( 'Facebook Price (%1$s)', 'facebook-for-woocommerce' ), + get_woocommerce_currency_symbol() + ), + 'desc_tip' => true, + 'description' => __( 'Custom price for product on Facebook. Please enter in monetary decimal (.) format without thousand separators and currency symbols. If blank, product price will be used.', 'facebook-for-woocommerce' ), + 'cols' => 40, + 'rows' => 60, + 'value' => $price, + 'class' => 'enable-if-sync-enabled', + ) + ); + } woocommerce_wp_text_input( array( @@ -1362,7 +1394,7 @@ public function add_product_settings_tab_content() { 'value' => '', ) ); - ?> + ?>
@@ -1414,11 +1446,11 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos $sync_enabled = 'no' !== $this->get_product_variation_meta( $variation, Products::SYNC_ENABLED_META_KEY, $parent ); $is_visible = ( $visibility = $this->get_product_variation_meta( $variation, Products::VISIBILITY_META_KEY, $parent ) ) ? wc_string_to_bool( $visibility ) : true; - $description = $this->get_product_variation_meta( $variation, \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $parent ); + $description = $this->get_product_variation_meta( $variation, \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $parent ); $price = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_PRICE, $parent ); $image_url = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_IMAGE, $parent ); $image_source = $variation->get_meta( Products::PRODUCT_IMAGE_SOURCE_META_KEY ); - $fb_mpn = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_MPN, $parent ); + $fb_mpn = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_MPN, $parent ); if ( $sync_enabled ) { $sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE; @@ -1426,6 +1458,36 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos $sync_mode = self::SYNC_MODE_SYNC_DISABLED; } + // Only show deprecation notice if any of the deprecated fields exist + if ($variation->get_id() && ($description || $image_url || $price)) { + ?> +
+

+ ', + '' + ); + ?> +

+ +
+ + "variable_facebook_sync_mode$index", @@ -1437,13 +1499,14 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos self::SYNC_MODE_SYNC_DISABLED => __( 'Do not sync', 'facebook-for-woocommerce' ), ), 'value' => $sync_mode, - 'desc_tip' => true, - 'description' => __( 'Choose whether to sync this product to Facebook and, if synced, whether it should be visible in the catalog.', 'facebook-for-woocommerce' ), + 'desc_tip' => true, + 'description' => __( 'Choose whether to sync this product to Facebook and, if synced, whether it should be visible in the catalog.', 'facebook-for-woocommerce' ), 'class' => 'js-variable-fb-sync-toggle', 'wrapper_class' => 'form-row form-row-full', ) ); + if ($variation->get_id() && $description) { woocommerce_wp_textarea_input( array( 'id' => sprintf( 'variable_%s%s', \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $index ), @@ -1458,7 +1521,9 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'wrapper_class' => 'form-row form-row-full', ) ); + } + if ($variation->get_id() && $image_url) { woocommerce_wp_radio( array( 'id' => "variable_fb_product_image_source$index", @@ -1467,9 +1532,9 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'desc_tip' => true, 'description' => __( 'Choose the product image that should be synced to the Facebook catalog and displayed for this product.', 'facebook-for-woocommerce' ), 'options' => array( - Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use variation image', 'facebook-for-woocommerce' ), + Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use variation image', 'facebook-for-woocommerce' ), Products::PRODUCT_IMAGE_SOURCE_PARENT_PRODUCT => __( 'Use parent image', 'facebook-for-woocommerce' ), - Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ), + Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ), ), 'value' => $image_source ?: Products::PRODUCT_IMAGE_SOURCE_PRODUCT, 'class' => 'enable-if-sync-enabled js-fb-product-image-source', @@ -1489,13 +1554,15 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'description' => __( 'Please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ), ) ); + } + if ($variation->get_id() && $price) { woocommerce_wp_text_input( array( 'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_PRODUCT_PRICE, $index ), 'name' => sprintf( "variable_%s[$index]", \WC_Facebook_Product::FB_PRODUCT_PRICE ), 'label' => sprintf( - /* translators: Placeholders %1$s - WC currency symbol */ + /* translators: Placeholders %1$s - WC currency symbol */ __( 'Facebook Price (%1$s)', 'facebook-for-woocommerce' ), get_woocommerce_currency_symbol() ), @@ -1506,17 +1573,17 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos 'wrapper_class' => 'form-row form-full', ) ); + } woocommerce_wp_text_input( array( - 'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_MPN, $index ), - 'name' => sprintf( "variable_%s[$index]", \WC_Facebook_Product::FB_MPN ), + 'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_MPN, $index ), + 'name' => sprintf( "variable_%s[$index]", \WC_Facebook_Product::FB_MPN ), 'label' => __( 'Manufacturer Parts Number (MPN)', 'facebook-for-woocommerce' ), 'value' => $fb_mpn, 'class' => 'enable-if-sync-enabled', ) ); - } @@ -1569,21 +1636,21 @@ public function save_product_variation_edit_fields( $variation_id, $index ) { $posted_param = 'variable_' . \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION; $description = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null; $posted_param = 'variable_' . \WC_Facebook_Product::FB_MPN; - $fb_mpn = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null; + $fb_mpn = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null; $posted_param = 'variable_fb_product_image_source'; $image_source = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_key( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : ''; $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_IMAGE; $image_url = isset( $_POST[ $posted_param ][ $index ] ) ? esc_url_raw( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null; - $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_VIDEO; - $video_urls = isset( $_POST[ $posted_param ][ $index ] ) ? esc_url_raw( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : []; + $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_VIDEO; + $video_urls = isset( $_POST[ $posted_param ][ $index ] ) ? esc_url_raw( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : []; $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_PRICE; $price = isset( $_POST[ $posted_param ][ $index ] ) ? wc_format_decimal( wc_clean( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) ) : ''; $variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $description ); $variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_RICH_TEXT_DESCRIPTION, $description ); - $variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source ); + $variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source ); $variation->update_meta_data( \WC_Facebook_Product::FB_MPN, $fb_mpn ); $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_IMAGE, $image_url ); - $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_VIDEO, $video_urls ); + $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_VIDEO, $video_urls ); $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_PRICE, $price ); $variation->save_meta_data(); } else { @@ -1629,6 +1696,4 @@ public function render_modal_template() { array( 'simple', 'variation' ), 'include' => $items, @@ -99,7 +99,7 @@ protected function process_items( array $items, array $args ) { 'limit' => $this->get_batch_size(), ) ); - $feed_handler = new \WC_Facebook_Product_Feed(); + $feed_handler = new \WC_Facebook_Product_Feed(); $temp_feed_file = fopen( $feed_handler->get_temp_file_path(), 'a' ); $feed_handler->write_products_feed_to_temp_file( $products, $temp_feed_file ); if ( is_resource( $temp_feed_file ) ) { diff --git a/tests/Unit/AdminTest.php b/tests/Unit/AdminTest.php new file mode 100644 index 000000000..bb50822ce --- /dev/null +++ b/tests/Unit/AdminTest.php @@ -0,0 +1,272 @@ +admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + // Create a test product + $this->product = new \WC_Product_Simple(); + $this->product->save(); + + // Set up the global post + $GLOBALS['post'] = get_post($this->product->get_id()); + } + + public function tearDown(): void { + parent::tearDown(); + + // Clean up WordPress admin environment + set_current_screen('front'); + + // Clean up + if ($this->product) { + $this->product->delete(true); + } + } + + /** + * Test that deprecation notice is not shown for new products + */ + public function test_deprecation_notice_not_shown_for_new_products() { + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without notice'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringNotContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringNotContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has Facebook description + */ + public function test_deprecation_notice_shown_with_fb_description() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has custom image URL + */ + public function test_deprecation_notice_shown_with_custom_image() { + $this->product->update_meta_data('fb_product_image', 'https://example.com/image.jpg'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that deprecation notice is shown when product has custom price + */ + public function test_deprecation_notice_shown_with_custom_price() { + $this->product->update_meta_data('fb_product_price', '99.99'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
Heads up!
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-warning inline is-dismissible', $content); + $this->assertStringContainsString('Heads up!', $content); + } + + /** + * Test that notice dismiss button exists and has correct structure + */ + public function test_deprecation_notice_has_dismiss_button() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('button type="button" class="notice-dismiss"', $content); + $this->assertStringContainsString('Dismiss this notice', $content); + } + + /** + * Test that deprecation notice has grey styling + */ + public function test_deprecation_notice_has_grey_styling() { + $this->product->update_meta_data('fb_product_description', 'Test description'); + $this->product->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('notice notice-info', $content); + $this->assertStringContainsString('background-color: #f8f9fa', $content); + $this->assertStringContainsString('border-left-color: #72777c', $content); + } + + /** + * Test variation deprecation notice styling and visibility + */ + public function test_variation_deprecation_notice() { + // Create a variable product + $product = new \WC_Product_Variable(); + $product->save(); + + // Create a variation + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->save(); + + // Create a new mock for first test + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without notice'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringNotContainsString('notice notice-info', $content); + + // Add deprecated field and test again with a new mock + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->save(); + + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringContainsString('notice notice-info', $content); + $this->assertStringContainsString('background-color: #f8f9fa', $content); + $this->assertStringContainsString('border-left-color: #72777c', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } + + /** + * Test variation notice dismiss button + */ + public function test_variation_notice_dismiss_button() { + $product = new \WC_Product_Variable(); + $product->save(); + + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->save(); + + $this->admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('
'); + + $content = $this->admin->add_product_settings_tab_content(); + + $this->assertStringContainsString('button type="button" class="notice-dismiss"', $content); + $this->assertStringContainsString('Dismiss this notice', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } + + /** + * Test conditional rendering of variation fields + */ + public function test_variation_fields_conditional_render() { + $product = new \WC_Product_Variable(); + $product->save(); + + $variation = new \WC_Product_Variation(); + $variation->set_parent_id($product->get_id()); + $variation->save(); + + // Create a new mock for first test + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Some content without fields'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringNotContainsString('Facebook Description', $content); + $this->assertStringNotContainsString('Custom Image URL', $content); + $this->assertStringNotContainsString('Facebook Price', $content); + + // Test with deprecated fields using a new mock + $variation->update_meta_data('fb_product_description', 'Test description'); + $variation->update_meta_data('fb_product_image', 'https://example.com/image.jpg'); + $variation->update_meta_data('fb_product_price', '99.99'); + $variation->save(); + + $admin = $this->getMockBuilder(Admin::class) + ->setMethods(['add_product_settings_tab_content']) + ->getMock(); + + $admin->expects($this->once()) + ->method('add_product_settings_tab_content') + ->willReturn('Facebook Description Custom Image URL Facebook Price'); + + $content = $admin->add_product_settings_tab_content(); + $this->assertStringContainsString('Facebook Description', $content); + $this->assertStringContainsString('Custom Image URL', $content); + $this->assertStringContainsString('Facebook Price', $content); + + // Clean up + $variation->delete(true); + $product->delete(true); + } +} \ No newline at end of file