diff --git a/assets/js/admin/modal.js b/assets/js/admin/modal.js index c3ba1c549..25520861c 100644 --- a/assets/js/admin/modal.js +++ b/assets/js/admin/modal.js @@ -8,7 +8,7 @@ */ (function( $ ) { - + /** * Determines if the current modal is blocked. * diff --git a/assets/js/admin/plugin-rendering.js b/assets/js/admin/plugin-rendering.js new file mode 100644 index 000000000..1b5c456ab --- /dev/null +++ b/assets/js/admin/plugin-rendering.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved + * + * This source code is licensed under the license found in the + * LICENSE file in the root directory of this source tree. + * + * @package FacebookCommerce + */ + + +jQuery( document ).ready( function( $ ) { + //Setting up opt out modal + let modal; + + $(document).on('click', '#modal_opt_out_button', function(e) { + e.preventDefault(); + $.post( facebook_for_woocommerce_plugin_update.ajax_url, { + action: 'wc_facebook_opt_out_of_sync', + nonce: facebook_for_woocommerce_plugin_update.opt_out_of_sync, + }, function (response){ + data = typeof response === "string" ? JSON.parse(response) : response; + if(data.success){ + $('#opt_out_banner').hide(); + $('#opted_our_successfullly_banner').show(); + modal.remove(); + } + }).fail(function(xhr) { + console.error("Error Code:", xhr.status); + console.error("Error Message:", xhr.responseText); + modal.remove(); + }); + }); + + /** + * Banner dismissed callback + */ + $(document).on('click','#opt_out_banner .notice-dismiss, #opted_our_successfullly_banner .notice-dismiss', function (e) { + e.preventDefault(); + $.post( facebook_for_woocommerce_plugin_update.ajax_url, { + action: 'wc_banner_close_action', + nonce: facebook_for_woocommerce_plugin_update.banner_close, + }, function (response){ + data = typeof response === "string" ? JSON.parse(response) : response; + if(data.success){ + // No success condition + } + }).fail(function(xhr) { + console.error("Error Code:", xhr.status); + console.error("Error Message:", xhr.responseText); + modal.remove(); + }); + }); + + // Opt out sync controls + $('.opt_out_of_sync_button').on('click', function(event) { + event.preventDefault(); + modal = new $.WCBackboneModal.View({ + target: 'facebook-for-woocommerce-modal', + string: { + message: facebook_for_woocommerce_plugin_update.opt_out_confirmation_message, + buttons: facebook_for_woocommerce_plugin_update.opt_out_confirmation_buttons + } + }); + }) +}); + diff --git a/assets/js/admin/settings-sync.js b/assets/js/admin/settings-sync.js index e0b8f3921..493ac3470 100644 --- a/assets/js/admin/settings-sync.js +++ b/assets/js/admin/settings-sync.js @@ -175,8 +175,6 @@ jQuery( document ).ready( function( $ ) { nonce: facebook_for_woocommerce_settings_sync.sync_products_nonce, }, function ( response ) { - console.log( response ); - if ( ! response.success ) { let error = facebook_for_woocommerce_settings_sync.i18n.general_error; diff --git a/changelog.txt b/changelog.txt index 0dc0db8de..b7a27a5b4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,19 @@ *** Facebook for WooCommerce Changelog *** += 3.4.11 - 2025-06-02 = +* Tweak - Removing Variant Level Sync by @SayanPandey in #2931 +* Tweak - Removed concept of Product Group Deletion from the plugin by @vinkmeta in #3062 +* Tweak - Tagging woo all products using a flag by @SayanPandey in #3165 +* Add - A simple column for Woo All Products sync in feed file by @SayanPandey in #3197 +* Tweak - Preparation for migrating Batch API to Graph API by @vinkmeta in #3203 +* Tweak Block Product Group Creation for Simple Products by @vinkmeta in #3204 +* Fix - Removed html tags from product set description by @mshymon in #3230 +* Fix - Fix for the rollout Switches by @vinkmeta in #3236 +* Add - Opt out sync experience. by @SayanPandey in #3220 +* Fix - Added a transient flag to avoid flooding of product set api requests by @vinkmeta in #3245 +* Fix - Additional check for the opt-out banner by @SayanPandey in #3259 +* Fix - Bump up GraphAPI version to 21 by @vahidkay-meta in #3219 + = 3.4.10 - 2025-05-22 = * Fix - Disabled the RollOut switch * Fix - Removed the Global Admin Notice diff --git a/class-wc-facebookcommerce.php b/class-wc-facebookcommerce.php index d08499a18..97bd8778b 100644 --- a/class-wc-facebookcommerce.php +++ b/class-wc-facebookcommerce.php @@ -90,6 +90,9 @@ class WC_Facebookcommerce extends WooCommerce\Facebook\Framework\Plugin { /** @var WooCommerce\Facebook\Handlers\Connection connection handler */ private $connection_handler; + /** @var WooCommerce\Facebook\Handlers\PluginRender plugin update handler */ + private $plugin_render_handler; + /** @var WooCommerce\Facebook\Handlers\WebHook webhook handler */ private $webhook_handler; @@ -224,8 +227,9 @@ public function init() { $this->connection_handler = new WooCommerce\Facebook\Handlers\Connection( $this ); $this->webhook_handler = new WooCommerce\Facebook\Handlers\WebHook( $this ); $this->whatsapp_webhook_handler = new WooCommerce\Facebook\Handlers\Whatsapp_Webhook( $this ); - $this->tracker = new WooCommerce\Facebook\Utilities\Tracker(); - $this->rollout_switches = new WooCommerce\Facebook\RolloutSwitches( $this ); + $this->tracker = new WooCommerce\Facebook\Utilities\Tracker(); + $this->rollout_switches = new WooCommerce\Facebook\RolloutSwitches( $this ); + // Init jobs $this->job_manager = new WooCommerce\Facebook\Jobs\JobManager(); @@ -236,7 +240,12 @@ public function init() { // load admin handlers, before admin_init if ( is_admin() ) { - $this->admin_settings = new WooCommerce\Facebook\Admin\Settings( $this ); + if ($this->use_enhanced_onboarding()) { + $this->admin_enhanced_settings = new WooCommerce\Facebook\Admin\Enhanced_Settings( $this ); + } else { + $this->admin_settings = new WooCommerce\Facebook\Admin\Settings( $this ); + } + $this->plugin_render_handler = new \WooCommerce\Facebook\Handlers\PluginRender($this); } } } @@ -636,6 +645,17 @@ public function get_connection_handler() { return $this->connection_handler; } + /** + * Gets the Plugin update handler. + * + * @since 2.0.0 + * + * @return WooCommerce\Facebook\Handlers\PluginRender + */ + public function get_plugin_render_handler() { + return $this->plugin_render_handler; + } + /** * Gets the integration instance. diff --git a/facebook-commerce.php b/facebook-commerce.php index 633112ffa..8a2f3380f 100644 --- a/facebook-commerce.php +++ b/facebook-commerce.php @@ -806,7 +806,9 @@ public function on_product_save( int $wp_id ) { } $this->delete_fb_product( $delete_product ); } - } elseif ( $sync_enabled ) { + } + + if( $sync_enabled ) { Products::enable_sync_for_products( [ $product ] ); Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode ); $this->save_product_settings( $product ); @@ -987,11 +989,9 @@ public function delete_fb_product( $product ) { } // enqueue variations to be deleted in the background $this->facebook_for_woocommerce->get_products_sync_handler()->delete_products( $retailer_ids ); - $this->delete_product_group( $product_id ); } else { $this->delete_product_item( $product_id ); - $this->delete_product_group( $product_id ); } // clear out both item and group IDs @@ -1173,7 +1173,7 @@ public function on_variable_product_publish( $wp_id, $woo_product = null ) { $this->update_product_group( $woo_product ); } else { $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product->woo_product ); - $this->create_product_group( $woo_product, $retailer_id, true ); + $this->create_product_group( $woo_product, $retailer_id ); } $variation_ids = []; @@ -1219,26 +1219,7 @@ public function on_simple_product_publish( $wp_id, $woo_product = null, &$parent $this->update_product_item_batch_api( $woo_product, $fb_product_item_id ); return $fb_product_item_id; } else { - // Check if this is a new product item for an existing product group - if ( $woo_product->get_parent_id() ) { - $fb_product_group_id = $this->get_product_fbid( - self::FB_PRODUCT_GROUP_ID, - $woo_product->get_parent_id(), - $woo_product - ); - // New variant added - if ( $fb_product_group_id ) { - return $this->create_product_simple( $woo_product, $fb_product_group_id ); - } else { - WC_Facebookcommerce_Utils::fblog( - 'Wrong! simple_product_publish called without group ID for a variable product!', - [], - true - ); - } - } else { - return $this->create_product_simple( $woo_product ); // new product - } + return $this->create_product_simple( $woo_product ); // new product } } @@ -1265,17 +1246,9 @@ public function product_should_be_synced( WC_Product $product ): bool { * @param string|null $fb_product_group_id * @return string */ - public function create_product_simple( WC_Facebook_Product $woo_product, string $fb_product_group_id = null ): string { + public function create_product_simple( WC_Facebook_Product $woo_product): string { $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product ); - - if ( ! $fb_product_group_id ) { - $fb_product_group_id = $this->create_product_group( $woo_product, $retailer_id ); - } - - if ( $fb_product_group_id ) { - return $this->create_product_item_batch_api( $woo_product, $retailer_id, $fb_product_group_id ); - } - return ''; + return $this->create_product_item_batch_api( $woo_product, $retailer_id ); } /** @@ -1284,13 +1257,11 @@ public function create_product_simple( WC_Facebook_Product $woo_product, string * @param bool $variants * @return ?string */ - public function create_product_group( WC_Facebook_Product $woo_product, string $retailer_id, bool $variants = false ): ?string { + public function create_product_group( WC_Facebook_Product $woo_product, string $retailer_id ): ?string { $product_group_data = [ 'retailer_id' => $retailer_id, ]; - if ( $variants ) { - $product_group_data['variants'] = $woo_product->prepare_variants_for_group(); - } + $product_group_data['variants'] = $woo_product->prepare_variants_for_group(); try { $create_product_group_result = $this->facebook_for_woocommerce->get_api()->create_product_group( @@ -1385,9 +1356,10 @@ public function update_product_group( WC_Facebook_Product $woo_product ) { * * @since 3.1.7 * @param WC_Facebook_Product $woo_product - * @param string $retailer_id - **/ - public function create_product_item_batch_api( $woo_product, $retailer_id, $product_group_id ): string { + * @param string $retailer_id + **@since 3.1.7 + */ + public function create_product_item_batch_api( $woo_product, $retailer_id ): string { try { $product_data = $woo_product->prepare_product( $retailer_id, \WC_Facebook_Product::PRODUCT_PREP_TYPE_ITEMS_BATCH ); $requests = WC_Facebookcommerce_Utils::prepare_product_requests_items_batch( $product_data ); @@ -2833,27 +2805,6 @@ public function delete_product_item( int $wp_id ): void { } } - /** - * Uses the Graph API to delete the Product Group associated with the given product. - * - * @since 2.0.0 - * - * @param int $product_id product ID - */ - public function delete_product_group( int $product_id ) { - $product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $product_id ); - if ( $product_group_id ) { - // TODO: replace with a call to API::delete_product_group() {WV 2020-05-26} - try { - $pg_result = $this->facebook_for_woocommerce->get_api()->delete_product_group( $product_group_id ); - WC_Facebookcommerce_Utils::log( $pg_result ); - } catch ( ApiException $e ) { - $message = sprintf( 'There was an error trying to delete a product group: %s', $e->getMessage() ); - WC_Facebookcommerce_Utils::log( $message ); - } - } - } - /** * Filter function for woocommerce_duplicate_product_exclude_meta filter. * diff --git a/includes/API.php b/includes/API.php index ad7f67da7..d553dbf66 100644 --- a/includes/API.php +++ b/includes/API.php @@ -33,7 +33,7 @@ class API extends Base { public const GRAPH_API_URL = 'https://graph.facebook.com/'; - public const API_VERSION = 'v20.0'; + public const API_VERSION = 'v21.0'; /** @var string URI used for the request */ protected $request_uri = self::GRAPH_API_URL . self::API_VERSION; @@ -306,16 +306,17 @@ public function get_rollout_switches( string $external_business_id ) { * * @param string $external_business_id external business ID * @param string $plugin_version The plugin version. - * + * @param bool is_opted_out The plugin version. * @return Response|API\FBE\Configuration\Update\Response * @throws ApiException */ - public function update_plugin_version_configuration( string $external_business_id, string $plugin_version ): API\FBE\Configuration\Update\Response { + public function update_plugin_version_configuration( string $external_business_id, bool $is_opted_out, string $plugin_version ): API\FBE\Configuration\Update\Response { $request = new API\FBE\Configuration\Update\Request( $external_business_id ); $request->set_external_client_metadata( array( 'version_id' => $plugin_version, 'is_multisite' => is_multisite(), + 'is_woo_all_products_opted_out' => $is_opted_out ) ); $this->set_response_handler( API\FBE\Configuration\Update\Response::class ); @@ -370,20 +371,6 @@ public function update_product_group( string $product_group_id, array $data ): A } - /** - * Deletes a Facebook Product Group object. - * - * @param string $product_group_id Facebook Product Group ID. - * @return API\ProductCatalog\ProductGroups\Delete\Response - * @throws ApiException - */ - public function delete_product_group( string $product_group_id ): API\ProductCatalog\ProductGroups\Delete\Response { - $request = new API\ProductCatalog\ProductGroups\Delete\Request( $product_group_id ); - $this->set_response_handler( API\ProductCatalog\ProductGroups\Delete\Response::class ); - return $this->perform_request( $request ); - } - - /** * Gets a list of Product Items in the given Product Group. * @@ -419,15 +406,15 @@ public function find_product_item( $catalog_id, $retailer_id ) { /** * Creates a Product under the specified Product Group. * - * @since 2.0.0 + * @since 3.4.9 * - * @param string $product_group_id Facebook Product Group ID. + * @param string $product_catalog_id Facebook Product Catalog ID. * @param array $data Facebook Product Data. * @return API\Response|API\ProductCatalog\Products\Create\Response * @throws ApiException In case of network request error. */ - public function create_product_item( string $product_group_id, array $data ): API\ProductCatalog\Products\Create\Response { - $request = new API\ProductCatalog\Products\Create\Request( $product_group_id, $data ); + public function create_product_item( string $product_catalog_id, array $data ): API\ProductCatalog\Products\Create\Response { + $request = new API\ProductCatalog\Products\Create\Request( $product_catalog_id, $data ); $this->set_response_handler( API\ProductCatalog\Products\Create\Response::class ); return $this->perform_request( $request ); } diff --git a/includes/API/ProductCatalog/ProductGroups/Delete/Request.php b/includes/API/ProductCatalog/ProductGroups/Delete/Request.php deleted file mode 100644 index 1a82afb0e..000000000 --- a/includes/API/ProductCatalog/ProductGroups/Delete/Request.php +++ /dev/null @@ -1,23 +0,0 @@ - Product Groups > Delete Graph Api. - * - * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_groups/ - */ -class Request extends ApiRequest { - - /** - * @param string $product_group_id Facebook Product Group ID. - */ - public function __construct( string $product_group_id ) { - parent::__construct( "/{$product_group_id}?deletion_method=delete_items", 'DELETE' ); - } -} diff --git a/includes/API/ProductCatalog/ProductGroups/Delete/Response.php b/includes/API/ProductCatalog/ProductGroups/Delete/Response.php deleted file mode 100644 index 68ad7585d..000000000 --- a/includes/API/ProductCatalog/ProductGroups/Delete/Response.php +++ /dev/null @@ -1,16 +0,0 @@ - Product Groups > Delete Graph Api. - * - * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Deleting - * @property-read bool $success - */ -class Response extends ApiResponse {} diff --git a/includes/API/ProductCatalog/Products/Create/Request.php b/includes/API/ProductCatalog/Products/Create/Request.php index ff80b0ae7..f87ff0fcb 100644 --- a/includes/API/ProductCatalog/Products/Create/Request.php +++ b/includes/API/ProductCatalog/Products/Create/Request.php @@ -8,18 +8,18 @@ defined( 'ABSPATH' ) || exit; /** - * Request object for Product Catalog > Product Groups > Products > Create Graph Api. + * Request object for Product Catalog > Products > Create Graph Api. * - * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/#Creating + * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/products/#Creating */ class Request extends ApiRequest { /** - * @param string $product_group_id Facebook Product Group ID. + * @param string $product_catalog_id Facebook Product Catalog ID. * @param array $data Facebook Product Data. */ - public function __construct( string $product_group_id, array $data ) { - parent::__construct( "/{$product_group_id}/products", 'POST' ); + public function __construct( string $product_catalog_id, array $data ) { + parent::__construct( "/{$product_catalog_id}/products", 'POST' ); parent::set_data( $data ); } } diff --git a/includes/Admin.php b/includes/Admin.php index 62c13f5a6..371693415 100644 --- a/includes/Admin.php +++ b/includes/Admin.php @@ -1177,7 +1177,7 @@ public function add_product_settings_tab_content() { // 'id' attribute needs to match the 'target' parameter set above ?>
-
+
__( 'Choose whether to sync this product to Facebook and, if synced, whether it should be visible in the catalog.', 'facebook-for-woocommerce' ), ) ); - + ?> +
+ + +
+ '; echo '