From 62c5b39ebb3b076b969c6f22802efa8581e031b9 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Mon, 10 Feb 2025 16:45:13 -0800 Subject: [PATCH 01/25] - Adding support for MICE IFRAME in the WooC Extension Preliminary diff (using some Magento public primitives) allowing MICE Splash Page to be loaded in WooC Extension Next steps: 1. Implement token storage 2. Implement MICE Management page --- .../facebook-for-woocommerce-connection.css | 82 +------------- .../Admin/Settings_Screens/Connection.php | 107 ++++++++++-------- includes/Handlers/MetaExtension.php | 63 +++++++++++ 3 files changed, 126 insertions(+), 126 deletions(-) create mode 100644 includes/Handlers/MetaExtension.php diff --git a/assets/css/admin/facebook-for-woocommerce-connection.css b/assets/css/admin/facebook-for-woocommerce-connection.css index c054b0c36..df8894f53 100644 --- a/assets/css/admin/facebook-for-woocommerce-connection.css +++ b/assets/css/admin/facebook-for-woocommerce-connection.css @@ -1,78 +1,8 @@ -#wc-facebook-connection-box { - width: 50%; - min-width: 720px; - color: #050505; - background: #FFF url( '../../images/background.png' ) no-repeat; - background-position: right 30px center; - padding: 10px 15px 20px; - box-shadow: 0 0 3px rgba( 0, 0, 0, 0.2 ); - margin-bottom: 20px; +#facebook-commerce-iframe { + width: 100%; + min-height: calc(100vh - 200px); } -#wc-facebook-connection-box .logo { - width: 40px; - height: 40px; - background-image: url( '../../images/logo.png' ); - background-size: 100%; - margin-bottom: 10px; -} - -#wc-facebook-connection-box h1 { - font-weight: bold; - font-size: 1.5em; -} -#wc-facebook-connection-box h2 { - font-size: 1.1em; - color: rgb(162, 162, 162); - margin: 0; -} - -#wc-facebook-connection-box .benefits { - padding-top: 10px; - margin-bottom: 20px; -} - -#wc-facebook-connection-box .benefits .benefit { - margin: 0 0 6px 0; - padding-top: 5px; - padding-left: 35px; - min-height: 26px; - background-size: 26px; - background-repeat: no-repeat; -} - -#wc-facebook-connection-box .benefits .benefit-0 { - background-image: url( '../../images/icon-0.png' ); -} - -#wc-facebook-connection-box .benefits .benefit-1 { - background-image: url( '../../images/icon-1.png' ); -} - -#wc-facebook-connection-box .benefits .benefit-2 { - background-image: url( '../../images/icon-2.png' ); -} - -#wc-facebook-connection-box .actions { - display: flex; - align-items: center; -} - -#wc-facebook-connection-box .button { - color: rgb( 5, 5, 5 ); - background-color: rgb( 228, 230, 235 ); - border: none; - border-radius: 6px; - padding: 4px 4em; - margin-right: 10px; - font-weight: bold; -} - -#wc-facebook-connection-box .button.button-primary { - color: white; - background-color: rgb( 24, 119, 242 ); -} - -#wc-facebook-connection-box .uninstall { - vertical-align: middle; -} +.woocommerce-embed-page #wpbody-content { + padding-bottom: 0; +} \ No newline at end of file diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 975b44352..2ce11d10b 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -25,6 +25,15 @@ class Connection extends Abstract_Settings_Screen { /** @var string screen ID */ const ID = 'connection'; + /** @var string Facebook app ID from Connection handler */ + const APP_ID = \WooCommerce\Facebook\Handlers\Connection::CLIENT_ID; + + /** @var string Business name */ + const BUSINESS_NAME = 'WooCommerce'; + + /** @var string Client token */ + const CLIENT_TOKEN = '195311308289826|52dcd04d6c7ed113121b5eb4be23b4a7'; + /** * Connection constructor. @@ -38,6 +47,9 @@ public function __construct() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_notices', array( $this, 'add_notices' ) ); + + // Add action to enqueue the message handler script + add_action('admin_footer', array($this, 'render_message_handler')); } @@ -255,60 +267,55 @@ class="dashicons dashicons-external" * @param bool $is_connected whether the plugin is connected */ private function render_facebook_box( $is_connected ) { - - if ( $is_connected ) { - $title = __( 'Reach the Right People and Sell More Online', 'facebook-for-woocommerce' ); - } else { - $title = __( 'Grow your business on Facebook', 'facebook-for-woocommerce' ); - } - - $subtitle = __( 'Use this WooCommerce and Facebook integration to:', 'facebook-for-woocommerce' ); - $benefits = array( - __( 'Create an ad in a few steps', 'facebook-for-woocommerce' ), - __( 'Use built-in best practices for online sales', 'facebook-for-woocommerce' ), - __( 'Get reporting on sales and revenue', 'facebook-for-woocommerce' ), + $connection = facebook_for_woocommerce()->get_connection_handler(); + $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generateIframeSplashUrl( + $is_connected, + $connection->get_plugin(), + $connection->get_external_business_id() ); - ?> + + - - - -

-

- - - -
- - - - - - - - - - - - - - - - -
- - + /** + * Renders the message handler script in the footer. + * + * @since 2.0.0 + */ + public function render_message_handler() { + if (!$this->is_current_screen_page()) { + return; + } + ?> + home_url(), + 'admin_url' => admin_url(), + 'client_version' => $plugin->get_version(), + 'commerce_partner_seller_platform_type' => 'MAGENTO_OPEN_SOURCE', + 'country_code' => WC()->countries->get_base_country(), + ); + return add_query_arg( + array( + 'access_client_token' => self::CLIENT_TOKEN, + 'business_vertical' => 'ECOMMERCE', + 'channel' => 'COMMERCE', + 'app_id' => Connection::CLIENT_ID, + 'business_name' => self::BUSINESS_NAME, + 'currency' => get_woocommerce_currency(), + 'timezone' => 'America/Los_Angeles', + 'external_business_id' => $external_business_id, + 'installed' => $is_connected, + 'external_client_metadata' => rawurlencode( json_encode( $external_client_metadata ) ), + ), + 'https://www.commercepartnerhub.com/commerce_extension/splash/' + ); + } +} \ No newline at end of file From 9564eb4893cfeaf7b42f0cc4f7fa66d71f984966 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Mon, 10 Feb 2025 16:45:13 -0800 Subject: [PATCH 02/25] - Adding support for MICE IFRAME in the WooC Extension Preliminary diff (using some Magento public primitives) allowing MICE Splash Page to be loaded in WooC Extension Next steps: 1. Implement token storage 2. Implement MICE Management page --- .../facebook-for-woocommerce-connection.css | 82 ++-------------- .../Admin/Settings_Screens/Connection.php | 94 ++++++++++++++++--- includes/Handlers/MetaExtension.php | 67 +++++++++++++ 3 files changed, 153 insertions(+), 90 deletions(-) create mode 100644 includes/Handlers/MetaExtension.php diff --git a/assets/css/admin/facebook-for-woocommerce-connection.css b/assets/css/admin/facebook-for-woocommerce-connection.css index c054b0c36..df8894f53 100644 --- a/assets/css/admin/facebook-for-woocommerce-connection.css +++ b/assets/css/admin/facebook-for-woocommerce-connection.css @@ -1,78 +1,8 @@ -#wc-facebook-connection-box { - width: 50%; - min-width: 720px; - color: #050505; - background: #FFF url( '../../images/background.png' ) no-repeat; - background-position: right 30px center; - padding: 10px 15px 20px; - box-shadow: 0 0 3px rgba( 0, 0, 0, 0.2 ); - margin-bottom: 20px; +#facebook-commerce-iframe { + width: 100%; + min-height: calc(100vh - 200px); } -#wc-facebook-connection-box .logo { - width: 40px; - height: 40px; - background-image: url( '../../images/logo.png' ); - background-size: 100%; - margin-bottom: 10px; -} - -#wc-facebook-connection-box h1 { - font-weight: bold; - font-size: 1.5em; -} -#wc-facebook-connection-box h2 { - font-size: 1.1em; - color: rgb(162, 162, 162); - margin: 0; -} - -#wc-facebook-connection-box .benefits { - padding-top: 10px; - margin-bottom: 20px; -} - -#wc-facebook-connection-box .benefits .benefit { - margin: 0 0 6px 0; - padding-top: 5px; - padding-left: 35px; - min-height: 26px; - background-size: 26px; - background-repeat: no-repeat; -} - -#wc-facebook-connection-box .benefits .benefit-0 { - background-image: url( '../../images/icon-0.png' ); -} - -#wc-facebook-connection-box .benefits .benefit-1 { - background-image: url( '../../images/icon-1.png' ); -} - -#wc-facebook-connection-box .benefits .benefit-2 { - background-image: url( '../../images/icon-2.png' ); -} - -#wc-facebook-connection-box .actions { - display: flex; - align-items: center; -} - -#wc-facebook-connection-box .button { - color: rgb( 5, 5, 5 ); - background-color: rgb( 228, 230, 235 ); - border: none; - border-radius: 6px; - padding: 4px 4em; - margin-right: 10px; - font-weight: bold; -} - -#wc-facebook-connection-box .button.button-primary { - color: white; - background-color: rgb( 24, 119, 242 ); -} - -#wc-facebook-connection-box .uninstall { - vertical-align: middle; -} +.woocommerce-embed-page #wpbody-content { + padding-bottom: 0; +} \ No newline at end of file diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 975b44352..687d55d30 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -21,10 +21,12 @@ */ class Connection extends Abstract_Settings_Screen { - + /** @var string screen ID */ const ID = 'connection'; + /** @var bool feature flag for new iframe implementation */ + const USE_IFRAME_CONNECTION = true; /** * Connection constructor. @@ -38,6 +40,9 @@ public function __construct() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_notices', array( $this, 'add_notices' ) ); + + // Add action to enqueue the message handler script + add_action('admin_footer', array($this, 'render_message_handler')); } @@ -126,7 +131,7 @@ public function render() { * TODO: add pixel & ad account API retrieval when we gain the ads_management permission * TODO: add the page name and link when we gain the manage_pages permission */ - $static_items = array( + $static_items = self::USE_IFRAME_CONNECTION ? array() : array( 'page' => array( 'label' => __( 'Page', 'facebook-for-woocommerce' ), 'value' => facebook_for_woocommerce()->get_integration()->get_facebook_page_id(), @@ -255,7 +260,47 @@ class="dashicons dashicons-external" * @param bool $is_connected whether the plugin is connected */ private function render_facebook_box( $is_connected ) { + if (self::USE_IFRAME_CONNECTION) { + $this->render_facebook_box_iframe($is_connected); + } else { + $this->render_facebook_box_legacy($is_connected); + } + } + + /** + * Renders the Facebook CTA box using iframe implementation. + * + * @since 2.0.0 + * + * @param bool $is_connected whether the plugin is connected + */ + private function render_facebook_box_iframe( $is_connected ) { + $connection = facebook_for_woocommerce()->get_connection_handler(); + $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generateIframeSplashUrl( + $is_connected, + $connection->get_plugin(), + $connection->get_external_business_id() + ); + ?> + + -
- -

-
    $benefit ) : ?>
-
- - @@ -296,19 +334,47 @@ function confirmDialog() { return confirm( 'Are you sure you want to disconnect from Facebook?' ); } - - - -
-
+ is_current_screen_page()) { + return; + } + + ?> + wc_get_page_permalink('shop') ?: home_url(), + 'admin_url' => admin_url(), + 'client_version' => $plugin->get_version(), + 'commerce_partner_seller_platform_type' => 'MAGENTO_OPEN_SOURCE', + 'country_code' => WC()->countries->get_base_country(), + ); + return add_query_arg( + array( + 'access_client_token' => self::CLIENT_TOKEN, + 'business_vertical' => 'ECOMMERCE', + 'channel' => 'COMMERCE', + 'app_id' => Connection::CLIENT_ID, + 'business_name' => self::BUSINESS_NAME, + 'currency' => get_woocommerce_currency(), + 'timezone' => 'America/Los_Angeles', + 'external_business_id' => $external_business_id, + 'installed' => $is_connected, + 'external_client_metadata' => rawurlencode( json_encode( $external_client_metadata ) ), + ), + 'https://www.commercepartnerhub.com/commerce_extension/splash/' + ); + } +} \ No newline at end of file From afa1975e3e4751456c8b21ac875295512252c503 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Thu, 13 Feb 2025 12:56:02 -0800 Subject: [PATCH 03/25] Changing iframe connection setting to use a function instead of a const for easier testing. --- includes/Admin/Settings_Screens/Connection.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 687d55d30..56ce7eecf 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -21,12 +21,19 @@ */ class Connection extends Abstract_Settings_Screen { - /** @var string screen ID */ const ID = 'connection'; - /** @var bool feature flag for new iframe implementation */ - const USE_IFRAME_CONNECTION = true; + /** + * Determines if the iframe connection should be used. + * + * @since 2.0.0 + * + * @return bool + */ + protected function use_iframe_connection() { + return true; + } /** * Connection constructor. @@ -131,7 +138,7 @@ public function render() { * TODO: add pixel & ad account API retrieval when we gain the ads_management permission * TODO: add the page name and link when we gain the manage_pages permission */ - $static_items = self::USE_IFRAME_CONNECTION ? array() : array( + $static_items = $this->use_iframe_connection() ? array() : array( 'page' => array( 'label' => __( 'Page', 'facebook-for-woocommerce' ), 'value' => facebook_for_woocommerce()->get_integration()->get_facebook_page_id(), @@ -260,7 +267,7 @@ class="dashicons dashicons-external" * @param bool $is_connected whether the plugin is connected */ private function render_facebook_box( $is_connected ) { - if (self::USE_IFRAME_CONNECTION) { + if ($this->use_iframe_connection()) { $this->render_facebook_box_iframe($is_connected); } else { $this->render_facebook_box_legacy($is_connected); From 08523588bb11a8b712c416e7cb357ccd7db589b2 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Thu, 13 Feb 2025 18:02:13 -0800 Subject: [PATCH 04/25] Adding new rest endpoints to capture locally stored token and config data --- class-wc-facebookcommerce.php | 1 + .../Admin/Settings_Screens/Connection.php | 260 ++++++++++-------- includes/Handlers/MetaExtension.php | 139 +++++++++- 3 files changed, 285 insertions(+), 115 deletions(-) diff --git a/class-wc-facebookcommerce.php b/class-wc-facebookcommerce.php index 119b63133..00b89a326 100644 --- a/class-wc-facebookcommerce.php +++ b/class-wc-facebookcommerce.php @@ -212,6 +212,7 @@ public function init() { } $this->connection_handler = new WooCommerce\Facebook\Handlers\Connection( $this ); + new WooCommerce\Facebook\Handlers\MetaExtension(); $this->webhook_handler = new WooCommerce\Facebook\Handlers\WebHook( $this ); $this->tracker = new WooCommerce\Facebook\Utilities\Tracker(); diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 56ce7eecf..1e4707ac7 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -11,7 +11,7 @@ namespace WooCommerce\Facebook\Admin\Settings_Screens; -defined( 'ABSPATH' ) or exit; +defined('ABSPATH') or exit; use WooCommerce\Facebook\Admin\Abstract_Settings_Screen; use WooCommerce\Facebook\Framework\Api\Exception as ApiException; @@ -19,7 +19,8 @@ /** * The Connection settings screen object. */ -class Connection extends Abstract_Settings_Screen { +class Connection extends Abstract_Settings_Screen +{ /** @var string screen ID */ const ID = 'connection'; @@ -31,22 +32,24 @@ class Connection extends Abstract_Settings_Screen { * * @return bool */ - protected function use_iframe_connection() { + protected function use_iframe_connection() + { return true; } /** * Connection constructor. */ - public function __construct() { + public function __construct() + { $this->id = self::ID; - $this->label = __( 'Connection', 'facebook-for-woocommerce' ); - $this->title = __( 'Connection', 'facebook-for-woocommerce' ); + $this->label = __('Connection', 'facebook-for-woocommerce'); + $this->title = __('Connection', 'facebook-for-woocommerce'); - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); + add_action('admin_enqueue_scripts', array($this, 'enqueue_assets')); - add_action( 'admin_notices', array( $this, 'add_notices' ) ); + add_action('admin_notices', array($this, 'add_notices')); // Add action to enqueue the message handler script add_action('admin_footer', array($this, 'render_message_handler')); @@ -60,19 +63,20 @@ public function __construct() { * * @since 2.0.0 */ - public function add_notices() { + public function add_notices() + { // display a notice if the connection has previously failed - if ( get_transient( 'wc_facebook_connection_failed' ) ) { + if (get_transient('wc_facebook_connection_failed')) { $message = sprintf( /* translators: Placeholders: %1$s - tag, %2$s - tag, %3$s - tag, %4$s - tag, %5$s - tag, %6$s - tag */ - __( '%1$sHeads up!%2$s It looks like there was a problem with reconnecting your site to Facebook. Please %3$sclick here%4$s to try again, or %5$sget in touch with our support team%6$s for assistance.', 'facebook-for-woocommerce' ), + __('%1$sHeads up!%2$s It looks like there was a problem with reconnecting your site to Facebook. Please %3$sclick here%4$s to try again, or %5$sget in touch with our support team%6$s for assistance.', 'facebook-for-woocommerce'), '', '', - '', + '', '', - '', + '', '' ); @@ -84,7 +88,7 @@ public function add_notices() { ) ); - delete_transient( 'wc_facebook_connection_failed' ); + delete_transient('wc_facebook_connection_failed'); } } @@ -96,13 +100,14 @@ public function add_notices() { * * @since 2.0.0 */ - public function enqueue_assets() { + public function enqueue_assets() + { - if ( ! $this->is_current_screen_page() ) { + if (! $this->is_current_screen_page()) { return; } - wp_enqueue_style( 'wc-facebook-admin-connection-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-connection.css', array(), \WC_Facebookcommerce::VERSION ); + wp_enqueue_style('wc-facebook-admin-connection-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-connection.css', array(), \WC_Facebookcommerce::VERSION); } @@ -111,15 +116,16 @@ public function enqueue_assets() { * * @since 2.0.0 */ - public function render() { + public function render() + { $is_connected = facebook_for_woocommerce()->get_connection_handler()->is_connected(); // always render the CTA box - $this->render_facebook_box( $is_connected ); + $this->render_facebook_box($is_connected); // don't proceed further if not connected - if ( ! $is_connected ) { + if (! $is_connected) { return; } @@ -140,46 +146,46 @@ public function render() { */ $static_items = $this->use_iframe_connection() ? array() : array( 'page' => array( - 'label' => __( 'Page', 'facebook-for-woocommerce' ), + 'label' => __('Page', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_integration()->get_facebook_page_id(), ), 'pixel' => array( - 'label' => __( 'Pixel', 'facebook-for-woocommerce' ), + 'label' => __('Pixel', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(), ), 'catalog' => array( - 'label' => __( 'Catalog', 'facebook-for-woocommerce' ), + 'label' => __('Catalog', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_integration()->get_product_catalog_id(), 'url' => 'https://facebook.com/products', ), 'business-manager' => array( - 'label' => __( 'Business Manager account', 'facebook-for-woocommerce' ), + 'label' => __('Business Manager account', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_connection_handler()->get_business_manager_id(), ), 'ad-account' => array( - 'label' => __( 'Ad Manager account', 'facebook-for-woocommerce' ), + 'label' => __('Ad Manager account', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_connection_handler()->get_ad_account_id(), ), 'instagram-business-id' => array( - 'label' => __( 'Instagram Business ID', 'facebook-for-woocommerce' ), + 'label' => __('Instagram Business ID', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_connection_handler()->get_instagram_business_id(), ), 'commerce-merchant-settings-id' => array( - 'label' => __( 'Commerce Merchant Settings ID', 'facebook-for-woocommerce' ), + 'label' => __('Commerce Merchant Settings ID', 'facebook-for-woocommerce'), 'value' => facebook_for_woocommerce()->get_connection_handler()->get_commerce_merchant_settings_id(), ), ); // if the catalog ID is set, update the URL and try to get its name for display $catalog_id = $static_items['catalog']['value']; - if ( !empty( $catalog_id ) ) { + if (!empty($catalog_id)) { $static_items['catalog']['url'] = "https://www.facebook.com/commerce/catalogs/{$catalog_id}/products/"; try { - $response = facebook_for_woocommerce()->get_api()->get_catalog( $catalog_id ); - if ( $name = $response->name ) { + $response = facebook_for_woocommerce()->get_api()->get_catalog($catalog_id); + if ($name = $response->name) { $static_items['catalog']['value'] = $name; } - } catch ( ApiException $exception ) { + } catch (ApiException $exception) { // Log the exception with additional information facebook_for_woocommerce()->log( sprintf( @@ -191,13 +197,13 @@ public function render() { } } - ?> +?> $item ) : + foreach ($static_items as $id => $item) : $item = wp_parse_args( $item, @@ -208,36 +214,35 @@ public function render() { ) ); - ?> + ?> - +
- use_iframe_connection()) { $this->render_facebook_box_iframe($is_connected); } else { @@ -281,23 +287,23 @@ private function render_facebook_box( $is_connected ) { * * @param bool $is_connected whether the plugin is connected */ - private function render_facebook_box_iframe( $is_connected ) { + private function render_facebook_box_iframe($is_connected) + { $connection = facebook_for_woocommerce()->get_connection_handler(); $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generateIframeSplashUrl( $is_connected, $connection->get_plugin(), $connection->get_external_business_id() ); - ?> - - + + ?>
-

-

+

+

    - $benefit ) : ?> -
  • + $benefit) : ?> +
- - - + + + - - + +
- is_current_screen_page()) { return; } - - ?> + ?> - __( 'Debug', 'facebook-for-woocommerce' ), + 'title' => __('Debug', 'facebook-for-woocommerce'), 'type' => 'title', ), array( 'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE, - 'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ), + 'title' => __('Enable debug mode', 'facebook-for-woocommerce'), 'type' => 'checkbox', - 'desc' => __( 'Log plugin events for debugging.', 'facebook-for-woocommerce' ), + 'desc' => __('Log plugin events for debugging.', 'facebook-for-woocommerce'), /** * Translators: %s URL to the documentation page. */ - 'desc_tip' => sprintf( __( 'Only enable this if you are experiencing problems with the plugin. Learn more.', 'facebook-for-woocommerce' ), 'https://woocommerce.com/document/facebook-for-woocommerce/#debug-tools' ), + 'desc_tip' => sprintf(__('Only enable this if you are experiencing problems with the plugin. Learn more.', 'facebook-for-woocommerce'), 'https://woocommerce.com/document/facebook-for-woocommerce/#debug-tools'), 'default' => 'no', ), array( 'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR, - 'title' => __( 'Experimental! Enable new style feed generation', 'facebook-for-woocommerce' ), + 'title' => __('Experimental! Enable new style feed generation', 'facebook-for-woocommerce'), 'type' => 'checkbox', - 'desc' => __( 'Use new, memory improved, feed generation process.', 'facebook-for-woocommerce' ), + 'desc' => __('Use new, memory improved, feed generation process.', 'facebook-for-woocommerce'), /** * Translators: %s URL to the documentation page. */ - 'desc_tip' => sprintf( __( 'This is an experimental feature in testing phase. Only enable this if you are experiencing problems with feed generation. Learn more.', 'facebook-for-woocommerce' ), 'https://woocommerce.com/document/facebook-for-woocommerce/#feed-generation' ), + 'desc_tip' => sprintf(__('This is an experimental feature in testing phase. Only enable this if you are experiencing problems with feed generation. Learn more.', 'facebook-for-woocommerce'), 'https://woocommerce.com/document/facebook-for-woocommerce/#feed-generation'), 'default' => 'no', ), - array( 'type' => 'sectionend' ), + array('type' => 'sectionend'), ); } - - } diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index 60d690de0..f28ae9e0d 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -12,15 +12,19 @@ namespace WooCommerce\Facebook\Handlers; -defined( 'ABSPATH' ) or exit; +defined('ABSPATH') or exit; use WooCommerce\Facebook\Handlers\Connection; +use WP_REST_Request; +use WP_REST_Response; +use WP_Error; -class MetaExtension { +class MetaExtension +{ /** @var string Client token */ const CLIENT_TOKEN = '195311308289826|52dcd04d6c7ed113121b5eb4be23b4a7'; - const APP_ID = '474166926521348'; + const APP_ID = '474166926521348'; /** @var string Business name */ const BUSINESS_NAME = 'WooCommerce'; @@ -29,7 +33,10 @@ class MetaExtension { * * @since 2.0.0 */ - public function __construct() { + public function __construct() + { + add_action('wp_ajax_wc_facebook_update_tokens', array(__CLASS__, 'ajax_update_fb_settings')); + add_action('rest_api_init', array(__CLASS__, 'init_rest_endpoint')); } /** @@ -40,7 +47,8 @@ public function __construct() { * @param string $external_business_id External business ID. * @return string */ - public static function generateIframeSplashUrl( $is_connected, $plugin, $external_business_id ) { + public static function generateIframeSplashUrl($is_connected, $plugin, $external_business_id) + { $external_client_metadata = array( 'shop_domain' => wc_get_page_permalink('shop') ?: home_url(), 'admin_url' => admin_url(), @@ -59,9 +67,126 @@ public static function generateIframeSplashUrl( $is_connected, $plugin, $externa 'timezone' => 'America/Los_Angeles', 'external_business_id' => $external_business_id, 'installed' => $is_connected, - 'external_client_metadata' => rawurlencode( json_encode( $external_client_metadata ) ), + 'external_client_metadata' => rawurlencode(json_encode($external_client_metadata)), ), 'https://www.commercepartnerhub.com/commerce_extension/splash/' ); } -} \ No newline at end of file + + /** + * AJAX endpoint to update Facebook settings with authenticated tokens. + * + * Expects POST parameters: + * - nonce: security nonce. + * - access_token: system user access token. + * - merchant_access_token: merchant access token. + * - page_access_token: page access token. + * - product_catalog_id: product catalog ID (optional). + * - pixel_id: pixel ID (optional). + * + * @return void JSON response. + */ + public static function ajax_update_fb_settings() + { + // Ensure the current user can manage WooCommerce settings. + if (! current_user_can('manage_woocommerce')) { + wp_send_json_error(array('message' => __('Unauthorized request', 'facebook-for-woocommerce'))); + } + + // Validate the nonce. + $nonce = isset($_POST['nonce']) ? sanitize_key(wp_unslash($_POST['nonce'])) : ''; + if (empty($nonce) || ! wp_verify_nonce($nonce, 'wc_facebook_ajax_token_update')) { + wp_send_json_error(array('message' => __('Invalid nonce', 'facebook-for-woocommerce'))); + } + + // Sanitize and retrieve POST data. + $access_token = isset($_POST['access_token']) ? sanitize_text_field(wp_unslash($_POST['access_token'])) : ''; + $merchant_access_token = isset($_POST['merchant_access_token']) ? sanitize_text_field(wp_unslash($_POST['merchant_access_token'])) : ''; + $page_access_token = isset($_POST['page_access_token']) ? sanitize_text_field(wp_unslash($_POST['page_access_token'])) : ''; + $product_catalog_id = isset($_POST['product_catalog_id']) ? sanitize_text_field(wp_unslash($_POST['product_catalog_id'])) : ''; + $pixel_id = isset($_POST['pixel_id']) ? sanitize_text_field(wp_unslash($_POST['pixel_id'])) : ''; + + // Validate required tokens. + if (empty($access_token) || empty($merchant_access_token) || empty($page_access_token)) { + wp_send_json_error(array('message' => __('Missing required token data', 'facebook-for-woocommerce'))); + } + + // Update Facebook settings via options. + update_option('wc_facebook_access_token', $access_token); + update_option('wc_facebook_merchant_access_token', $merchant_access_token); + update_option('wc_facebook_page_access_token', $page_access_token); + update_option('wc_facebook_product_catalog_id', $product_catalog_id); + update_option('wc_facebook_pixel_id', $pixel_id); + + wp_send_json_success(array('message' => __('Facebook settings updated successfully', 'facebook-for-woocommerce'))); + } + + /** + * REST API endpoint initialization. + * + * @return void + */ + public static function init_rest_endpoint() + { + register_rest_route( + 'wc-facebook/v1', + 'update_tokens', + array( + 'methods' => 'POST', + 'callback' => array(__CLASS__, 'rest_update_fb_tokens'), + 'permission_callback' => array(__CLASS__, 'rest_update_fb_tokens_permission_callback'), + ) + ); + } + + /** + * Permission callback for the REST API endpoint. + * + * @param WP_REST_Request $request The request object. + * @return bool + */ + public static function rest_update_fb_tokens_permission_callback($request) + { + return current_user_can('manage_woocommerce'); + } + + /** + * REST API endpoint callback to update Facebook settings. + * + * Expects POST parameters: + * - nonce: security nonce. + * - access_token: system user access token. + * - merchant_access_token: merchant access token. + * - page_access_token: page access token. + * - product_catalog_id: product catalog ID (optional). + * - pixel_id: pixel ID (optional). + * + * @param WP_REST_Request $request The request object. + * @return WP_REST_Response|WP_Error + */ + public static function rest_update_fb_tokens(WP_REST_Request $request) + { + // Get JSON data from request body + $params = $request->get_json_params(); + + // Sanitize and retrieve data + $access_token = isset($params['access_token']) ? sanitize_text_field($params['access_token']) : ''; + $merchant_access_token = isset($params['merchant_access_token']) ? sanitize_text_field($params['merchant_access_token']) : ''; + $page_access_token = isset($params['page_access_token']) ? sanitize_text_field($params['page_access_token']) : ''; + $product_catalog_id = isset($params['product_catalog_id']) ? sanitize_text_field($params['product_catalog_id']) : ''; + $pixel_id = isset($params['pixel_id']) ? sanitize_text_field($params['pixel_id']) : ''; + + if (empty($access_token) || empty($merchant_access_token) || empty($page_access_token)) { + return new WP_Error('missing_token', __('Missing required token data', 'facebook-for-woocommerce'), array('status' => 400)); + } + + // Update the options + update_option('wc_facebook_access_token', $access_token); + update_option('wc_facebook_merchant_access_token', $merchant_access_token); + update_option('wc_facebook_page_access_token', $page_access_token); + update_option('wc_facebook_product_catalog_id', $product_catalog_id); + update_option('wc_facebook_pixel_id', $pixel_id); + + return new WP_REST_Response(array('success' => true, 'message' => __('Facebook settings updated successfully', 'facebook-for-woocommerce')), 200); + } +} From 24bd466359ad0a504fe59c48c7cba2b0b299f1dc Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Thu, 13 Feb 2025 18:03:24 -0800 Subject: [PATCH 05/25] restoring css --- .../facebook-for-woocommerce-connection.css | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/assets/css/admin/facebook-for-woocommerce-connection.css b/assets/css/admin/facebook-for-woocommerce-connection.css index df8894f53..03a1016b8 100644 --- a/assets/css/admin/facebook-for-woocommerce-connection.css +++ b/assets/css/admin/facebook-for-woocommerce-connection.css @@ -1,3 +1,82 @@ +#wc-facebook-connection-box { + width: 50%; + min-width: 720px; + color: #050505; + background: #FFF url( '../../images/background.png' ) no-repeat; + background-position: right 30px center; + padding: 10px 15px 20px; + box-shadow: 0 0 3px rgba( 0, 0, 0, 0.2 ); + margin-bottom: 20px; +} + +#wc-facebook-connection-box .logo { + width: 40px; + height: 40px; + background-image: url( '../../images/logo.png' ); + background-size: 100%; + margin-bottom: 10px; +} + +#wc-facebook-connection-box h1 { + font-weight: bold; + font-size: 1.5em; +} +#wc-facebook-connection-box h2 { + font-size: 1.1em; + color: rgb(162, 162, 162); + margin: 0; +} + +#wc-facebook-connection-box .benefits { + padding-top: 10px; + margin-bottom: 20px; +} + +#wc-facebook-connection-box .benefits .benefit { + margin: 0 0 6px 0; + padding-top: 5px; + padding-left: 35px; + min-height: 26px; + background-size: 26px; + background-repeat: no-repeat; +} + +#wc-facebook-connection-box .benefits .benefit-0 { + background-image: url( '../../images/icon-0.png' ); +} + +#wc-facebook-connection-box .benefits .benefit-1 { + background-image: url( '../../images/icon-1.png' ); +} + +#wc-facebook-connection-box .benefits .benefit-2 { + background-image: url( '../../images/icon-2.png' ); +} + +#wc-facebook-connection-box .actions { + display: flex; + align-items: center; +} + +#wc-facebook-connection-box .button { + color: rgb( 5, 5, 5 ); + background-color: rgb( 228, 230, 235 ); + border: none; + border-radius: 6px; + padding: 4px 4em; + margin-right: 10px; + font-weight: bold; +} + +#wc-facebook-connection-box .button.button-primary { + color: white; + background-color: rgb( 24, 119, 242 ); +} + +#wc-facebook-connection-box .uninstall { + vertical-align: middle; +} + #facebook-commerce-iframe { width: 100%; min-height: calc(100vh - 200px); From 8f62598f764cd89aae75cf7e369c0ff13660dbf0 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Tue, 18 Feb 2025 15:16:28 -0800 Subject: [PATCH 06/25] Adding functionality to render the management page view --- .../Admin/Settings_Screens/Connection.php | 13 ++++ includes/Handlers/MetaExtension.php | 60 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 1e4707ac7..88f5105cd 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -129,6 +129,19 @@ public function render() return; } + // Check if we have a merchant access token + $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); + + if (!empty($merchant_access_token)) { + // Render the management iframe + $connection = facebook_for_woocommerce()->get_connection_handler(); + \WooCommerce\Facebook\Handlers\MetaExtension::render_management_iframe( + $connection->get_plugin(), + $connection->get_external_business_id() + ); + return; + } + /** * Build the basic static elements. * diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index f28ae9e0d..b1cd05935 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -189,4 +189,64 @@ public static function rest_update_fb_tokens(WP_REST_Request $request) return new WP_REST_Response(array('success' => true, 'message' => __('Facebook settings updated successfully', 'facebook-for-woocommerce')), 200); } + + /** + * Generates the Commerce Hub iframe management page URL. + * + * @param object $plugin The plugin instance. + * @param string $external_business_id External business ID. + * @return string + */ + public static function generateIframeManagementUrl($plugin, $external_business_id) + { + $external_client_metadata = array( + 'shop_domain' => wc_get_page_permalink('shop') ?: home_url(), + 'admin_url' => admin_url(), + 'client_version' => $plugin->get_version(), + 'commerce_partner_seller_platform_type' => 'MAGENTO_OPEN_SOURCE', + 'country_code' => WC()->countries->get_base_country(), + ); + + // Get the stored merchant access token + $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); + + return add_query_arg( + array( + 'access_client_token' => self::CLIENT_TOKEN, + 'business_vertical' => 'ECOMMERCE', + 'channel' => 'COMMERCE', + 'app_id' => self::APP_ID, + 'business_name' => self::BUSINESS_NAME, + 'currency' => get_woocommerce_currency(), + 'timezone' => 'America/Los_Angeles', + 'external_business_id' => $external_business_id, + 'installed' => true, // This is the management view + 'external_client_metadata' => rawurlencode(json_encode($external_client_metadata)), + 'merchant_access_token' => $merchant_access_token, + ), + 'https://www.commercepartnerhub.com/commerce_extension/management/' + ); + } + + /** + * Renders the management iframe. + * + * @param object $plugin The plugin instance. + * @param string $external_business_id External business ID. + * @return void + */ + public static function render_management_iframe($plugin, $external_business_id) + { + $iframe_url = self::generateIframeManagementUrl($plugin, $external_business_id); + ?> + + Date: Tue, 18 Feb 2025 16:52:17 -0800 Subject: [PATCH 07/25] Fleshing out calls, ensuring correct gating of logic --- .../Admin/Settings_Screens/Connection.php | 92 +++++----- includes/Handlers/MetaExtension.php | 157 ++++++++++++++---- 2 files changed, 165 insertions(+), 84 deletions(-) diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 88f5105cd..c4d533835 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -119,20 +119,10 @@ public function enqueue_assets() public function render() { - $is_connected = facebook_for_woocommerce()->get_connection_handler()->is_connected(); - - // always render the CTA box - $this->render_facebook_box($is_connected); - - // don't proceed further if not connected - if (! $is_connected) { - return; - } - // Check if we have a merchant access token $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); - if (!empty($merchant_access_token)) { + if (!empty($merchant_access_token) && $this->use_iframe_connection()) { // Render the management iframe $connection = facebook_for_woocommerce()->get_connection_handler(); \WooCommerce\Facebook\Handlers\MetaExtension::render_management_iframe( @@ -142,6 +132,18 @@ public function render() return; } + $is_connected = facebook_for_woocommerce()->get_connection_handler()->is_connected(); + + // always render the CTA box + $this->render_facebook_box($is_connected); + + // don't proceed further if not connected + if (! $is_connected) { + return; + } + + + /** * Build the basic static elements. * @@ -381,52 +383,45 @@ public function render_message_handler() if (!$this->is_current_screen_page()) { return; } - ?> + + // Check if we have a merchant access token + $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); + + if (!empty($merchant_access_token)) { + return; + } +?> - - + + - is_current_screen_page() ) { return; } - ?> + ?> - __('Debug', 'facebook-for-woocommerce'), + 'title' => __( 'Debug', 'facebook-for-woocommerce' ), 'type' => 'title', ), array( 'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE, - 'title' => __('Enable debug mode', 'facebook-for-woocommerce'), + 'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ), 'type' => 'checkbox', 'desc' => __( 'Log plugin events for debugging.', 'facebook-for-woocommerce' ), /* translators: %s URL to the documentation page. */ @@ -459,14 +449,14 @@ public function get_settings() array( 'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR, - 'title' => __('Experimental! Enable new style feed generation', 'facebook-for-woocommerce'), + 'title' => __( 'Experimental! Enable new style feed generation', 'facebook-for-woocommerce' ), 'type' => 'checkbox', 'desc' => __( 'Use new, memory improved, feed generation process.', 'facebook-for-woocommerce' ), /* translators: %s URL to the documentation page. */ 'desc_tip' => sprintf( __( 'This is an experimental feature in testing phase. Only enable this if you are experiencing problems with feed generation. Learn more.', 'facebook-for-woocommerce' ), 'https://woocommerce.com/document/facebook-for-woocommerce/#feed-generation' ), 'default' => 'no', ), - array('type' => 'sectionend'), + array( 'type' => 'sectionend' ), ); } } diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index 7a03e98cd..f684d42b4 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -35,10 +35,9 @@ class MetaExtension { * * @since 2.0.0 */ - public function __construct() - { - add_action('wp_ajax_wc_facebook_update_tokens', array(__CLASS__, 'ajax_update_fb_settings')); - add_action('rest_api_init', array(__CLASS__, 'init_rest_endpoint')); + public function __construct() { + add_action( 'wp_ajax_wc_facebook_update_tokens', array( __CLASS__, 'ajax_update_fb_settings' ) ); + add_action( 'rest_api_init', array( __CLASS__, 'init_rest_endpoint' ) ); } /** @@ -87,39 +86,38 @@ public static function generate_iframe_splash_url( $is_connected, $plugin, $exte * * @return void JSON response. */ - public static function ajax_update_fb_settings() - { + public static function ajax_update_fb_settings() { // Ensure the current user can manage WooCommerce settings. - if (! current_user_can('manage_woocommerce')) { - wp_send_json_error(array('message' => __('Unauthorized request', 'facebook-for-woocommerce'))); + if ( ! current_user_can( 'manage_woocommerce' ) ) { + wp_send_json_error( array( 'message' => __( 'Unauthorized request', 'facebook-for-woocommerce' ) ) ); } // Validate the nonce. - $nonce = isset($_POST['nonce']) ? sanitize_key(wp_unslash($_POST['nonce'])) : ''; - if (empty($nonce) || ! wp_verify_nonce($nonce, 'wc_facebook_ajax_token_update')) { - wp_send_json_error(array('message' => __('Invalid nonce', 'facebook-for-woocommerce'))); + $nonce = isset( $_POST['nonce'] ) ? sanitize_key( wp_unslash( $_POST['nonce'] ) ) : ''; + if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wc_facebook_ajax_token_update' ) ) { + wp_send_json_error( array( 'message' => __( 'Invalid nonce', 'facebook-for-woocommerce' ) ) ); } // Sanitize and retrieve POST data. - $access_token = isset($_POST['access_token']) ? sanitize_text_field(wp_unslash($_POST['access_token'])) : ''; - $merchant_access_token = isset($_POST['merchant_access_token']) ? sanitize_text_field(wp_unslash($_POST['merchant_access_token'])) : ''; - $page_access_token = isset($_POST['page_access_token']) ? sanitize_text_field(wp_unslash($_POST['page_access_token'])) : ''; - $product_catalog_id = isset($_POST['product_catalog_id']) ? sanitize_text_field(wp_unslash($_POST['product_catalog_id'])) : ''; - $pixel_id = isset($_POST['pixel_id']) ? sanitize_text_field(wp_unslash($_POST['pixel_id'])) : ''; + $access_token = isset( $_POST['access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['access_token'] ) ) : ''; + $merchant_access_token = isset( $_POST['merchant_access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['merchant_access_token'] ) ) : ''; + $page_access_token = isset( $_POST['page_access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['page_access_token'] ) ) : ''; + $product_catalog_id = isset( $_POST['product_catalog_id'] ) ? sanitize_text_field( wp_unslash( $_POST['product_catalog_id'] ) ) : ''; + $pixel_id = isset( $_POST['pixel_id'] ) ? sanitize_text_field( wp_unslash( $_POST['pixel_id'] ) ) : ''; // Validate required tokens. - if (empty($access_token) || empty($merchant_access_token) || empty($page_access_token)) { - wp_send_json_error(array('message' => __('Missing required token data', 'facebook-for-woocommerce'))); + if ( empty( $access_token ) || empty( $merchant_access_token ) || empty( $page_access_token ) ) { + wp_send_json_error( array( 'message' => __( 'Missing required token data', 'facebook-for-woocommerce' ) ) ); } // Update Facebook settings via options. - update_option('wc_facebook_access_token', $access_token); - update_option('wc_facebook_merchant_access_token', $merchant_access_token); - update_option('wc_facebook_page_access_token', $page_access_token); - update_option('wc_facebook_product_catalog_id', $product_catalog_id); - update_option('wc_facebook_pixel_id', $pixel_id); + update_option( 'wc_facebook_access_token', $access_token ); + update_option( 'wc_facebook_merchant_access_token', $merchant_access_token ); + update_option( 'wc_facebook_page_access_token', $page_access_token ); + update_option( 'wc_facebook_product_catalog_id', $product_catalog_id ); + update_option( 'wc_facebook_pixel_id', $pixel_id ); - wp_send_json_success(array('message' => __('Facebook settings updated successfully', 'facebook-for-woocommerce'))); + wp_send_json_success( array( 'message' => __( 'Facebook settings updated successfully', 'facebook-for-woocommerce' ) ) ); } /** @@ -127,15 +125,14 @@ public static function ajax_update_fb_settings() * * @return void */ - public static function init_rest_endpoint() - { + public static function init_rest_endpoint() { register_rest_route( 'wc-facebook/v1', 'update_tokens', array( 'methods' => 'POST', - 'callback' => array(__CLASS__, 'rest_update_fb_tokens'), - 'permission_callback' => array(__CLASS__, 'rest_update_fb_tokens_permission_callback'), + 'callback' => array( __CLASS__, 'rest_update_fb_tokens' ), + 'permission_callback' => array( __CLASS__, 'rest_update_fb_tokens_permission_callback' ), ) ); } @@ -146,9 +143,8 @@ public static function init_rest_endpoint() * @param WP_REST_Request $request The request object. * @return bool */ - public static function rest_update_fb_tokens_permission_callback($request) - { - return current_user_can('manage_woocommerce'); + public static function rest_update_fb_tokens_permission_callback( $request ) { + return current_user_can( 'manage_woocommerce' ); } /** @@ -165,29 +161,34 @@ public static function rest_update_fb_tokens_permission_callback($request) * @param WP_REST_Request $request The request object. * @return WP_REST_Response|WP_Error */ - public static function rest_update_fb_tokens(WP_REST_Request $request) - { + public static function rest_update_fb_tokens( WP_REST_Request $request ) { // Get JSON data from request body $params = $request->get_json_params(); // Sanitize and retrieve data - $access_token = isset($params['access_token']) ? sanitize_text_field($params['access_token']) : ''; - $merchant_access_token = isset($params['merchant_access_token']) ? sanitize_text_field($params['merchant_access_token']) : ''; - $page_access_token = isset($params['page_access_token']) ? sanitize_text_field($params['page_access_token']) : ''; - $product_catalog_id = isset($params['product_catalog_id']) ? sanitize_text_field($params['product_catalog_id']) : ''; - $pixel_id = isset($params['pixel_id']) ? sanitize_text_field($params['pixel_id']) : ''; - - if (empty($access_token) || empty($merchant_access_token) || empty($page_access_token)) { - return new WP_Error('missing_token', __('Missing required token data', 'facebook-for-woocommerce'), array('status' => 400)); + $access_token = isset( $params['access_token'] ) ? sanitize_text_field( $params['access_token'] ) : ''; + $merchant_access_token = isset( $params['merchant_access_token'] ) ? sanitize_text_field( $params['merchant_access_token'] ) : ''; + $page_access_token = isset( $params['page_access_token'] ) ? sanitize_text_field( $params['page_access_token'] ) : ''; + $product_catalog_id = isset( $params['product_catalog_id'] ) ? sanitize_text_field( $params['product_catalog_id'] ) : ''; + $pixel_id = isset( $params['pixel_id'] ) ? sanitize_text_field( $params['pixel_id'] ) : ''; + + if ( empty( $access_token ) || empty( $merchant_access_token ) || empty( $page_access_token ) ) { + return new WP_Error( 'missing_token', __( 'Missing required token data', 'facebook-for-woocommerce' ), array( 'status' => 400 ) ); } // Update the options - update_option('wc_facebook_access_token', $access_token); - update_option('wc_facebook_merchant_access_token', $merchant_access_token); - update_option('wc_facebook_page_access_token', $page_access_token); - update_option('wc_facebook_product_catalog_id', $product_catalog_id); - update_option('wc_facebook_pixel_id', $pixel_id); + update_option( 'wc_facebook_access_token', $access_token ); + update_option( 'wc_facebook_merchant_access_token', $merchant_access_token ); + update_option( 'wc_facebook_page_access_token', $page_access_token ); + update_option( 'wc_facebook_product_catalog_id', $product_catalog_id ); + update_option( 'wc_facebook_pixel_id', $pixel_id ); - return new WP_REST_Response(array('success' => true, 'message' => __('Facebook settings updated successfully', 'facebook-for-woocommerce')), 200); + return new WP_REST_Response( + array( + 'success' => true, + 'message' => __( 'Facebook settings updated successfully', 'facebook-for-woocommerce' ), + ), + 200 + ); } } From 911970199573a85c40436c42f4ca3b45db3f7452 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Mon, 24 Feb 2025 15:39:43 -0800 Subject: [PATCH 12/25] Fixing lint complaints --- .../Admin/Settings_Screens/Connection.php | 18 ++- includes/Handlers/MetaExtension.php | 121 +++++++++--------- 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 2f66d5617..cdeae1c80 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -114,9 +114,9 @@ public function enqueue_assets() { public function render() { // Check if we have a merchant access token - $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); - - if (!empty($merchant_access_token) && $this->use_iframe_connection()) { + $merchant_access_token = get_option( 'wc_facebook_merchant_access_token', '' ); + + if ( ! empty( $merchant_access_token ) && $this->use_iframe_connection() ) { // Render the management iframe $connection = facebook_for_woocommerce()->get_connection_handler(); \WooCommerce\Facebook\Handlers\MetaExtension::render_management_iframe( @@ -136,8 +136,6 @@ public function render() { return; } - - /** * Build the basic static elements. * @@ -375,12 +373,12 @@ public function render_message_handler() { } // Check if we have a merchant access token - $merchant_access_token = get_option('wc_facebook_merchant_access_token', ''); - - if (!empty($merchant_access_token)) { + $merchant_access_token = get_option( 'wc_facebook_merchant_access_token', '' ); + + if ( ! empty( $merchant_access_token ) ) { return; } -?> + ?> diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index edadbb8e9..6f8b0b2f3 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -30,96 +30,261 @@ class MetaExtension { /** @var string Business name */ const BUSINESS_NAME = 'WooCommerce'; + /** @var string API version */ + const API_VERSION = 'v18.0'; + + /** @var string Commerce Hub base URL */ + const COMMERCE_HUB_URL = 'https://www.commercepartnerhub.com/'; + + /** @var string Option names for Facebook settings */ + const OPTION_ACCESS_TOKEN = 'wc_facebook_access_token'; + const OPTION_MERCHANT_ACCESS_TOKEN = 'wc_facebook_merchant_access_token'; + const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token'; + const OPTION_SYSTEM_USER_ID = 'wc_facebook_system_user_id'; + const OPTION_BUSINESS_MANAGER_ID = 'wc_facebook_business_manager_id'; + const OPTION_AD_ACCOUNT_ID = 'wc_facebook_ad_account_id'; + const OPTION_INSTAGRAM_BUSINESS_ID = 'wc_facebook_instagram_business_id'; + const OPTION_COMMERCE_MERCHANT_SETTINGS_ID = 'wc_facebook_commerce_merchant_settings_id'; + const OPTION_EXTERNAL_BUSINESS_ID = 'wc_facebook_external_business_id'; + const OPTION_COMMERCE_PARTNER_INTEGRATION_ID = 'wc_facebook_commerce_partner_integration_id'; + const OPTION_PRODUCT_CATALOG_ID = 'wc_facebook_product_catalog_id'; + const OPTION_PIXEL_ID = 'wc_facebook_pixel_id'; + const OPTION_PROFILES = 'wc_facebook_profiles'; + const OPTION_INSTALLED_FEATURES = 'wc_facebook_installed_features'; + const OPTION_HAS_CONNECTED_FBE_2 = 'wc_facebook_has_connected_fbe_2'; + const OPTION_HAS_AUTHORIZED_PAGES = 'wc_facebook_has_authorized_pages_read_engagement'; + + /** @var string Nonce action */ + const NONCE_ACTION = 'wc_facebook_ajax_token_update'; + /** * Constructor. * * @since 2.0.0 */ public function __construct() { - add_action( 'wp_ajax_wc_facebook_update_tokens', array( __CLASS__, 'ajax_update_fb_settings' ) ); add_action( 'rest_api_init', array( __CLASS__, 'init_rest_endpoint' ) ); } + // ========================== + // = Settings Management = + // ========================== + /** - * Generates the Commerce Hub iframe splash page URL. + * Validates if the required tokens are present. * - * @param bool $is_connected Whether the plugin is currently connected. - * @param object $plugin The plugin instance. - * @param string $external_business_id External business ID. - * @return string + * @since 2.0.0 + * @param array $tokens Array of tokens to validate. + * @return bool True if all required tokens are present. */ - public static function generate_iframe_splash_url( $is_connected, $plugin, $external_business_id ) { - $external_client_metadata = array( - 'shop_domain' => wc_get_page_permalink( 'shop' ) ? wc_get_page_permalink( 'shop' ) : \home_url(), - 'admin_url' => admin_url(), - 'client_version' => $plugin->get_version(), - 'commerce_partner_seller_platform_type' => 'SELF_SERVE_PLATFORM', - 'country_code' => WC()->countries->get_base_country(), - ); - return add_query_arg( - array( - 'access_client_token' => self::CLIENT_TOKEN, - 'business_vertical' => 'ECOMMERCE', - 'channel' => 'COMMERCE', - 'app_id' => Connection::CLIENT_ID, - 'business_name' => self::BUSINESS_NAME, - 'currency' => get_woocommerce_currency(), - 'timezone' => 'America/Los_Angeles', - 'external_business_id' => $external_business_id, - 'installed' => $is_connected, - 'external_client_metadata' => rawurlencode( wp_json_encode( $external_client_metadata ) ), - ), - 'https://www.commercepartnerhub.com/commerce_extension/splash/' + private static function validate_required_tokens( $tokens ) { + return ! empty( $tokens['access_token'] ) && ! empty( $tokens['merchant_access_token'] ) && ! empty( $tokens['page_access_token'] ); + } + + /** + * Updates Facebook settings options. + * + * @since 2.0.0 + * @param array $settings Array of settings to update. + * @return void + */ + private static function update_settings( $settings ) { + foreach ( $settings as $key => $value ) { + if ( ! empty( $key ) ) { + update_option( $key, $value ); + } + } + } + + /** + * Sanitizes and retrieves a value from an array. + * + * @since 2.0.0 + * @param array $data Array to retrieve value from. + * @param string $key Key to retrieve. + * @param bool $sanitize Whether to sanitize the value. + * @return mixed|string The value or empty string if not set. + */ + private static function get_param_value( $data, $key, $sanitize = true ) { + if ( ! isset( $data[ $key ] ) ) { + return ''; + } + + $value = $data[ $key ]; + + if ( $sanitize && is_string( $value ) ) { + return sanitize_text_field( $value ); + } + + return $value; + } + + /** + * Maps request parameters to option names. + * + * @since 2.0.0 + * @param array $params Request parameters. + * @return array Mapped options with values. + */ + private static function map_params_to_options( $params ) { + $options = array(); + + // Define parameter to option mapping + $mapping = array( + 'access_token' => self::OPTION_ACCESS_TOKEN, + 'merchant_access_token' => self::OPTION_MERCHANT_ACCESS_TOKEN, + 'page_access_token' => self::OPTION_PAGE_ACCESS_TOKEN, + 'system_user_id' => self::OPTION_SYSTEM_USER_ID, + 'business_manager_id' => self::OPTION_BUSINESS_MANAGER_ID, + 'ad_account_id' => self::OPTION_AD_ACCOUNT_ID, + 'instagram_business_id' => self::OPTION_INSTAGRAM_BUSINESS_ID, + 'commerce_merchant_settings_id' => self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, + 'external_business_id' => self::OPTION_EXTERNAL_BUSINESS_ID, + 'commerce_partner_integration_id' => self::OPTION_COMMERCE_PARTNER_INTEGRATION_ID, + 'product_catalog_id' => self::OPTION_PRODUCT_CATALOG_ID, + 'pixel_id' => self::OPTION_PIXEL_ID, + 'profiles' => self::OPTION_PROFILES, + 'installed_features' => self::OPTION_INSTALLED_FEATURES, + // Integration settings with special handling + 'page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, + 'catalog_id' => \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID, ); + + // Process each parameter + foreach ( $mapping as $param_key => $option_name ) { + if ( isset( $params[ $param_key ] ) ) { + // Skip if this is an alias and we've already processed the canonical field + if ( 'product_catalog_id' === $param_key && isset( $params['catalog_id'] ) ) { + continue; + } + + // Determine if we should sanitize + $sanitize = ! in_array( $param_key, array( 'profiles', 'installed_features' ), true ); + + $options[ $option_name ] = self::get_param_value( $params, $param_key, $sanitize ); + } + } + + return $options; } /** - * AJAX endpoint to update Facebook settings with authenticated tokens. + * Updates connection status flags based on tokens. * - * Expects POST parameters: - * - nonce: security nonce. - * - access_token: system user access token. - * - merchant_access_token: merchant access token. - * - page_access_token: page access token. - * - product_catalog_id: product catalog ID (optional). - * - pixel_id: pixel ID (optional). + * @since 2.0.0 + * @param array $params Parameters containing tokens. + * @return void + */ + private static function update_connection_status( $params ) { + if ( ! empty( $params['access_token'] ) ) { + update_option( self::OPTION_HAS_CONNECTED_FBE_2, 'yes' ); + } + + if ( ! empty( $params['page_access_token'] ) ) { + update_option( self::OPTION_HAS_AUTHORIZED_PAGES, 'yes' ); + } + } + + /** + * Clears Facebook integration options. + * + * @since 2.0.0 + * @return void + */ + private static function clear_integration_options() { + $options = array( + // Connection handler options + self::OPTION_ACCESS_TOKEN, + self::OPTION_MERCHANT_ACCESS_TOKEN, + self::OPTION_PAGE_ACCESS_TOKEN, + self::OPTION_SYSTEM_USER_ID, + self::OPTION_BUSINESS_MANAGER_ID, + self::OPTION_AD_ACCOUNT_ID, + self::OPTION_INSTAGRAM_BUSINESS_ID, + self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, + self::OPTION_EXTERNAL_BUSINESS_ID, + self::OPTION_COMMERCE_PARTNER_INTEGRATION_ID, + + // Additional data stored during connection + self::OPTION_PROFILES, + self::OPTION_INSTALLED_FEATURES, + + // Connection status flags + self::OPTION_HAS_CONNECTED_FBE_2, + self::OPTION_HAS_AUTHORIZED_PAGES, + ); + + // Clear all options + foreach ( $options as $option_name ) { + if ( in_array( $option_name, array( self::OPTION_PROFILES, self::OPTION_INSTALLED_FEATURES ), true ) ) { + update_option( $option_name, null ); + } elseif ( in_array( $option_name, array( self::OPTION_HAS_CONNECTED_FBE_2, self::OPTION_HAS_AUTHORIZED_PAGES ), true ) ) { + update_option( $option_name, 'no' ); + } else { + update_option( $option_name, '' ); + } + } + + // Integration settings - use constants for consistency + update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, '' ); + update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, '' ); + } + + // ========================== + // = API Communication = + // ========================== + + /** + * Makes an API call to Facebook's Graph API. * - * @return void JSON response. + * @param string $method HTTP method (GET, POST, etc.) + * @param string $endpoint API endpoint + * @param array $params Request parameters + * @return array Response data + * @throws \Exception If the request fails. */ - public static function ajax_update_fb_settings() { - // Ensure the current user can manage WooCommerce settings. - if ( ! current_user_can( 'manage_woocommerce' ) ) { - wp_send_json_error( array( 'message' => __( 'Unauthorized request', 'facebook-for-woocommerce' ) ) ); + private static function call_api( $method, $endpoint, $params ) { + $url = 'https://graph.facebook.com/' . self::API_VERSION . '/' . $endpoint; + + if ( 'GET' === $method ) { + $url = add_query_arg( $params, $url ); } - // Validate the nonce. - $nonce = isset( $_POST['nonce'] ) ? sanitize_key( wp_unslash( $_POST['nonce'] ) ) : ''; - if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wc_facebook_ajax_token_update' ) ) { - wp_send_json_error( array( 'message' => __( 'Invalid nonce', 'facebook-for-woocommerce' ) ) ); + $args = array( + 'method' => $method, + 'timeout' => 30, + 'headers' => array( + 'Content-Type' => 'application/json', + ), + ); + + if ( 'POST' === $method ) { + $args['body'] = wp_json_encode( $params ); } - // Sanitize and retrieve POST data. - $access_token = isset( $_POST['access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['access_token'] ) ) : ''; - $merchant_access_token = isset( $_POST['merchant_access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['merchant_access_token'] ) ) : ''; - $page_access_token = isset( $_POST['page_access_token'] ) ? sanitize_text_field( wp_unslash( $_POST['page_access_token'] ) ) : ''; - $product_catalog_id = isset( $_POST['product_catalog_id'] ) ? sanitize_text_field( wp_unslash( $_POST['product_catalog_id'] ) ) : ''; - $pixel_id = isset( $_POST['pixel_id'] ) ? sanitize_text_field( wp_unslash( $_POST['pixel_id'] ) ) : ''; + $response = wp_remote_request( $url, $args ); - // Validate required tokens. - if ( empty( $access_token ) || empty( $merchant_access_token ) || empty( $page_access_token ) ) { - wp_send_json_error( array( 'message' => __( 'Missing required token data', 'facebook-for-woocommerce' ) ) ); + if ( is_wp_error( $response ) ) { + throw new \Exception( $response->get_error_message() ); } - // Update Facebook settings via options. - update_option( 'wc_facebook_access_token', $access_token ); - update_option( 'wc_facebook_merchant_access_token', $merchant_access_token ); - update_option( 'wc_facebook_page_access_token', $page_access_token ); - update_option( 'wc_facebook_product_catalog_id', $product_catalog_id ); - update_option( 'wc_facebook_pixel_id', $pixel_id ); + $status_code = wp_remote_retrieve_response_code( $response ); + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + + // Check for API errors + if ( $status_code >= 400 ) { + $error_message = isset( $data['error']['message'] ) ? $data['error']['message'] : __( 'Unknown API error', 'facebook-for-woocommerce' ); + throw new \Exception( sprintf( 'Facebook API error (%d): %s', $status_code, $error_message ) ); + } - wp_send_json_success( array( 'message' => __( 'Facebook settings updated successfully', 'facebook-for-woocommerce' ) ) ); + return $data; } + // ========================== + // = REST API Endpoints = + // ========================== + /** * REST API endpoint initialization. * @@ -128,11 +293,21 @@ public static function ajax_update_fb_settings() { public static function init_rest_endpoint() { register_rest_route( 'wc-facebook/v1', - 'update_tokens', + 'update_fb_settings', + array( + 'methods' => 'POST', + 'callback' => array( __CLASS__, 'rest_update_fb_settings' ), + 'permission_callback' => array( __CLASS__, 'rest_update_fb_settings_permission_callback' ), + ) + ); + + register_rest_route( + 'wc-facebook/v1', + 'uninstall', array( 'methods' => 'POST', - 'callback' => array( __CLASS__, 'rest_update_fb_tokens' ), - 'permission_callback' => array( __CLASS__, 'rest_update_fb_tokens_permission_callback' ), + 'callback' => array( __CLASS__, 'rest_handle_uninstall' ), + 'permission_callback' => array( __CLASS__, 'rest_update_fb_settings_permission_callback' ), ) ); } @@ -142,7 +317,7 @@ public static function init_rest_endpoint() { * * @return bool */ - public static function rest_update_fb_tokens_permission_callback() { + public static function rest_update_fb_settings_permission_callback() { return current_user_can( 'manage_woocommerce' ); } @@ -157,13 +332,13 @@ public static function rest_update_fb_tokens_permission_callback() { * - pixel_id: pixel ID. * - page_id: page ID. * - commerce_partner_integration_id: commerce partner integration ID. - * - profiles: profiles dat). + * - profiles: profiles data. * - installed_features: installed features data. * * @param WP_REST_Request $request The request object. * @return WP_REST_Response|WP_Error */ - public static function rest_update_fb_tokens( WP_REST_Request $request ) { + public static function rest_update_fb_settings( WP_REST_Request $request ) { // Get JSON data from request body $params = $request->get_json_params(); @@ -176,34 +351,12 @@ public static function rest_update_fb_tokens( WP_REST_Request $request ) { ); } - // Define option mappings with sanitization - $options = array( - 'access_token' => 'wc_facebook_access_token', - 'merchant_access_token' => 'wc_facebook_merchant_access_token', - 'page_access_token' => 'wc_facebook_page_access_token', - 'product_catalog_id' => 'wc_facebook_product_catalog_id', - 'pixel_id' => 'wc_facebook_pixel_id', - 'page_id' => 'wc_facebook_page_id', - 'commerce_partner_integration_id' => 'wc_facebook_commerce_partner_integration_id', - 'profiles' => 'wc_facebook_profiles', - 'installed_features' => 'wc_facebook_installed_features', - ); + // Map parameters to options and update settings + $options = self::map_params_to_options( $params ); + self::update_settings( $options ); - // Process each option - foreach ( $options as $param_key => $option_name ) { - if ( isset( $params[ $param_key ] ) ) { - $value = $params[ $param_key ]; - - // Apply sanitization to string values only - if ( in_array( $param_key, array( 'profiles', 'installed_features' ), true ) ) { - // These are arrays/objects, don't sanitize - update_option( $option_name, $value ); - } else { - // Text fields get sanitized - update_option( $option_name, sanitize_text_field( $value ) ); - } - } - } + // Update connection status flags + self::update_connection_status( $params ); return new WP_REST_Response( array( @@ -215,41 +368,83 @@ public static function rest_update_fb_tokens( WP_REST_Request $request ) { } /** - * Makes an API call to Facebook's Graph API. + * REST API endpoint callback to handle uninstall requests. * - * @param string $method HTTP method (GET, POST, etc.) - * @param string $endpoint API endpoint - * @param array $params Request parameters - * @return array Response data - * @throws \Exception If the request fails. + * @return WP_REST_Response|WP_Error */ - private static function call_api( $method, $endpoint, $params ) { - $url = 'https://graph.facebook.com/v18.0/' . $endpoint; + public static function rest_handle_uninstall() { + try { + // Try to disconnect from Facebook API first + $external_business_id = get_option( self::OPTION_EXTERNAL_BUSINESS_ID, '' ); + if ( ! empty( $external_business_id ) ) { + try { + facebook_for_woocommerce()->get_api()->delete_mbe_connection( (string) $external_business_id ); + } catch ( \Exception $e ) { + facebook_for_woocommerce()->log( sprintf( 'Error during API uninstall: %s', $e->getMessage() ) ); + // Continue with local disconnection even if API call fails + } + } - if ( 'GET' === $method ) { - $url = add_query_arg( $params, $url ); - } + // Clear all integration options + self::clear_integration_options(); - $args = array( - 'method' => $method, - 'timeout' => 30, - 'headers' => array( - 'Content-Type' => 'application/json', - ), - ); + // Get the integration instance and update the product catalog ID + $integration = facebook_for_woocommerce()->get_integration(); + if ( $integration ) { + $integration->update_product_catalog_id( '' ); + } - if ( 'POST' === $method ) { - $args['body'] = wp_json_encode( $params ); + return new WP_REST_Response( + array( + 'success' => true, + 'message' => __( 'Facebook integration successfully uninstalled', 'facebook-for-woocommerce' ), + ), + 200 + ); + } catch ( \Exception $e ) { + return new WP_Error( + 'uninstall_error', + $e->getMessage(), + array( 'status' => 500 ) + ); } + } - $response = wp_remote_request( $url, $args ); - - if ( is_wp_error( $response ) ) { - throw new \Exception( $response->get_error_message() ); - } + // ========================== + // = IFrame Management = + // ========================== - $body = wp_remote_retrieve_body( $response ); - return json_decode( $body, true ); + /** + * Generates the Commerce Hub iframe splash page URL. + * + * @param bool $is_connected Whether the plugin is currently connected. + * @param object $plugin The plugin instance. + * @param string $external_business_id External business ID. + * @return string + */ + public static function generate_iframe_splash_url( $is_connected, $plugin, $external_business_id ) { + $external_client_metadata = array( + 'shop_domain' => wc_get_page_permalink( 'shop' ) ? wc_get_page_permalink( 'shop' ) : \home_url(), + 'admin_url' => admin_url(), + 'client_version' => $plugin->get_version(), + 'commerce_partner_seller_platform_type' => 'SELF_SERVE_PLATFORM', + 'country_code' => WC()->countries->get_base_country(), + ); + return add_query_arg( + array( + 'access_client_token' => self::CLIENT_TOKEN, + 'business_vertical' => 'ECOMMERCE', + 'channel' => 'COMMERCE', + 'app_id' => Connection::CLIENT_ID, + 'business_name' => self::BUSINESS_NAME, + 'currency' => get_woocommerce_currency(), + 'timezone' => 'America/Los_Angeles', + 'external_business_id' => $external_business_id, + 'installed' => $is_connected, + 'external_client_metadata' => rawurlencode( wp_json_encode( $external_client_metadata ) ), + ), + self::COMMERCE_HUB_URL . 'commerce_extension/splash/' + ); } /** @@ -261,7 +456,7 @@ private static function call_api( $method, $endpoint, $params ) { */ public static function get_commerce_extension_iframe_url( $external_business_id, $access_token = null ) { if ( empty( $access_token ) ) { - $access_token = get_option( 'wc_facebook_access_token', '' ); + $access_token = get_option( self::OPTION_ACCESS_TOKEN, '' ); } try { @@ -284,7 +479,7 @@ public static function get_commerce_extension_iframe_url( $external_business_id, $base_url_override = apply_filters( 'wc_facebook_commerce_extension_base_url', $base_url_override ); if ( $base_url_override ) { - $uri = str_replace( 'https://www.commercepartnerhub.com/', $base_url_override, $uri ); + $uri = str_replace( self::COMMERCE_HUB_URL, $base_url_override, $uri ); } return $uri; @@ -304,7 +499,7 @@ public static function get_commerce_extension_iframe_url( $external_business_id, * @return string */ public static function generate_iframe_management_url( $plugin, $external_business_id ) { - $access_token = get_option( 'wc_facebook_access_token', '' ); + $access_token = get_option( self::OPTION_ACCESS_TOKEN, '' ); if ( empty( $access_token ) ) { return ''; diff --git a/tests/Unit/Admin/Settings_Screens/ConnectionTest.php b/tests/Unit/Admin/Settings_Screens/ConnectionTest.php index 540beee1b..b4ed467fb 100644 --- a/tests/Unit/Admin/Settings_Screens/ConnectionTest.php +++ b/tests/Unit/Admin/Settings_Screens/ConnectionTest.php @@ -24,7 +24,7 @@ protected function setUp(): void { */ public function test_use_iframe_connection_enabled_by_default() { $reflection = new \ReflectionClass($this->connection); - $method = $reflection->getMethod('use_iframe_connection'); + $method = $reflection->getMethod('use_enhanced_onboarding'); $method->setAccessible(true); $this->assertTrue($method->invoke($this->connection)); @@ -36,10 +36,10 @@ public function test_use_iframe_connection_enabled_by_default() { public function test_render_facebook_box_iframe() { // Create a mock for the Connection class that returns a predictable iframe URL $connection_mock = $this->getMockBuilder(Connection::class) - ->onlyMethods(['use_iframe_connection']) + ->onlyMethods(['use_enhanced_onboarding']) ->getMock(); - $connection_mock->method('use_iframe_connection') + $connection_mock->method('use_enhanced_onboarding') ->willReturn(true); // Create a reflection to access the private method @@ -104,7 +104,7 @@ public function test_render_message_handler() { $this->assertStringContainsString('CommerceExtension::UNINSTALL', $output); // Assert fetch request setup - $this->assertStringContainsString('fetch(\'/wp-json/wc-facebook/v1/update_tokens\'', $output); + $this->assertStringContainsString('fetch(\'/wp-json/wc-facebook/v1/update_fb_settings\'', $output); $this->assertStringContainsString('method: \'POST\'', $output); $this->assertStringContainsString('credentials: \'same-origin\'', $output); } diff --git a/tests/Unit/Handlers/MetaExtensionTest.php b/tests/Unit/Handlers/MetaExtensionTest.php index 66d517f40..37f9cce0e 100644 --- a/tests/Unit/Handlers/MetaExtensionTest.php +++ b/tests/Unit/Handlers/MetaExtensionTest.php @@ -52,7 +52,7 @@ public function test_generate_iframe_splash_url() { /** * Test REST API token update with valid data */ - public function test_rest_update_fb_tokens_valid_data() { + public function test_rest_update_fb_settings_valid_data() { // Create a mock for WP_REST_Request $request = $this->getMockBuilder(WP_REST_Request::class) ->disableOriginalConstructor() @@ -70,7 +70,7 @@ public function test_rest_update_fb_tokens_valid_data() { 'pixel_id' => '789012' ]); - $response = MetaExtension::rest_update_fb_tokens($request); + $response = MetaExtension::rest_update_fb_settings($request); $this->assertInstanceOf(WP_REST_Response::class, $response); $this->assertEquals(200, $response->get_status()); @@ -87,7 +87,7 @@ public function test_rest_update_fb_tokens_valid_data() { /** * Test REST API token update with missing required merchant token */ - public function test_rest_update_fb_tokens_missing_merchant_token() { + public function test_rest_update_fb_settings_missing_merchant_token() { // Create a mock for WP_REST_Request $request = $this->getMockBuilder(WP_REST_Request::class) ->disableOriginalConstructor() @@ -102,7 +102,7 @@ public function test_rest_update_fb_tokens_missing_merchant_token() { 'page_access_token' => 'test_page_token' ]); - $response = MetaExtension::rest_update_fb_tokens($request); + $response = MetaExtension::rest_update_fb_settings($request); $this->assertInstanceOf(WP_Error::class, $response); $this->assertEquals('missing_token', $response->get_error_code()); From e3827c312faaccdde4ffe6265881d0a5db79d981 Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Wed, 26 Feb 2025 16:26:20 -0800 Subject: [PATCH 19/25] Updating tests; changing implmentation of iframe generation to consistently vend a URL --- .../Admin/Settings_Screens/Connection.php | 65 ++--- includes/Handlers/MetaExtension.php | 70 +---- .../Admin/Settings_Screens/ConnectionTest.php | 253 +++++------------- tests/Unit/Handlers/MetaExtensionTest.php | 29 +- 4 files changed, 133 insertions(+), 284 deletions(-) diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 3be84b8aa..94a3448cc 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -118,16 +118,9 @@ public function enqueue_assets() { * @since 2.0.0 */ public function render() { - // Check if we have a merchant access token - $merchant_access_token = get_option( 'wc_facebook_merchant_access_token', '' ); - - if ( ! empty( $merchant_access_token ) && $this->use_enhanced_onboarding() ) { - // Render the management iframe - $connection = facebook_for_woocommerce()->get_connection_handler(); - \WooCommerce\Facebook\Handlers\MetaExtension::render_management_iframe( - $connection->get_plugin(), - $connection->get_external_business_id() - ); + // Check if we should render iframe + if ( $this->use_enhanced_onboarding() ) { + $this->render_facebook_iframe(); return; } @@ -276,44 +269,42 @@ class="dashicons dashicons-external" parent::render(); } - /** - * Renders the Facebook CTA box. + * Renders the appropriate Facebook iframe based on connection status. * * @since 2.0.0 - * - * @param bool $is_connected whether the plugin is connected */ - private function render_facebook_box( $is_connected ) { - if ( $this->use_enhanced_onboarding() ) { - $this->render_facebook_box_iframe( $is_connected ); + private function render_facebook_iframe() { + $connection = facebook_for_woocommerce()->get_connection_handler(); + $is_connected = $connection->is_connected(); + $merchant_access_token = get_option( 'wc_facebook_merchant_access_token', '' ); + + if ( ! empty( $merchant_access_token ) && $is_connected ) { + // Get management iframe URL for connected merchants + $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generate_iframe_management_url( + $connection->get_external_business_id() + ); } else { - $this->render_facebook_box_legacy( $is_connected ); + // Get onboarding iframe URL for new connections + $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generate_iframe_splash_url( + $is_connected, + $connection->get_plugin(), + $connection->get_external_business_id() + ); + } + + if ( empty( $iframe_url ) ) { + return; } - } - /** - * Renders the Facebook CTA box using iframe implementation. - * - * @since 2.0.0 - * - * @param bool $is_connected whether the plugin is connected - */ - private function render_facebook_box_iframe( $is_connected ) { - $connection = facebook_for_woocommerce()->get_connection_handler(); - $iframe_url = \WooCommerce\Facebook\Handlers\MetaExtension::generate_iframe_splash_url( - $is_connected, - $connection->get_plugin(), - $connection->get_external_business_id() - ); ?> + id=""> log( 'Facebook Commerce Extension URL Error: ' . $e->getMessage() ); } - return ''; } - - /** - * Generates the Commerce Hub iframe management page URL. - * - * @param object $plugin The plugin instance. - * @param string $external_business_id External business ID. - * @return string - */ - public static function generate_iframe_management_url( $plugin, $external_business_id ) { - $access_token = get_option( self::OPTION_ACCESS_TOKEN, '' ); - - if ( empty( $access_token ) ) { - return ''; - } - - return self::get_commerce_extension_iframe_url( $external_business_id, $access_token ); - } - - /** - * Renders the management iframe. - * - * @param object $plugin The plugin instance. - * @param string $external_business_id External business ID. - * @return void - */ - public static function render_management_iframe( $plugin, $external_business_id ) { - $iframe_url = self::generate_iframe_management_url( $plugin, $external_business_id ); - - if ( empty( $iframe_url ) ) { - return; - } - - ?> - - connection); - $method = $reflection->getMethod('use_enhanced_onboarding'); + private function invoke_method($object, $methodName, array $parameters = []) { + $reflection = new \ReflectionClass(get_class($object)); + $method = $reflection->getMethod($methodName); $method->setAccessible(true); - - $this->assertTrue($method->invoke($this->connection)); + + return $method->invokeArgs($object, $parameters); } /** - * Test that render_facebook_box_iframe generates correct iframe with URL + * Test that iframe connection can be enabled or disabled */ - public function test_render_facebook_box_iframe() { - // Create a mock for the Connection class that returns a predictable iframe URL + public function test_use_iframe_connection() { + // Test the actual implementation (currently returns false) + $reflection = new \ReflectionClass($this->connection); + $method = $reflection->getMethod('use_enhanced_onboarding'); + $method->setAccessible(true); + + // REMOVE WHEN READY TO RELEASE ENHANCED ONBOARDING FLOW + // Test to ensure that the use_enhanced_onboarding method returns false + $actual_value = $method->invoke($this->connection); + $this->assertFalse($actual_value); + + // Create a mock that returns true for testing the true case $connection_mock = $this->getMockBuilder(Connection::class) ->onlyMethods(['use_enhanced_onboarding']) ->getMock(); $connection_mock->method('use_enhanced_onboarding') ->willReturn(true); - - // Create a reflection to access the private method + + // Use reflection to call the protected method on the mock $reflection = new \ReflectionClass($connection_mock); - $method = $reflection->getMethod('render_facebook_box_iframe'); + $method = $reflection->getMethod('use_enhanced_onboarding'); $method->setAccessible(true); + $mock_value = $method->invoke($connection_mock); - // Mock the facebook_for_woocommerce function - $this->mockWordPressFunctions(); + $this->assertTrue($mock_value); + } + + /** + * Test that render method calls render_facebook_iframe when enhanced onboarding is enabled + */ + public function test_render_facebook_box_iframe() { + // Create a partial mock of the Connection class + $connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['use_enhanced_onboarding']) + ->getMock(); - // Start output buffering + // Configure the mock to return true for use_enhanced_onboarding + $connection->expects($this->once()) + ->method('use_enhanced_onboarding') + ->willReturn(true); + + // Start output buffering to capture the render output ob_start(); - - // Call the method - $method->invoke($connection_mock, true); - - // Get the output + $connection->render(); $output = ob_get_clean(); - // Assert the iframe is present with expected attributes - $this->assertStringContainsString('assertStringContainsString('id="facebook-commerce-iframe"', $output); - $this->assertStringContainsString('width="100%"', $output); - $this->assertStringContainsString('height="600"', $output); - $this->assertStringContainsString('frameborder="0"', $output); - $this->assertStringContainsString('style="background: transparent;"', $output); - - // Since we can't test the exact URL (it's generated dynamically), - // we'll just check that src attribute exists - $this->assertStringContainsString('src=', $output); + // Since we can't directly test the private render_facebook_iframe method, + // we'll verify that the render method doesn't output the legacy Facebook box + // when enhanced onboarding is enabled + $this->assertStringNotContainsString('wc-facebook-connection-box', $output); } /** @@ -78,21 +97,19 @@ public function test_render_facebook_box_iframe() { public function test_render_message_handler() { // Mock is_current_screen_page to return true $connection_mock = $this->getMockBuilder(Connection::class) - ->onlyMethods(['is_current_screen_page']) + ->onlyMethods(['is_current_screen_page', 'use_enhanced_onboarding']) ->getMock(); $connection_mock->method('is_current_screen_page') ->willReturn(true); - - // Mock get_option to return empty string for merchant token - $this->mockGetOption('wc_facebook_merchant_access_token', ''); - - // Mock wp_create_nonce - $this->mockWpCreateNonce('wp_rest', 'test_nonce'); + + $connection_mock->method('use_enhanced_onboarding') + ->willReturn(true); // Start output buffering ob_start(); + // Call the method $connection_mock->render_message_handler(); $output = ob_get_clean(); @@ -128,150 +145,28 @@ public function test_render_message_handler_not_current_screen() { } /** - * Test that render_message_handler doesn't output when merchant token exists + * Test that the management URL is used when merchant token exists */ - public function test_render_message_handler_with_merchant_token() { - // Create a custom Connection class that overrides the necessary methods - $connection_class = new class extends Connection { - // Override render_message_handler to simulate the behavior we want to test - public function render_message_handler() { - // Only proceed if we're on the current screen - if (!$this->is_current_screen_page()) { - return; - } - - // This is the key part we're testing - if token exists, return early - if (!empty('test_token')) { - return; - } - - // If we get here, we would output JavaScript, but we're testing the early return - echo 'This should not be output'; - } - - // Override is_current_screen_page to always return true for testing - public function is_current_screen_page() { - return true; - } - }; - - ob_start(); - $connection_class->render_message_handler(); - $output = ob_get_clean(); - - $this->assertEmpty($output); - } - - /** - * Helper method to mock WordPress functions - */ - private function mockWordPressFunctions() { - // Mock facebook_for_woocommerce - if (!function_exists('facebook_for_woocommerce')) { - function facebook_for_woocommerce() { - $mock = new \stdClass(); - - // Mock get_connection_handler - $mock->get_connection_handler = function() { - $handler = new \stdClass(); - - // Mock get_plugin - $handler->get_plugin = function() { - $plugin = new \stdClass(); - $plugin->get_version = function() { - return '1.0.0'; - }; - return $plugin; - }; - - // Mock get_external_business_id - $handler->get_external_business_id = function() { - return 'test_business_id'; - }; - - return $handler; - }; - - return $mock; - } - } - - // Mock WC - if (!function_exists('WC')) { - function WC() { - $mock = new \stdClass(); - $mock->countries = new \stdClass(); - $mock->countries->get_base_country = function() { - return 'US'; - }; - return $mock; - } - } - - // Mock get_woocommerce_currency - if (!function_exists('get_woocommerce_currency')) { - function get_woocommerce_currency() { - return 'USD'; - } - } - - // Mock wc_get_page_permalink - if (!function_exists('wc_get_page_permalink')) { - function wc_get_page_permalink() { - return 'https://example.com/shop'; - } - } + public function test_renders_management_url_based_on_merchant_token() { + // Create a mock for testing the private render_facebook_iframe method + $connection = $this->getMockBuilder(Connection::class) + ->onlyMethods(['use_enhanced_onboarding']) + ->getMock(); - // Mock home_url - if (!function_exists('home_url')) { - function home_url() { - return 'https://example.com'; - } - } + $connection->expects($this->any()) + ->method('use_enhanced_onboarding') + ->willReturn(true); - // Mock admin_url - if (!function_exists('admin_url')) { - function admin_url() { - return 'https://example.com/wp-admin/'; - } - } - } - - /** - * Helper method to mock get_option - */ - private function mockGetOption($option_name, $return_value) { - if (!function_exists('get_option')) { - function get_option($option, $default = '') { - global $mock_option_name, $mock_option_value; - if ($option === $mock_option_name) { - return $mock_option_value; - } - return $default; - } - } + // Set up the merchant token + update_option('wc_facebook_merchant_access_token', 'test_token'); - global $mock_option_name, $mock_option_value; - $mock_option_name = $option_name; - $mock_option_value = $return_value; - } - - /** - * Helper method to mock wp_create_nonce - */ - private function mockWpCreateNonce($action, $return_value) { - if (!function_exists('wp_create_nonce')) { - function wp_create_nonce($nonce_action) { - global $mock_nonce_action, $mock_nonce_value; - if ($nonce_action === $mock_nonce_action) { - return $mock_nonce_value; - } - return 'default_nonce'; - } - } + // Use output buffering to capture the iframe HTML + ob_start(); + $this->invoke_method($connection, 'render_facebook_iframe'); + $output = ob_get_clean(); - global $mock_nonce_action, $mock_nonce_value; - $mock_nonce_action = $action; - $mock_nonce_value = $return_value; + // Check that the iframe is rendered + $this->assertStringContainsString('assertStringContainsString('frameborder="0"', $output); } } \ No newline at end of file diff --git a/tests/Unit/Handlers/MetaExtensionTest.php b/tests/Unit/Handlers/MetaExtensionTest.php index 37f9cce0e..bae011bef 100644 --- a/tests/Unit/Handlers/MetaExtensionTest.php +++ b/tests/Unit/Handlers/MetaExtensionTest.php @@ -109,19 +109,19 @@ public function test_rest_update_fb_settings_missing_merchant_token() { } /** - * Test get_commerce_extension_iframe_url with valid access token + * Test generate_iframe_management_url with valid access token */ public function test_get_commerce_extension_iframe_url() { // Set up the access token update_option('wc_facebook_access_token', 'test_access_token'); // Test with empty business ID (should return empty string) - $url = MetaExtension::get_commerce_extension_iframe_url(''); + $url = MetaExtension::generate_iframe_management_url(''); $this->assertEmpty($url); // Test with valid business ID but we can't mock the API call // so we're just testing the method exists - $this->assertTrue(method_exists(MetaExtension::class, 'get_commerce_extension_iframe_url')); + $this->assertTrue(method_exists(MetaExtension::class, 'generate_iframe_management_url')); } /** @@ -132,10 +132,10 @@ public function test_generate_iframe_management_url() { // Test with no access token update_option('wc_facebook_access_token', ''); - $url = MetaExtension::generate_iframe_management_url($plugin, 'test_business_id'); + $url = MetaExtension::generate_iframe_management_url('test_business_id'); $this->assertEmpty($url); - // Since we can't easily mock the get_commerce_extension_iframe_url method, + // Since we can't easily mock the call_api method, // we'll just verify the method exists and is called $this->assertTrue(method_exists(MetaExtension::class, 'generate_iframe_management_url')); } @@ -156,4 +156,23 @@ public function test_init_rest_endpoint() { // If we got here without errors, the test passes $this->assertTrue(true); } + + /** + * Test that generate_iframe_management_url works correctly + * This is a simplified test since we can't mock private static methods + */ + public function test_get_commerce_extension_iframe_management_url() { + $external_business_id = 'test_business_id'; + + // Set up the access token + update_option(MetaExtension::OPTION_ACCESS_TOKEN, 'test_token'); + + // Since we can't mock the private static call_api method, + // we'll just test that the method exists and returns empty string + // when no API response is available + $result = MetaExtension::generate_iframe_management_url($external_business_id); + + // Without a real API response, the method should return empty string + $this->assertEmpty($result); + } } From 792d3040caa8aff08a2e679ed33ddcdd82408b6e Mon Sep 17 00:00:00 2001 From: Carter Buce Date: Wed, 5 Mar 2025 14:23:03 -0800 Subject: [PATCH 20/25] fix multisite behavior --- facebook-commerce.php | 429 +++++++++++------- .../Admin/Settings_Screens/Connection.php | 138 +++--- includes/Handlers/MetaExtension.php | 95 ++-- 3 files changed, 400 insertions(+), 262 deletions(-) diff --git a/facebook-commerce.php b/facebook-commerce.php index ff64ffc94..191e60f68 100644 --- a/facebook-commerce.php +++ b/facebook-commerce.php @@ -1,5 +1,6 @@ Facebook for WooCommerce
'; public const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress'; - public const FB_SYNC_REMAINING = 'fb_sync_remaining'; - public const FB_SYNC_TIMEOUT = 30; - public const FB_PRIORITY_MID = 9; + public const FB_SYNC_REMAINING = 'fb_sync_remaining'; + public const FB_SYNC_TIMEOUT = 30; + public const FB_PRIORITY_MID = 9; /** * Facebook exception test mode switch. @@ -183,6 +184,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration { * Init and hook in the integration. * * @param WC_Facebookcommerce $facebook_for_woocommerce + * * @return void */ public function __construct( WC_Facebookcommerce $facebook_for_woocommerce ) { @@ -235,13 +237,13 @@ public function __construct( WC_Facebookcommerce $facebook_for_woocommerce ) { // Display an info banner for eligible pixel and user. if ( $this->get_external_merchant_settings_id() - && $this->get_facebook_pixel_id() - && $this->get_pixel_install_time() ) { + && $this->get_facebook_pixel_id() + && $this->get_pixel_install_time() ) { $should_query_tip = - WC_Facebookcommerce_Utils::check_time_cap( - get_option( 'fb_info_banner_last_query_time', '' ), - self::FB_TIP_QUERY - ); + WC_Facebookcommerce_Utils::check_time_cap( + get_option( 'fb_info_banner_last_query_time', '' ), + self::FB_TIP_QUERY + ); $last_tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip(); if ( $should_query_tip || $last_tip_info ) { @@ -372,6 +374,7 @@ public function __construct( WC_Facebookcommerce $facebook_for_woocommerce ) { * __get method for backward compatibility. * * @param string $key property name + * * @return mixed * @since 3.0.32 */ @@ -380,6 +383,7 @@ public function __get( $key ) { if ( in_array( $key, array( 'events_tracker', 'background_processor' ), true ) ) { /* translators: %s property name. */ _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The %s property is private and should not be accessed outside its class.', 'facebook-for-woocommerce' ), esc_html( $key ) ), '3.0.32' ); + return $this->$key; } @@ -406,7 +410,7 @@ public function init_pixel() { if ( WC_Facebookcommerce_Utils::is_valid_id( $settings_pixel_id ) && ( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) || - $pixel_id != $settings_pixel_id + $pixel_id != $settings_pixel_id ) ) { WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id ); @@ -426,15 +430,16 @@ public function init_pixel() { return true; } + return false; } /** * Returns the Automatic advanced matching of this pixel * + * @return AAMSettings * @since 2.0.3 * - * @return AAMSettings */ private function load_aam_settings_of_pixel() { $installed_pixel = $this->get_facebook_pixel_id(); @@ -467,6 +472,7 @@ private function load_aam_settings_of_pixel() { set_transient( $config_key, strval( $aam_settings ), $refresh_interval ); } } + return $aam_settings; } @@ -532,11 +538,12 @@ public function ajax_fb_background_check_queue() { /** * Gets a list of Product Item IDs indexed by the ID of the variation. * - * @since 2.0.0 - * * @param WC_Facebook_Product|WC_Product $product product - * @param string $product_group_id product group ID + * @param string $product_group_id product group ID + * * @return array + * @since 2.0.0 + * */ public function get_variation_product_item_ids( $product, $product_group_id ) { $product_item_ids_by_variation_id = []; @@ -548,8 +555,8 @@ public function get_variation_product_item_ids( $product, $product_group_id ) { if ( $product_item_id = $variation->get_meta( self::FB_PRODUCT_ITEM_ID ) ) { $product_item_ids_by_variation_id[ $variation_id ] = $product_item_id; } else { - $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation ); - $missing_product_item_ids[ $retailer_id ] = $variation; + $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation ); + $missing_product_item_ids[ $retailer_id ] = $variation; $product_item_ids_by_variation_id[ $variation_id ] = null; } } @@ -580,6 +587,7 @@ public function get_variation_product_item_ids( $product, $product_group_id ) { * ) * * @param string $product_group_id product group ID + * * @return array a map of ( `retailer id` -> `id` ) pairs. */ private function find_variation_product_item_ids( string $product_group_id ): array { @@ -594,6 +602,7 @@ private function find_variation_product_item_ids( string $product_group_id ): ar $message = sprintf( 'There was an error trying to find the IDs for Product Items in the Product Group %s: %s', $product_group_id, $e->getMessage() ); WC_Facebookcommerce_Utils::log( $message ); } + return $product_item_ids; } @@ -604,6 +613,7 @@ private function find_variation_product_item_ids( string $product_group_id ): ar */ public function get_product_count() { $product_counts = wp_count_posts( 'product' ); + return $product_counts->publish; } @@ -706,7 +716,7 @@ public function load_assets() { diaSettingId: 'get_external_merchant_settings_id() ? esc_js( $this->get_external_merchant_settings_id() ) : ''; ?>', store: { baseUrl: window.location.protocol + '//' + window.location.host, - baseCurrency:'', + baseCurrency: '', timezoneId: '', storeName: '', version: 'version ); ?>', @@ -750,28 +760,30 @@ public function load_assets() { /** * Gets the IDs of products marked for deletion from Facebook when removed from Sync. * - * @internal - * + * @return array * @since 2.3.0 * - * @return array + * @internal + * */ private function get_removed_from_sync_products_to_delete() { $posted_products = Helper::get_posted_value( WC_Facebook_Product::FB_REMOVE_FROM_SYNC ); if ( empty( $posted_products ) ) { return []; } + return array_map( 'absint', explode( ',', $posted_products ) ); } /** * Checks the product type and calls the corresponding on publish method. * - * @internal + * @param int $wp_id post ID * * @since 1.10.0 * - * @param int $wp_id post ID + * @internal + * */ public function on_product_save( int $wp_id ) { $product = wc_get_product( $wp_id ); @@ -843,11 +855,11 @@ public function on_product_save( int $wp_id ) { } /** - * Saves the submitted Facebook settings for a variable product. - * - * - * @param \WC_Product $product The variable product object. - */ + * Saves the submitted Facebook settings for a variable product. + * + * + * @param \WC_Product $product The variable product object. + */ private function save_variable_product_settings( WC_Product $product ) { $woo_product = new WC_Facebook_Product( $product->get_id() ); if ( isset( $_POST[ WC_Facebook_Product::FB_VARIABLE_BRAND ] ) ) { @@ -858,9 +870,10 @@ private function save_variable_product_settings( WC_Product $product ) { /** * Saves the submitted Facebook settings for a product. * + * @param \WC_Product $product the product object + * * @since 1.10.0 * - * @param \WC_Product $product the product object */ private function save_product_settings( WC_Product $product ) { $woo_product = new WC_Facebook_Product( $product->get_id() ); @@ -870,7 +883,7 @@ private function save_product_settings( WC_Product $product ) { $woo_product->set_description( sanitize_text_field( wp_unslash( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ) ) ); $woo_product->set_rich_text_description( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ); } - + if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) { $woo_product->set_price( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) ); } @@ -931,11 +944,12 @@ public function on_product_delete( int $product_id ) { /** * Deletes Facebook product. * - * @internal + * @param \WC_Product $product WooCommerce product object * * @since 2.3.0 * - * @param \WC_Product $product WooCommerce product object + * @internal + * */ public function delete_fb_product( $product ) { @@ -971,11 +985,12 @@ public function delete_fb_product( $product ) { /** * Updates Facebook Visibility upon trashing and restore. * + * @param string $new_status + * @param string $old_status + * @param \WP_post $post + * * @internal * - * @param string $new_status - * @param string $old_status - * @param \WP_post $post */ public function fb_change_product_published_status( $new_status, $old_status, $post ) { if ( ! $post ) { @@ -1017,9 +1032,10 @@ public function fb_change_product_published_status( $new_status, $old_status, $p /** * Re-publish restored variable product. * + * @param int $post_id + * * @internal * - * @param int $post_id */ public function fb_restore_untrashed_variable_product( $post_id ) { $product = wc_get_product( $post_id ); @@ -1048,11 +1064,12 @@ public function fb_restore_untrashed_variable_product( $post_id ) { * Change from trash status -> publish status * No need to update for change from trash <-> unpublish status * - * @since 2.0.2 - * * @param string $new_status * @param string $old_status + * * @return bool + * @since 2.0.2 + * */ private function should_update_visibility_for_product_status_change( $new_status, $old_status ) { return ( $old_status === 'publish' && $new_status !== 'publish' ) || ( $old_status === 'trash' && $new_status === 'publish' ) || ( $old_status === 'future' && $new_status === 'publish' ); @@ -1061,8 +1078,9 @@ private function should_update_visibility_for_product_status_change( $new_status /** * Deletes a product from Facebook when status is changed to draft. * - * @since 3.0.27 * @param \WP_post $post + * + * @since 3.0.27 */ public function delete_draft_product( $post ) { @@ -1070,7 +1088,7 @@ public function delete_draft_product( $post ) { return; } - $this->on_product_delete ( $post->ID ); + $this->on_product_delete( $post->ID ); } @@ -1102,7 +1120,7 @@ public function on_product_publish( $product_id ) { * If the user has opt-in to remove products that are out of stock, * this function will delete the product from FB Page as well. * - * @param int $wp_id + * @param int $wp_id * @param WC_Product $woo_product * * @return bool @@ -1111,15 +1129,17 @@ public function delete_on_out_of_stock( int $wp_id, WC_Product $woo_product ): b if ( Products::product_should_be_deleted( $woo_product ) ) { $product = wc_get_product( $wp_id ); $this->delete_fb_product( $product ); + return true; } + return false; } /** * Syncs product to Facebook when saving a variable product. * - * @param int $wp_id product post ID + * @param int $wp_id product post ID * @param WC_Facebook_Product|null $woo_product product object */ public function on_variable_product_publish( $wp_id, $woo_product = null ) { @@ -1162,9 +1182,10 @@ public function on_variable_product_publish( $wp_id, $woo_product = null ) { /** * Syncs product to Facebook when saving a simple product. * - * @param int $wp_id product post ID + * @param int $wp_id product post ID * @param WC_Facebook_Product|null $woo_product product object * @param WC_Facebook_Product|null $parent_product parent object + * * @return int|mixed|void|null */ public function on_simple_product_publish( $wp_id, $woo_product = null, &$parent_product = null ) { @@ -1187,6 +1208,7 @@ public function on_simple_product_publish( $wp_id, $woo_product = null, &$parent if ( $fb_product_item_id ) { $woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product ); $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 @@ -1215,13 +1237,15 @@ public function on_simple_product_publish( $wp_id, $woo_product = null, &$parent /** * Determines whether the product with the given ID should be synced. * + * @param WC_Product $product product object + * * @since 2.0.0 * - * @param WC_Product $product product object */ public function product_should_be_synced( WC_Product $product ): bool { try { $this->facebook_for_woocommerce->get_product_sync_validator( $product )->validate(); + return true; } catch ( \Exception $e ) { return false; @@ -1232,7 +1256,8 @@ public function product_should_be_synced( WC_Product $product ): bool { * Create product group and product, store fb-specific info. * * @param WC_Facebook_Product $woo_product - * @param string|null $fb_product_group_id + * @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 { @@ -1245,13 +1270,15 @@ public function create_product_simple( WC_Facebook_Product $woo_product, string if ( $fb_product_group_id ) { return $this->create_product_item_batch_api( $woo_product, $retailer_id, $fb_product_group_id ); } + return ''; } /** * @param WC_Facebook_Product $woo_product - * @param string $retailer_id - * @param bool $variants + * @param string $retailer_id + * @param bool $variants + * * @return ?string */ public function create_product_group( WC_Facebook_Product $woo_product, string $retailer_id, bool $variants = false ): ?string { @@ -1276,12 +1303,14 @@ public function create_product_group( WC_Facebook_Product $woo_product, string $ self::FB_PRODUCT_GROUP_ID, $fb_product_group_id ); + return $fb_product_group_id; } } catch ( ApiException $e ) { $message = sprintf( 'There was an error trying to create the product group: %s', $e->getMessage() ); WC_Facebookcommerce_Utils::log( $message ); } + return null; } @@ -1306,7 +1335,7 @@ public function update_product_group( WC_Facebook_Product $woo_product ) { if ( ! $variants ) { WC_Facebookcommerce_Utils::log( sprintf( - /* translators: %1$s is referring to facebook product group id. */ + /* translators: %1$s is referring to facebook product group id. */ __( 'Nothing to update for product group for %1$s', 'facebook-for-woocommerce' @@ -1314,6 +1343,7 @@ public function update_product_group( WC_Facebook_Product $woo_product ) { $fb_product_group_id ) ); + return; } @@ -1353,10 +1383,10 @@ public function update_product_group( WC_Facebook_Product $woo_product ) { * Creates a product item using the facebook Catalog Batch API. This replaces existing functionality, * which is currently using facebook Product Item API implemented by `WC_Facebookcommerce_Integration::create_product_item` * - * @since 3.1.7 * @param WC_Facebook_Product $woo_product - * @param string $retailer_id - **/ + * @param string $retailer_id + **@since 3.1.7 + */ public function create_product_item_batch_api( $woo_product, $retailer_id, $product_group_id ): string { try { $product_data = $woo_product->prepare_product( $retailer_id, \WC_Facebook_Product::PRODUCT_PREP_TYPE_ITEMS_BATCH ); @@ -1375,6 +1405,7 @@ public function create_product_item_batch_api( $woo_product, $retailer_id, $prod $message = sprintf( 'There was an error trying to create a product item: %s', $e->getMessage() ); WC_Facebookcommerce_Utils::log( $message ); } + return ''; } @@ -1404,6 +1435,7 @@ public function create_product_item( $woo_product, $retailer_id, $product_group_ $message = sprintf( 'There was an error trying to create a product item: %s', $e->getMessage() ); WC_Facebookcommerce_Utils::log( $message ); } + return ''; } @@ -1411,25 +1443,26 @@ public function create_product_item( $woo_product, $retailer_id, $product_group_ * Determines if there is a matching variation for the default attributes. * Select closest matching if best can't be found. * - * @since 2.6.6 - * The algorithm only considers the variations that already have been synchronized to the catalog successfully. + * @param WC_Facebook_Product $woo_product + * @param string $fb_product_group_id * + * @return integer|null Facebook Catalog variation id. * @since 2.1.2 * - * @param WC_Facebook_Product $woo_product - * @param string $fb_product_group_id - * @return integer|null Facebook Catalog variation id. + * @since 2.6.6 + * The algorithm only considers the variations that already have been synchronized to the catalog successfully. + * */ private function get_product_group_default_variation( WC_Facebook_Product $woo_product, string $fb_product_group_id ) { $default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' ); - $default_variation = null; + $default_variation = null; // Fetch variations that exist in the catalog. $existing_catalog_variations = $this->find_variation_product_item_ids( $fb_product_group_id ); $existing_catalog_variations_retailer_ids = array_keys( $existing_catalog_variations ); // All woocommerce variations for the product. - $product_variations = $woo_product->woo_product->get_available_variations(); + $product_variations = $woo_product->woo_product->get_available_variations(); if ( ! empty( $default_attributes ) ) { @@ -1443,13 +1476,13 @@ private function get_product_group_default_variation( WC_Facebook_Product $woo_p ); // Check if currently processed variation exist in the catalog. - if (!in_array($fb_retailer_id, $existing_catalog_variations_retailer_ids)) { + if ( ! in_array( $fb_retailer_id, $existing_catalog_variations_retailer_ids ) ) { continue; } - $variation_attributes = $this->get_product_variation_attributes($variation); - $variation_attributes_count = count($variation_attributes); - $matching_attributes_count = count(array_intersect_assoc($default_attributes, $variation_attributes)); + $variation_attributes = $this->get_product_variation_attributes( $variation ); + $variation_attributes_count = count( $variation_attributes ); + $matching_attributes_count = count( array_intersect_assoc( $default_attributes, $variation_attributes ) ); // Check how much current variation matches the selected default attributes. if ( $matching_attributes_count === $variation_attributes_count ) { @@ -1468,12 +1501,13 @@ private function get_product_group_default_variation( WC_Facebook_Product $woo_p * Filter product group default variation. * This can be used to customize the choice of a default variation (e.g. choose one with the lowest price). * - * @since 2.6.25 * @param integer|null Facebook Catalog variation id. * @param \WC_Facebook_Product WooCommerce product. * @param string product group ID. * @param array List of available WC_Product variations. * @param array List of Product Item IDs indexed by the variation's retailer ID. + * + * @since 2.6.25 */ return apply_filters( 'wc_facebook_product_group_default_variation', @@ -1488,10 +1522,11 @@ private function get_product_group_default_variation( WC_Facebook_Product $woo_p /** * Parses given product variation for it's attributes * - * @since 2.1.2 - * * @param array $variation + * * @return array + * @since 2.1.2 + * */ private function get_product_variation_attributes( array $variation ): array { $final_attributes = []; @@ -1508,7 +1543,8 @@ private function get_product_variation_attributes( array $variation ): array { * Update existing product using batch API. * * @param WC_Facebook_Product $woo_product - * @param string $fb_product_item_id + * @param string $fb_product_item_id + * * @return void */ public function update_product_item_batch_api( WC_Facebook_Product $woo_product, string $fb_product_item_id ): void { @@ -1539,7 +1575,8 @@ public function update_product_item_batch_api( WC_Facebook_Product $woo_product, * Update existing product. * * @param WC_Facebook_Product $woo_product - * @param string $fb_product_item_id + * @param string $fb_product_item_id + * * @return void */ public function update_product_item( WC_Facebook_Product $woo_product, string $fb_product_item_id ): void { @@ -1572,11 +1609,11 @@ public function update_product_item( WC_Facebook_Product $woo_product, string $f /** * Create or update product set * - * @since 2.3.0 - * * @param array $product_set_data Product Set data. - * @param int $product_set_id Product Set Term Id. - **/ + * @param int $product_set_id Product Set Term Id. + **@since 2.3.0 + * + */ public function create_or_update_product_set_item( $product_set_data, $product_set_id ) { // check if exists in FB $fb_product_set_id = get_term_meta( $product_set_id, self::FB_PRODUCT_SET_ID, true ); @@ -1606,6 +1643,7 @@ public function create_or_update_product_set_item( $product_set_data, $product_s * Delete product set * * @param string $fb_product_set_id Facebook Product Set ID. + * * @return void * @throws ApiException * @throws \WooCommerce\Facebook\API\Exceptions\Request_Limit_Reached @@ -1630,6 +1668,7 @@ public function delete_product_set_item( string $fb_product_set_id ) { * - should_sync: Don't display if the product is not supposed to be synced. * * @param WP_Post $post Wordpress Post + * * @return void */ public function display_batch_api_completed( $post ) { @@ -1645,10 +1684,10 @@ public function display_batch_api_completed( $post ) { try { facebook_for_woocommerce()->get_product_sync_validator( $fb_product->woo_product )->validate(); } catch ( \Exception $e ) { - $should_sync = false; + $should_sync = false; } - if( $should_sync ) { + if ( $should_sync ) { if ( $fb_product->woo_product->is_type( 'variable' ) ) { $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $post->ID, $fb_product->woo_product ); } else { @@ -1658,9 +1697,9 @@ public function display_batch_api_completed( $post ) { if ( $fb_product_item_id ) { $this->display_success_message( - '' . + '' . 'View product on Meta catalog' ); } @@ -1735,10 +1774,11 @@ public function ajax_check_feed_upload_status_v2() { /** * Display custom success message (sugar). * - * @deprecated 2.1.0 - * * @param string $msg + * * @return void + * @deprecated 2.1.0 + * */ public function display_success_message( string $msg ): void { $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg; @@ -1753,6 +1793,7 @@ public function display_success_message( string $msg ): void { * Display custom info message (sugar). * * @param string $msg + * * @return void */ public function display_info_message( string $msg ): void { @@ -1769,6 +1810,7 @@ public function display_info_message( string $msg ): void { * Call remove_sticky_message or wait for time out. * * @param string $msg + * * @return void */ public function display_sticky_message( string $msg ): void { @@ -1805,6 +1847,7 @@ public function remove_resync_message() { * Logs and stores custom error message (sugar). * * @param string $msg + * * @return void */ public function display_error_message( string $msg ): void { @@ -1816,6 +1859,7 @@ public function display_error_message( string $msg ): void { * Displays out of sync message if products are edited using WooCommerce Advanced Bulk Edit. * * @param string $import_id + * * @return void */ public function ajax_woo_adv_bulk_edit_compat( string $import_id ): void { @@ -1835,12 +1879,16 @@ public function ajax_woo_adv_bulk_edit_compat( string $import_id ): void { * Display import message. * * @param string $import_id + * * @return void */ public function wp_all_import_compat( string $import_id ): void { $import = new PMXI_Import_Record(); $import->getById( $import_id ); - if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], [ 'product', 'product_variation' ], true ) ) { + if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], [ + 'product', + 'product_variation' + ], true ) ) { $this->display_out_of_sync_message( 'import' ); } } @@ -1849,6 +1897,7 @@ public function wp_all_import_compat( string $import_id ): void { * Displays out of sync message. * * @param string $action_name + * * @return void */ public function display_out_of_sync_message( string $action_name ): void { @@ -1866,7 +1915,8 @@ public function display_out_of_sync_message( string $action_name ): void { * id error, update existing ID. * * @param stdClass $error_data - * @param int $wpid + * @param int $wpid + * * @return null **/ public function get_existing_fbid( stdClass $error_data, int $wpid ) { @@ -1876,6 +1926,7 @@ public function get_existing_fbid( stdClass $error_data, int $wpid ) { self::FB_PRODUCT_GROUP_ID, (string) $error_data->product_group_id ); + return $error_data->product_group_id; } elseif ( isset( $error_data->product_item_id ) ) { update_post_meta( @@ -1883,6 +1934,7 @@ public function get_existing_fbid( stdClass $error_data, int $wpid ) { self::FB_PRODUCT_ITEM_ID, (string) $error_data->product_item_id ); + return $error_data->product_item_id; } else { return null; @@ -1927,7 +1979,7 @@ public function get_sample_product_feed() { $feed_item = [ 'title' => strip_tags( $product_data['name'] ), 'availability' => $woo_product->is_in_stock() ? 'in stock' : - 'out of stock', + 'out of stock', 'description' => strip_tags( $product_data['description'] ), 'id' => $product_data['retailer_id'], 'image_link' => $product_data['image_url'], @@ -1940,6 +1992,7 @@ public function get_sample_product_feed() { // https://codex.wordpress.org/Function_Reference/wp_reset_postdata wp_reset_postdata(); ob_end_clean(); + return json_encode( [ $items ] ); } @@ -1965,6 +2018,7 @@ public function reset_all_products() { 'Not resetting any FBIDs from products, must call reset from admin context.' ); + return false; } @@ -1974,7 +2028,7 @@ public function reset_all_products() { $post_ids = get_posts( [ 'post_type' => 'product', - 'posts_per_page' => -1, + 'posts_per_page' => - 1, 'fields' => 'ids', ] ); @@ -1985,7 +2039,7 @@ public function reset_all_products() { get_posts( [ 'post_type' => 'product_variation', - 'posts_per_page' => -1, + 'posts_per_page' => - 1, 'post_parent' => $post_id, 'fields' => 'ids', ] @@ -1997,6 +2051,7 @@ public function reset_all_products() { $this->delete_post_meta_loop( $post_ids ); WC_Facebookcommerce_Utils::log( 'Product FBIDs deleted' ); + return true; } @@ -2104,7 +2159,7 @@ private function sync_facebook_products() { } $message = sprintf( - /* translators: Placeholders %s - error message */ + /* translators: Placeholders %s - error message */ __( 'There was an error trying to sync the products to Facebook. %s', 'facebook-for-woocommerce' ), $error_message ); @@ -2116,10 +2171,10 @@ private function sync_facebook_products() { /** * Syncs Facebook products using the background processor. * - * @since 1.10.2 * @return bool * @throws ApiException Some comment. * @throws PluginException If product sync disabled. + * @since 1.10.2 */ private function sync_facebook_products_using_background_processor() { if ( ! $this->is_product_sync_enabled() ) { @@ -2153,7 +2208,7 @@ private function sync_facebook_products_using_background_processor() { } try { - $catalog = $this->facebook_for_woocommerce->get_api()->get_catalog($this->get_product_catalog_id()); + $catalog = $this->facebook_for_woocommerce->get_api()->get_catalog( $this->get_product_catalog_id() ); } catch ( ApiException $e ) { $message = sprintf( 'There was an error trying to delete a product set item: %s', $e->getMessage() ); WC_Facebookcommerce_Utils::log( $message ); @@ -2223,7 +2278,7 @@ private function sync_facebook_products_using_background_processor() { ); $this->on_product_publish( $post_id ); - $count++; + $count ++; } WC_Facebookcommerce_Utils::log( 'Synced ' . $count . ' products' ); $this->remove_sticky_message(); @@ -2255,9 +2310,9 @@ public function ajax_fb_toggle_visibility() { /** * Gets the product catalog ID. * + * @return string * @since 1.10.0 * - * @return string */ public function get_product_catalog_id() { if ( ! is_string( $this->product_catalog_id ) ) { @@ -2268,10 +2323,11 @@ public function get_product_catalog_id() { /** * Filters the Facebook product catalog ID. * - * @since 1.10.0 - * * @param string $product_catalog_id Facebook product catalog ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return apply_filters( 'wc_facebook_product_catalog_id', $this->product_catalog_id, $this ); } @@ -2279,9 +2335,9 @@ public function get_product_catalog_id() { /** * Gets the external merchant settings ID. * + * @return string * @since 1.10.0 * - * @return string */ public function get_external_merchant_settings_id() { if ( ! is_string( $this->external_merchant_settings_id ) ) { @@ -2292,10 +2348,11 @@ public function get_external_merchant_settings_id() { /** * Filters the Facebook external merchant settings ID. * - * @since 1.10.0 - * * @param string $external_merchant_settings_id Facebook external merchant settings ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (string) apply_filters( 'wc_facebook_external_merchant_settings_id', $this->external_merchant_settings_id, $this ); } @@ -2303,9 +2360,9 @@ public function get_external_merchant_settings_id() { /** * Gets the feed ID. * + * @return string * @since 1.10.0 * - * @return string */ public function get_feed_id() { if ( ! is_string( $this->feed_id ) ) { @@ -2316,10 +2373,11 @@ public function get_feed_id() { /** * Filters the Facebook feed ID. * - * @since 1.10.0 - * * @param string $feed_id Facebook feed ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (string) apply_filters( 'wc_facebook_feed_id', $this->feed_id, $this ); } @@ -2327,9 +2385,9 @@ public function get_feed_id() { /*** * Gets the Facebook Upload ID. * + * @return string * @since 1.11.0 * - * @return string */ public function get_upload_id() { if ( ! is_string( $this->upload_id ) ) { @@ -2340,10 +2398,11 @@ public function get_upload_id() { /** * Filters the Facebook upload ID. * - * @since 1.11.0 - * * @param string $upload_id Facebook upload ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.11.0 + * */ return (string) apply_filters( 'wc_facebook_upload_id', $this->upload_id, $this ); } @@ -2351,9 +2410,9 @@ public function get_upload_id() { /** * Gets the Facebook pixel install time in UTC seconds. * + * @return int * @since 1.10.0 * - * @return int */ public function get_pixel_install_time() { if ( ! (int) $this->pixel_install_time ) { @@ -2364,10 +2423,11 @@ public function get_pixel_install_time() { /** * Filters the Facebook pixel install time. * - * @since 1.10.0 - * * @param string $pixel_install_time Facebook pixel install time in UTC seconds, or null if none set * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (int) apply_filters( 'wc_facebook_pixel_install_time', $this->pixel_install_time, $this ); } @@ -2375,9 +2435,9 @@ public function get_pixel_install_time() { /** * Gets the configured JS SDK version. * + * @return string * @since 1.10.0 * - * @return string */ public function get_js_sdk_version() { if ( ! is_string( $this->js_sdk_version ) ) { @@ -2388,10 +2448,11 @@ public function get_js_sdk_version() { /** * Filters the Facebook JS SDK version. * - * @since 1.10.0 - * * @param string $js_sdk_version Facebook JS SDK version * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (string) apply_filters( 'wc_facebook_js_sdk_version', $this->js_sdk_version, $this ); } @@ -2399,18 +2460,19 @@ public function get_js_sdk_version() { /** * Gets the configured Facebook page ID. * + * @return string * @since 1.10.0 * - * @return string */ public function get_facebook_page_id() { /** * Filters the configured Facebook page ID. * - * @since 1.10.0 - * * @param string $page_id the configured Facebook page ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (string) apply_filters( 'wc_facebook_page_id', get_option( self::SETTING_FACEBOOK_PAGE_ID, '' ), $this ); } @@ -2418,18 +2480,19 @@ public function get_facebook_page_id() { /** * Gets the configured Facebook pixel ID. * + * @return string * @since 1.10.0 * - * @return string */ public function get_facebook_pixel_id() { /** * Filters the configured Facebook pixel ID. * - * @since 1.10.0 - * * @param string $pixel_id the configured Facebook pixel ID * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (string) apply_filters( 'wc_facebook_pixel_id', get_option( self::SETTING_FACEBOOK_PIXEL_ID, '' ), $this ); } @@ -2437,18 +2500,19 @@ public function get_facebook_pixel_id() { /** * Gets the IDs of the categories to be excluded from sync. * + * @return int[] * @since 1.10.0 * - * @return int[] */ public function get_excluded_product_category_ids() { /** * Filters the configured excluded product category IDs. * - * @since 1.10.0 - * * @param int[] $category_ids the configured excluded product category IDs * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, [] ), $this ); } @@ -2456,18 +2520,19 @@ public function get_excluded_product_category_ids() { /** * Gets the IDs of the tags to be excluded from sync. * + * @return int[] * @since 1.10.0 * - * @return int[] */ public function get_excluded_product_tag_ids() { /** * Filters the configured excluded product tag IDs. * - * @since 1.10.0 - * * @param int[] $tag_ids the configured excluded product tag IDs * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, [] ), $this ); } @@ -2475,18 +2540,19 @@ public function get_excluded_product_tag_ids() { /** * Gets the configured product description mode. * + * @return string * @since 1.10.0 * - * @return string */ public function get_product_description_mode() { /** * Filters the configured product description mode. * - * @since 1.10.0 - * * @param string $mode the configured product description mode * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ $mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this ); @@ -2508,9 +2574,10 @@ public function get_product_description_mode() { /** * Updates the Facebook product catalog ID. * + * @param string $value product catalog ID value + * * @since 1.10.0 * - * @param string $value product catalog ID value */ public function update_product_catalog_id( $value ) { $this->product_catalog_id = $this->sanitize_facebook_credential( $value ); @@ -2521,9 +2588,10 @@ public function update_product_catalog_id( $value ) { /** * Updates the Facebook external merchant settings ID. * + * @param string $value external merchant settings ID value + * * @since 1.10.0 * - * @param string $value external merchant settings ID value */ public function update_external_merchant_settings_id( $value ) { $this->external_merchant_settings_id = $this->sanitize_facebook_credential( $value ); @@ -2534,9 +2602,10 @@ public function update_external_merchant_settings_id( $value ) { /** * Updates the Facebook feed ID. * + * @param string $value feed ID value + * * @since 1.10.0 * - * @param string $value feed ID value */ public function update_feed_id( $value ) { $this->feed_id = $this->sanitize_facebook_credential( $value ); @@ -2547,9 +2616,10 @@ public function update_feed_id( $value ) { /** * Updates the Facebook upload ID. * + * @param string $value upload ID value + * * @since 1.11.0 * - * @param string $value upload ID value */ public function update_upload_id( $value ) { $this->upload_id = $this->sanitize_facebook_credential( $value ); @@ -2560,9 +2630,10 @@ public function update_upload_id( $value ) { /** * Updates the Facebook pixel install time. * + * @param int $value pixel install time, in UTC seconds + * * @since 1.10.0 * - * @param int $value pixel install time, in UTC seconds */ public function update_pixel_install_time( $value ) { $value = (int) $value; @@ -2575,9 +2646,10 @@ public function update_pixel_install_time( $value ) { /** * Updates the Facebook JS SDK version. * + * @param string $value JS SDK version + * * @since 1.10.0 * - * @param string $value JS SDK version */ public function update_js_sdk_version( $value ) { $this->js_sdk_version = $this->sanitize_facebook_credential( $value ); @@ -2588,10 +2660,11 @@ public function update_js_sdk_version( $value ) { /** * Sanitizes a value that's a Facebook credential. * - * @since 1.10.0 - * * @param string $value value to sanitize + * * @return string + * @since 1.10.0 + * */ private function sanitize_facebook_credential( $value ) { return wc_clean( is_string( $value ) ? $value : '' ); @@ -2600,9 +2673,9 @@ private function sanitize_facebook_credential( $value ) { /** * Determines whether Facebook for WooCommerce is configured. * + * @return bool * @since 1.10.0 * - * @return bool */ public function is_configured() { return $this->get_facebook_page_id() && $this->facebook_for_woocommerce->get_connection_handler()->is_connected(); @@ -2611,18 +2684,19 @@ public function is_configured() { /** * Determines whether advanced matching is enabled. * + * @return bool * @since 1.10.0 * - * @return bool */ public function is_advanced_matching_enabled() { /** * Filters whether advanced matching is enabled. * - * @since 1.10.0 - * * @param bool $is_enabled whether advanced matching is enabled * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (bool) apply_filters( 'wc_facebook_is_advanced_matching_enabled', true, $this ); } @@ -2630,57 +2704,59 @@ public function is_advanced_matching_enabled() { /** * Determines whether product sync is enabled. * + * @return bool * @since 1.10.0 * - * @return bool */ public function is_product_sync_enabled() { /** * Filters whether product sync is enabled. * - * @since 1.10.0 - * * @param bool $is_enabled whether product sync is enabled * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.0 + * */ return (bool) apply_filters( 'wc_facebook_is_product_sync_enabled', 'yes' === get_option( self::SETTING_ENABLE_PRODUCT_SYNC, 'yes' ), $this ); } /** - * Return true if (legacy) feed generation is enabled. - * - * Feed generation for product sync is enabled by default, and generally recommended. - * Large stores, or stores running on shared hosting (low resources) may have issues - * with feed generation. This option allows those stores to disable generation to - * work around the issue. - * - * Note - this is temporary. In a future release, an improved feed system will be - * implemented, which should work well for all stores. This option will not disable - * the new improved implementation. - * - * @since 2.5.0 - * - * @return bool - */ - public function is_legacy_feed_file_generation_enabled() { - return 'yes' === get_option( self::OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED, 'yes' ); - } + * Return true if (legacy) feed generation is enabled. + * + * Feed generation for product sync is enabled by default, and generally recommended. + * Large stores, or stores running on shared hosting (low resources) may have issues + * with feed generation. This option allows those stores to disable generation to + * work around the issue. + * + * Note - this is temporary. In a future release, an improved feed system will be + * implemented, which should work well for all stores. This option will not disable + * the new improved implementation. + * + * @return bool + * @since 2.5.0 + * + */ + public function is_legacy_feed_file_generation_enabled() { + return 'yes' === get_option( self::OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED, 'yes' ); + } /** * Determines whether debug mode is enabled. * + * @return bool * @since 1.10.2 * - * @return bool */ public function is_debug_mode_enabled() { /** * Filters whether debug mode is enabled. * - * @since 1.10.2 - * * @param bool $is_enabled whether debug mode is enabled * @param \WC_Facebookcommerce_Integration $integration the integration instance + * + * @since 1.10.2 + * */ return (bool) apply_filters( 'wc_facebook_is_debug_mode_enabled', 'yes' === get_option( self::SETTING_ENABLE_DEBUG_MODE ), $this ); } @@ -2688,9 +2764,9 @@ public function is_debug_mode_enabled() { /** * Determines whether debug mode is enabled. * + * @return bool * @since 2.6.6 * - * @return bool */ public function is_new_style_feed_generation_enabled() { return (bool) ( 'yes' === get_option( self::SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR ) ); @@ -2710,15 +2786,16 @@ public function are_headers_requested_for_debug() { /*** * Determines if the feed has been migrated from FBE 1 to FBE 1.5 * + * @return bool * @since 1.11.0 * - * @return bool */ public function is_feed_migrated() { if ( ! is_bool( $this->feed_migrated ) ) { $value = get_option( 'wc_facebook_feed_migrated', 'no' ); $this->feed_migrated = wc_string_to_bool( $value ); } + return $this->feed_migrated; } @@ -2727,6 +2804,7 @@ public function is_feed_migrated() { * * @param string $message * @param string $type + * * @return string */ private function get_message_html( string $message, string $type = 'error' ): string { @@ -2804,6 +2882,7 @@ public function admin_options() { * Delete product item by id. * * @param int $wp_id + * * @return void */ public function delete_product_item( int $wp_id ): void { @@ -2825,9 +2904,10 @@ public function delete_product_item( int $wp_id ): void { /** * Uses the Graph API to delete the Product Group associated with the given product. * + * @param int $product_id product ID + * * @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 ); @@ -2847,11 +2927,13 @@ public function delete_product_group( int $product_id ) { * Filter function for woocommerce_duplicate_product_exclude_meta filter. * * @param array $to_delete + * * @return array */ public function fb_duplicate_product_reset_meta( array $to_delete ): array { $to_delete[] = self::FB_PRODUCT_ITEM_ID; $to_delete[] = self::FB_PRODUCT_GROUP_ID; + return $to_delete; } @@ -2859,7 +2941,7 @@ public function fb_duplicate_product_reset_meta( array $to_delete ): array { * Helper function to update FB visibility. * * @param int|WC_Product $product_id product ID or product object - * @param string $visibility visibility + * @param string $visibility visibility */ public function update_fb_visibility( $product_id, $visibility ) { // bail if the plugin is not configured properly @@ -2900,7 +2982,8 @@ public function update_fb_visibility( $product_id, $visibility ) { $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $product->get_id() ); if ( ! $fb_product_item_id ) { \WC_Facebookcommerce_Utils::fblog( $fb_product_item_id . " doesn't exist but underwent a visibility transform.", [], true ); - return; + + return; } try { $set_visibility = $this->facebook_for_woocommerce->get_api()->update_product_item( $fb_product_item_id, [ 'visibility' => $visibility ] ); @@ -2917,9 +3000,10 @@ public function update_fb_visibility( $product_id, $visibility ) { /** * Sync product upon quick or bulk edit save action. * + * @param \WC_Product $product product object + * * @internal * - * @param \WC_Product $product product object */ public function on_quick_and_bulk_edit_save( $product ) { // bail if not a product or product is not enabled for sync @@ -2943,9 +3027,10 @@ public function on_quick_and_bulk_edit_save( $product ) { /** * Gets Facebook product ID from meta or from Facebook API. * - * @param string $fbid_type ID type (group or item) - * @param int $wp_id post ID + * @param string $fbid_type ID type (group or item) + * @param int $wp_id post ID * @param WC_Facebook_Product|null $woo_product product + * * @return string facebook product id or an empty string */ public function get_product_fbid( string $fbid_type, int $wp_id, $woo_product = null ) { @@ -2981,7 +3066,7 @@ public function get_product_fbid( string $fbid_type, int $wp_id, $woo_product = WC_Facebookcommerce_Utils::log( $e->getMessage() ); $this->display_error_message( sprintf( - /* translators: Placeholders %1$s - original error message from Facebook API */ + /* translators: Placeholders %1$s - original error message from Facebook API */ esc_html__( 'There was an issue connecting to the Facebook API: %s', 'facebook-for-woocommerce' ), $e->getMessage() ) @@ -3007,9 +3092,9 @@ public function ajax_display_test_result() { $response['pass'] = 'false'; $response['debug_info'] = get_transient( 'facebook_plugin_test_fail' ); $response['stack_trace'] = - get_transient( 'facebook_plugin_test_stack_trace' ); + get_transient( 'facebook_plugin_test_stack_trace' ); $response['stack_trace'] = - preg_replace( "/\n/", '
', $response['stack_trace'] ); + preg_replace( "/\n/", '
', $response['stack_trace'] ); delete_transient( 'facebook_plugin_test_fail' ); delete_transient( 'facebook_plugin_test_stack_trace' ); } @@ -3021,12 +3106,12 @@ public function ajax_display_test_result() { /** * Determines if the enhanced onboarding (iframe) should be used. * + * @return bool * @since 2.0.0 * - * @return bool */ public function use_enhanced_onboarding() { - return false; + return true; } } diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 94a3448cc..65719062e 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -27,9 +27,9 @@ class Connection extends Abstract_Settings_Screen { /** * Determines if we should use enhanced onboarding. * + * @return bool * @since 2.0.0 * - * @return bool */ protected function use_enhanced_onboarding() { return facebook_for_woocommerce()->get_integration()->use_enhanced_onboarding(); @@ -47,6 +47,12 @@ public function __construct() { // Add action to enqueue the message handler script add_action( 'admin_footer', array( $this, 'render_message_handler' ) ); + + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); + } + + public function enqueue_admin_scripts() { + wp_enqueue_script( 'wp-api' ); } /** @@ -72,7 +78,7 @@ public function add_notices() { if ( get_transient( 'wc_facebook_connection_failed' ) ) { $message = sprintf( - /* translators: Placeholders: %1$s - tag, %2$s - tag, %3$s - tag, %4$s - tag, %5$s - tag, %6$s - tag */ + /* translators: Placeholders: %1$s - tag, %2$s - tag, %3$s - tag, %4$s - tag, %5$s - tag, %6$s - tag */ __( '%1$sHeads up!%2$s It looks like there was a problem with reconnecting your site to Facebook. Please %3$sclick here%4$s to try again, or %5$sget in touch with our support team%6$s for assistance.', 'facebook-for-woocommerce' ), '', '', @@ -121,6 +127,7 @@ public function render() { // Check if we should render iframe if ( $this->use_enhanced_onboarding() ) { $this->render_facebook_iframe(); + return; } @@ -208,58 +215,58 @@ public function render() { - $item ) : + $item ) : - $item = wp_parse_args( - $item, - array( - 'label' => '', - 'value' => '', - 'url' => '', - ) - ); + $item = wp_parse_args( + $item, + array( + 'label' => '', + 'value' => '', + 'url' => '', + ) + ); - ?> + ?> - + - + - - + + - +
@@ -311,9 +318,10 @@ private function render_facebook_iframe() { /** * Renders the legacy Facebook CTA box. * + * @param bool $is_connected whether the plugin is connected + * * @since 2.0.0 * - * @param bool $is_connected whether the plugin is connected */ private function render_facebook_box( $is_connected ) { if ( $is_connected ) { @@ -341,7 +349,8 @@ private function render_facebook_box( $is_connected ) {
- + - + @@ -377,7 +387,11 @@ public function render_message_handler() { } ?> @@ -455,9 +469,9 @@ public function render_message_handler() { /** * Gets the screen settings. * + * @return array * @since 2.0.0 * - * @return array */ public function get_settings() { diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index f57544897..6e5a23e0f 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -26,7 +26,7 @@ class MetaExtension { /** @var string Client token */ const CLIENT_TOKEN = '195311308289826|52dcd04d6c7ed113121b5eb4be23b4a7'; - const APP_ID = '474166926521348'; + const APP_ID = '474166926521348'; /** @var string Business name */ const BUSINESS_NAME = 'WooCommerce'; @@ -37,22 +37,22 @@ class MetaExtension { const COMMERCE_HUB_URL = 'https://www.commercepartnerhub.com/'; /** @var string Option names for Facebook settings */ - const OPTION_ACCESS_TOKEN = 'wc_facebook_access_token'; - const OPTION_MERCHANT_ACCESS_TOKEN = 'wc_facebook_merchant_access_token'; - const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token'; - const OPTION_SYSTEM_USER_ID = 'wc_facebook_system_user_id'; - const OPTION_BUSINESS_MANAGER_ID = 'wc_facebook_business_manager_id'; - const OPTION_AD_ACCOUNT_ID = 'wc_facebook_ad_account_id'; - const OPTION_INSTAGRAM_BUSINESS_ID = 'wc_facebook_instagram_business_id'; - const OPTION_COMMERCE_MERCHANT_SETTINGS_ID = 'wc_facebook_commerce_merchant_settings_id'; - const OPTION_EXTERNAL_BUSINESS_ID = 'wc_facebook_external_business_id'; + const OPTION_ACCESS_TOKEN = 'wc_facebook_access_token'; + const OPTION_MERCHANT_ACCESS_TOKEN = 'wc_facebook_merchant_access_token'; + const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token'; + const OPTION_SYSTEM_USER_ID = 'wc_facebook_system_user_id'; + const OPTION_BUSINESS_MANAGER_ID = 'wc_facebook_business_manager_id'; + const OPTION_AD_ACCOUNT_ID = 'wc_facebook_ad_account_id'; + const OPTION_INSTAGRAM_BUSINESS_ID = 'wc_facebook_instagram_business_id'; + const OPTION_COMMERCE_MERCHANT_SETTINGS_ID = 'wc_facebook_commerce_merchant_settings_id'; + const OPTION_EXTERNAL_BUSINESS_ID = 'wc_facebook_external_business_id'; const OPTION_COMMERCE_PARTNER_INTEGRATION_ID = 'wc_facebook_commerce_partner_integration_id'; - const OPTION_PRODUCT_CATALOG_ID = 'wc_facebook_product_catalog_id'; - const OPTION_PIXEL_ID = 'wc_facebook_pixel_id'; - const OPTION_PROFILES = 'wc_facebook_profiles'; - const OPTION_INSTALLED_FEATURES = 'wc_facebook_installed_features'; - const OPTION_HAS_CONNECTED_FBE_2 = 'wc_facebook_has_connected_fbe_2'; - const OPTION_HAS_AUTHORIZED_PAGES = 'wc_facebook_has_authorized_pages_read_engagement'; + const OPTION_PRODUCT_CATALOG_ID = 'wc_facebook_product_catalog_id'; + const OPTION_PIXEL_ID = 'wc_facebook_pixel_id'; + const OPTION_PROFILES = 'wc_facebook_profiles'; + const OPTION_INSTALLED_FEATURES = 'wc_facebook_installed_features'; + const OPTION_HAS_CONNECTED_FBE_2 = 'wc_facebook_has_connected_fbe_2'; + const OPTION_HAS_AUTHORIZED_PAGES = 'wc_facebook_has_authorized_pages_read_engagement'; /** @var string Nonce action */ const NONCE_ACTION = 'wc_facebook_ajax_token_update'; @@ -73,9 +73,10 @@ public function __construct() { /** * Validates if the required tokens are present. * - * @since 2.0.0 * @param array $tokens Array of tokens to validate. + * * @return bool True if all required tokens are present. + * @since 2.0.0 */ private static function validate_required_tokens( $tokens ) { return ! empty( $tokens['access_token'] ) && ! empty( $tokens['merchant_access_token'] ) && ! empty( $tokens['page_access_token'] ); @@ -84,9 +85,10 @@ private static function validate_required_tokens( $tokens ) { /** * Updates Facebook settings options. * - * @since 2.0.0 * @param array $settings Array of settings to update. + * * @return void + * @since 2.0.0 */ private static function update_settings( $settings ) { foreach ( $settings as $key => $value ) { @@ -99,11 +101,12 @@ private static function update_settings( $settings ) { /** * Sanitizes and retrieves a value from an array. * - * @since 2.0.0 - * @param array $data Array to retrieve value from. + * @param array $data Array to retrieve value from. * @param string $key Key to retrieve. - * @param bool $sanitize Whether to sanitize the value. + * @param bool $sanitize Whether to sanitize the value. + * * @return mixed|string The value or empty string if not set. + * @since 2.0.0 */ private static function get_param_value( $data, $key, $sanitize = true ) { if ( ! isset( $data[ $key ] ) ) { @@ -122,9 +125,10 @@ private static function get_param_value( $data, $key, $sanitize = true ) { /** * Maps request parameters to option names. * - * @since 2.0.0 * @param array $params Request parameters. + * * @return array Mapped options with values. + * @since 2.0.0 */ private static function map_params_to_options( $params ) { $options = array(); @@ -171,9 +175,10 @@ private static function map_params_to_options( $params ) { /** * Updates connection status flags based on tokens. * - * @since 2.0.0 * @param array $params Parameters containing tokens. + * * @return void + * @since 2.0.0 */ private static function update_connection_status( $params ) { if ( ! empty( $params['access_token'] ) ) { @@ -188,8 +193,8 @@ private static function update_connection_status( $params ) { /** * Clears Facebook integration options. * - * @since 2.0.0 * @return void + * @since 2.0.0 */ private static function clear_integration_options() { $options = array( @@ -218,7 +223,10 @@ private static function clear_integration_options() { foreach ( $options as $option_name ) { if ( in_array( $option_name, array( self::OPTION_PROFILES, self::OPTION_INSTALLED_FEATURES ), true ) ) { update_option( $option_name, null ); - } elseif ( in_array( $option_name, array( self::OPTION_HAS_CONNECTED_FBE_2, self::OPTION_HAS_AUTHORIZED_PAGES ), true ) ) { + } elseif ( in_array( $option_name, array( + self::OPTION_HAS_CONNECTED_FBE_2, + self::OPTION_HAS_AUTHORIZED_PAGES + ), true ) ) { update_option( $option_name, 'no' ); } else { update_option( $option_name, '' ); @@ -239,7 +247,8 @@ private static function clear_integration_options() { * * @param string $method HTTP method (GET, POST, etc.) * @param string $endpoint API endpoint - * @param array $params Request parameters + * @param array $params Request parameters + * * @return array Response data * @throws \Exception If the request fails. */ @@ -291,6 +300,11 @@ private static function call_api( $method, $endpoint, $params ) { * @return void */ public static function init_rest_endpoint() { + + $current_blog_id = get_current_blog_id(); + error_log( '(error_log) init_rest_endpoint called from Current Blog ID: ' . $current_blog_id ); + facebook_for_woocommerce()->log( '(fb_log) init_rest_endpoint called from Current Blog ID: ' . $current_blog_id ); + register_rest_route( 'wc-facebook/v1', 'update_fb_settings', @@ -336,12 +350,33 @@ public static function rest_update_fb_settings_permission_callback() { * - installed_features: installed features data. * * @param WP_REST_Request $request The request object. + * * @return WP_REST_Response|WP_Error */ public static function rest_update_fb_settings( WP_REST_Request $request ) { // Get JSON data from request body $params = $request->get_json_params(); + $current_blog_id = get_current_blog_id(); + error_log( '(error_log) rest_update_fb_settings called from Current Blog ID: ' . $current_blog_id ); + facebook_for_woocommerce()->log( '(fb_log) rest_update_fb_settings called from Current Blog ID: ' . $current_blog_id ); + + error_log( 'Request URI: ' . ( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : 'N/A' ) ); + error_log( 'Request Method: ' . ( isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'N/A' ) ); + error_log( 'Referer: ' . ( $request->get_header('referer') ?? 'N/A' ) ); + error_log( 'User-Agent: ' . ( $request->get_header('User-Agent') ?? 'N/A' ) ); + + // Log cookies. + error_log( 'Cookies: ' . print_r( $_COOKIE, true ) ); + + // Log request headers (if getallheaders() is available). + if ( function_exists( 'getallheaders' ) ) { + $headers = getallheaders(); + error_log( 'Request Headers: ' . print_r( $headers, true ) ); + } else { + error_log( 'getallheaders() function is not available.' ); + } + // Required parameter check if ( empty( $params['merchant_access_token'] ) ) { return new WP_Error( @@ -417,9 +452,10 @@ public static function rest_handle_uninstall() { /** * Generates the Commerce Hub iframe splash page URL. * - * @param bool $is_connected Whether the plugin is currently connected. - * @param object $plugin The plugin instance. + * @param bool $is_connected Whether the plugin is currently connected. + * @param object $plugin The plugin instance. * @param string $external_business_id External business ID. + * * @return string */ public static function generate_iframe_splash_url( $is_connected, $plugin, $external_business_id ) { @@ -430,6 +466,7 @@ public static function generate_iframe_splash_url( $is_connected, $plugin, $exte 'commerce_partner_seller_platform_type' => 'SELF_SERVE_PLATFORM', 'country_code' => WC()->countries->get_base_country(), ); + return add_query_arg( array( 'access_client_token' => self::CLIENT_TOKEN, @@ -451,6 +488,7 @@ public static function generate_iframe_splash_url( $is_connected, $plugin, $exte * Generates the Commerce Hub iframe management page URL. * * @param string $external_business_id External business ID. + * * @return string */ public static function generate_iframe_management_url( $external_business_id ) { @@ -475,6 +513,7 @@ public static function generate_iframe_management_url( $external_business_id ) { } catch ( \Exception $e ) { facebook_for_woocommerce()->log( 'Facebook Commerce Extension URL Error: ' . $e->getMessage() ); } + return ''; } } From a0e01b805ebd5c7a2977648f0ff00f61e347bdd6 Mon Sep 17 00:00:00 2001 From: jczhuoMeta <134329391+jczhuoMeta@users.noreply.github.com> Date: Wed, 5 Mar 2025 16:26:33 -0800 Subject: [PATCH 21/25] Update some params for the MiCE usecase --- includes/Handlers/Connection.php | 2 +- includes/Handlers/MetaExtension.php | 16 ++++++++-------- tests/Unit/Handlers/MetaExtensionTest.php | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/includes/Handlers/Connection.php b/includes/Handlers/Connection.php index d7acdc36b..5b137fa7b 100644 --- a/includes/Handlers/Connection.php +++ b/includes/Handlers/Connection.php @@ -955,7 +955,7 @@ private function get_connect_parameters_extras() { * * @return string */ - private function get_timezone_string() { + public function get_timezone_string() { $timezone = wc_timezone_string(); // convert +05:30 and +05:00 into Etc/GMT+5 - we ignore the minutes because Facebook does not allow minute offsets if ( preg_match( '/([+-])(\d{2}):\d{2}/', $timezone, $matches ) ) { diff --git a/includes/Handlers/MetaExtension.php b/includes/Handlers/MetaExtension.php index f57544897..7b075931f 100644 --- a/includes/Handlers/MetaExtension.php +++ b/includes/Handlers/MetaExtension.php @@ -25,13 +25,11 @@ class MetaExtension { /** @var string Client token */ - const CLIENT_TOKEN = '195311308289826|52dcd04d6c7ed113121b5eb4be23b4a7'; + const CLIENT_TOKEN = '474166926521348|92e978eb27baf47f9df578b48d430a2e'; const APP_ID = '474166926521348'; - /** @var string Business name */ - const BUSINESS_NAME = 'WooCommerce'; /** @var string API version */ - const API_VERSION = 'v18.0'; + const API_VERSION = 'v22.0'; /** @var string Commerce Hub base URL */ const COMMERCE_HUB_URL = 'https://www.commercepartnerhub.com/'; @@ -422,13 +420,15 @@ public static function rest_handle_uninstall() { * @param string $external_business_id External business ID. * @return string */ - public static function generate_iframe_splash_url( $is_connected, $plugin, $external_business_id ) { + public static function generate_iframe_splash_url( $is_connected, $plugin, $external_business_id ): string { + $connection_handler = facebook_for_woocommerce()->get_connection_handler(); $external_client_metadata = array( - 'shop_domain' => wc_get_page_permalink( 'shop' ) ? wc_get_page_permalink( 'shop' ) : \home_url(), + 'shop_domain' => wc_get_page_permalink( 'shop' ), 'admin_url' => admin_url(), 'client_version' => $plugin->get_version(), 'commerce_partner_seller_platform_type' => 'SELF_SERVE_PLATFORM', 'country_code' => WC()->countries->get_base_country(), + 'platform_store_id' => get_current_blog_id(), ); return add_query_arg( array( @@ -436,9 +436,9 @@ public static function generate_iframe_splash_url( $is_connected, $plugin, $exte 'business_vertical' => 'ECOMMERCE', 'channel' => 'COMMERCE', 'app_id' => Connection::CLIENT_ID, - 'business_name' => self::BUSINESS_NAME, + 'business_name' => rawurlencode( $connection_handler->get_business_name() ), 'currency' => get_woocommerce_currency(), - 'timezone' => 'America/Los_Angeles', + 'timezone' => $connection_handler->get_timezone_string(), 'external_business_id' => $external_business_id, 'installed' => $is_connected, 'external_client_metadata' => rawurlencode( wp_json_encode( $external_client_metadata ) ), diff --git a/tests/Unit/Handlers/MetaExtensionTest.php b/tests/Unit/Handlers/MetaExtensionTest.php index bae011bef..6f8dfef6c 100644 --- a/tests/Unit/Handlers/MetaExtensionTest.php +++ b/tests/Unit/Handlers/MetaExtensionTest.php @@ -38,11 +38,12 @@ public function test_generate_iframe_splash_url() { $plugin = facebook_for_woocommerce(); $url = MetaExtension::generate_iframe_splash_url(true, $plugin, 'test_business_id'); + $handler = facebook_for_woocommerce()->get_connection_handler(); // Assert URL contains expected parameters $this->assertStringContainsString('access_client_token=' . MetaExtension::CLIENT_TOKEN, $url); $this->assertStringContainsString('app_id=', $url); - $this->assertStringContainsString('business_name=' . urlencode(MetaExtension::BUSINESS_NAME), $url); + $this->assertStringContainsString('business_name=' . rawurlencode($handler->get_business_name()), $url); $this->assertStringContainsString('external_business_id=test_business_id', $url); $this->assertStringContainsString('installed=1', $url); $this->assertStringContainsString('external_client_metadata=', $url); From 0b085691c718abd62441821a92c58701baf8c7fa Mon Sep 17 00:00:00 2001 From: Paul Kang Date: Wed, 5 Mar 2025 16:27:40 -0800 Subject: [PATCH 22/25] Formatting fixes for multi-site --- facebook-commerce.php | 2 +- .../Admin/Settings_Screens/Connection.php | 20 +-- includes/Handlers/MetaExtension.php | 119 +++++++++--------- .../Admin/Settings_Screens/ConnectionTest.php | 15 +-- tests/Unit/Handlers/MetaExtensionTest.php | 13 +- 5 files changed, 84 insertions(+), 85 deletions(-) diff --git a/facebook-commerce.php b/facebook-commerce.php index 191e60f68..0e5ce5118 100644 --- a/facebook-commerce.php +++ b/facebook-commerce.php @@ -3111,7 +3111,7 @@ public function ajax_display_test_result() { * */ public function use_enhanced_onboarding() { - return true; + return false; } } diff --git a/includes/Admin/Settings_Screens/Connection.php b/includes/Admin/Settings_Screens/Connection.php index 65719062e..0f1e42e95 100644 --- a/includes/Admin/Settings_Screens/Connection.php +++ b/includes/Admin/Settings_Screens/Connection.php @@ -29,7 +29,6 @@ class Connection extends Abstract_Settings_Screen { * * @return bool * @since 2.0.0 - * */ protected function use_enhanced_onboarding() { return facebook_for_woocommerce()->get_integration()->use_enhanced_onboarding(); @@ -51,8 +50,15 @@ public function __construct() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); } + /** + * Enqueues the wp-api script only on the connection settings page. + * + * @since 2.0.0 + */ public function enqueue_admin_scripts() { - wp_enqueue_script( 'wp-api' ); + if ( $this->is_current_screen_page() ) { + wp_enqueue_script( 'wp-api' ); + } } /** @@ -321,7 +327,6 @@ private function render_facebook_iframe() { * @param bool $is_connected whether the plugin is connected * * @since 2.0.0 - * */ private function render_facebook_box( $is_connected ) { if ( $is_connected ) { @@ -350,7 +355,7 @@ private function render_facebook_box( $is_connected ) {
+ class="button button-primary uninstall" onclick="return confirmDialog();"> + class="button button-primary"> @@ -387,10 +392,6 @@ public function render_message_handler() { } ?>