get_namespace(), '/settings/update', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [ $this, 'handle_update' ], 'permission_callback' => [ $this, 'permission_callback' ], ] ); register_rest_route( $this->get_namespace(), '/settings/uninstall', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [ $this, 'handle_uninstall' ], 'permission_callback' => [ $this, 'permission_callback' ], ] ); } /** * Handle the update settings request. * * @since 3.5.0 * @http_method POST * @description Update Facebook settings * * @param \WP_REST_Request $wp_request The WordPress request object. * @return \WP_REST_Response */ public function handle_update( \WP_REST_Request $wp_request ): \WP_REST_Response { try { $request = new UpdateRequest( $wp_request ); $request_data = $request->get_data(); $validation_result = $request->validate(); if ( is_wp_error( $validation_result ) ) { return $this->error_response( $validation_result->get_error_message(), 400 ); } // Check if we should trigger product sync and/or metadata feed uploads for this update // Only trigger products and sets sync if catalog id is being updated $should_trigger_products_and_sets_sync = ! empty( $request_data['product_catalog_id'] ) && facebook_for_woocommerce()->get_integration()->get_product_catalog_id() !== $request_data['product_catalog_id']; // Only trigger metadata feed uploads if CPI id is being updated $should_trigger_metadata_feed_uploads = ! empty( $request_data['commerce_partner_integration_id'] ) && facebook_for_woocommerce()->get_connection_handler()->get_commerce_partner_integration_id() !== $request_data['commerce_partner_integration_id']; // Map parameters to options and update settings $options = $this->map_params_to_options( $request_data ); $this->update_settings( $options ); // Update connection status flags $this->update_connection_status( $request_data ); // Maybe trigger products sync and/or metadata feed uploads $this->maybe_trigger_feed_uploads( $should_trigger_products_and_sets_sync, $should_trigger_metadata_feed_uploads, $request_data ); return $this->success_response( [ 'message' => __( 'Facebook settings updated successfully', 'facebook-for-woocommerce' ), ] ); } catch ( \Exception $e ) { return $this->error_response( $e->getMessage(), 500 ); } } /** * Handle the uninstall request. * * @since 3.5.0 * @http_method POST * @description Uninstall Facebook integration * * @param \WP_REST_Request $wp_request The WordPress request object. * @return \WP_REST_Response */ public function handle_uninstall( \WP_REST_Request $wp_request ): \WP_REST_Response { try { $request = new UninstallRequest( $wp_request ); $validation_result = $request->validate(); if ( is_wp_error( $validation_result ) ) { return $this->error_response( $validation_result->get_error_message(), 400 ); } // Clear integration options $this->clear_integration_options(); return $this->success_response( [ 'message' => __( 'Facebook integration successfully uninstalled', 'facebook-for-woocommerce' ), ] ); } catch ( \Exception $e ) { return $this->error_response( $e->getMessage(), 500 ); } } /** * Maps request parameters to WooCommerce options. * * @since 3.5.0 * * @param array $params Request parameters. * @return array Mapped options. */ private function map_params_to_options( array $params ): array { $options = []; // Map access tokens if ( ! empty( $params['access_token'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_ACCESS_TOKEN ] = $params['access_token']; } if ( ! empty( $params['commerce_merchant_settings_id'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_COMMERCE_MERCHANT_SETTINGS_ID ] = $params['commerce_merchant_settings_id']; } if ( ! empty( $params['commerce_partner_integration_id'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_COMMERCE_PARTNER_INTEGRATION_ID ] = $params['commerce_partner_integration_id']; } if ( ! empty( $params['installed_features'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_INSTALLED_FEATURES ] = $params['installed_features']; } if ( ! empty( $params['merchant_access_token'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_MERCHANT_ACCESS_TOKEN ] = $params['merchant_access_token']; } if ( ! empty( $params['page_id'] ) ) { update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, $params['page_id'] ); } if ( ! empty( $params['pixel_id'] ) ) { $options[ \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID ] = $params['pixel_id']; update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, $params['pixel_id'] ); } if ( ! empty( $params['product_catalog_id'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID ] = $params['product_catalog_id']; } if ( ! empty( $params['profiles'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_PROFILES ] = $params['profiles']; } if ( ! empty( $params['business_manager_id'] ) ) { $options[ \WC_Facebookcommerce_Integration::OPTION_BUSINESS_MANAGER_ID ] = $params['business_manager_id']; } return $options; } /** * Updates Facebook settings options. * * @since 3.5.0 * * @param array $settings Array of settings to update. * @return void */ private function update_settings( array $settings ) { foreach ( $settings as $key => $value ) { if ( ! empty( $key ) ) { update_option( $key, $value ); } } } /** * Updates connection status flags. * * @since 3.5.0 * * @param array $params Request parameters. * @return void */ private function update_connection_status( array $params ) { // Set the connection is complete update_option( 'wc_facebook_has_connected_fbe_2', 'yes' ); update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' ); // Set the Messenger chat visibility if ( ! empty( $params['msger_chat'] ) ) { update_option( 'wc_facebook_enable_messenger', wc_bool_to_string( 'yes' === $params['msger_chat'] ) ); } } /** * Clears all integration options. * * @since 3.5.0 * * @return void */ private function clear_integration_options() { $options = [ \WC_Facebookcommerce_Integration::OPTION_ACCESS_TOKEN, \WC_Facebookcommerce_Integration::OPTION_BUSINESS_MANAGER_ID, \WC_Facebookcommerce_Integration::OPTION_AD_ACCOUNT_ID, \WC_Facebookcommerce_Integration::OPTION_SYSTEM_USER_ID, \WC_Facebookcommerce_Integration::OPTION_FEED_ID, \WC_Facebookcommerce_Integration::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, \WC_Facebookcommerce_Integration::OPTION_COMMERCE_PARTNER_INTEGRATION_ID, \WC_Facebookcommerce_Integration::OPTION_ENABLE_MESSENGER, \WC_Facebookcommerce_Integration::OPTION_HAS_AUTHORIZED_PAGES_READ_ENGAGEMENT, \WC_Facebookcommerce_Integration::OPTION_HAS_CONNECTED_FBE_2, \WC_Facebookcommerce_Integration::OPTION_INSTALLED_FEATURES, \WC_Facebookcommerce_Integration::OPTION_MERCHANT_ACCESS_TOKEN, \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID, \WC_Facebookcommerce_Integration::OPTION_PROFILES, \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, ]; foreach ( $options as $option ) { delete_option( $option ); } // Clear facebook_config option to stop pixel tracking and prevent stale data if ( class_exists( 'WC_Facebookcommerce_Pixel' ) ) { delete_option( \WC_Facebookcommerce_Pixel::SETTINGS_KEY ); } } /** * Triggers products sync if catalog id is being set to a different value. * Triggers metadata feed uploads if CPI id is being set to a different value. * * @since 3.5.0 * * @param bool $should_trigger_products_and_sets_sync * @param bool $should_trigger_metadata_feed_uploads * @param array $params * @return void */ private function maybe_trigger_feed_uploads( bool $should_trigger_products_and_sets_sync, bool $should_trigger_metadata_feed_uploads, array $params ) { try { if ( $should_trigger_products_and_sets_sync ) { // Allow opt-out of full batch-API sync, for example if store has a large number of products. if ( facebook_for_woocommerce()->get_integration()->allow_full_batch_api_sync() ) { facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_all_products(); } else { Logger::log( 'Initial full product sync disabled by filter hook `facebook_for_woocommerce_allow_full_batch_api_sync`', [ 'flow_name' => 'product_sync', 'flow_step' => 'initial_sync', ], array( 'should_send_log_to_meta' => true, 'should_save_log_in_woocommerce' => true, 'woocommerce_log_level' => \WC_Log_Levels::DEBUG, ) ); } } } catch ( \Exception $exception ) { Logger::log( 'Product feed upload failed.', array( 'event' => 'product_sync', 'event_type' => 'sync_products_after_settings_update', 'extra_data' => [ 'params' => wp_json_encode( $params ), ], ), array( 'should_send_log_to_meta' => true, 'should_save_log_in_woocommerce' => false, 'woocommerce_log_level' => \WC_Log_Levels::DEBUG, ), $exception, ); } try { if ( $should_trigger_products_and_sets_sync ) { facebook_for_woocommerce()->get_product_sets_sync_handler()->sync_all_product_sets(); } } catch ( \Exception $exception ) { Logger::log( 'Product sets sync failed.', array( 'event' => 'product_sets_sync', 'event_type' => 'sync_product_sets_after_settings_update', 'extra_data' => [ 'params' => wp_json_encode( $params ), ], ), array( 'should_send_log_to_meta' => true, 'should_save_log_in_woocommerce' => false, 'woocommerce_log_level' => \WC_Log_Levels::DEBUG, ), $exception, ); } try { if ( $should_trigger_metadata_feed_uploads ) { facebook_for_woocommerce()->feed_manager->run_all_feed_uploads(); } } catch ( \Exception $exception ) { Logger::log( 'Products metadata feed upload failed.', array( 'event' => 'feed_upload', 'event_type' => 'trigger_feed_uploads_after_settings_update', 'extra_data' => [ 'params' => wp_json_encode( $params ), ], ), array( 'should_send_log_to_meta' => true, 'should_save_log_in_woocommerce' => false, 'woocommerce_log_level' => \WC_Log_Levels::DEBUG, ), $exception, ); } } }