Facebook for WooCommerce
';
const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress';
const FB_SYNC_REMAINING = 'fb_sync_remaining';
const FB_SYNC_TIMEOUT = 30;
const FB_PRIORITY_MID = 9;
private $test_mode = false;
public function init_pixel() {
WC_Facebookcommerce_Pixel::initialize();
// Migrate WC customer pixel_id from WC settings to WP options.
// This is part of a larger effort to consolidate all the FB-specific
// settings for all plugin integrations.
if ( is_admin() ) {
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
$settings_pixel_id = $this->get_facebook_pixel_id();
if (
WC_Facebookcommerce_Utils::is_valid_id( $settings_pixel_id ) &&
( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) ||
$pixel_id != $settings_pixel_id
)
) {
WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
}
// migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
// so that it works the same way the pixel ID does
$settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
$settings_use_s2s = $this->is_use_s2s_enabled();
WC_Facebookcommerce_Pixel::set_use_s2s( $settings_use_s2s );
$settings_access_token = $this->get_access_token();
WC_Facebookcommerce_Pixel::set_access_token($settings_access_token);
}
}
/**
* Init and hook in the integration.
*
* @access public
* @return void
*/
public function __construct() {
if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
include_once 'facebook-commerce-events-tracker.php';
}
$this->id = WC_Facebookcommerce::INTEGRATION_ID;
$this->method_title = __(
'Facebook for WooCommerce',
'facebook-for-commerce'
);
$this->method_description = __(
'Facebook Commerce and Dynamic Ads (Pixel) Extension',
'facebook-for-commerce'
);
// Load the settings.
$this->init_settings();
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
// if there is a pixel option saved and no integration setting saved, inherit the pixel option
if ( $pixel_id && ! $this->get_facebook_pixel_id() ) {
$this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_id;
}
$advanced_matching_enabled = WC_Facebookcommerce_Pixel::get_use_pii_key();
// if Advanced Matching (use_pii) is enabled on the saved pixel option and not on the saved integration setting,
// inherit the pixel option
if ( $advanced_matching_enabled && ! $this->is_advanced_matching_enabled() ) {
$this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = $advanced_matching_enabled;
}
//For now, the values of use s2s and access token will be the ones returned from WC_Facebookcommerce_Pixel
$use_s2s = WC_Facebookcommerce_Pixel::get_use_s2s();
$this->settings[self::SETTING_USE_S2S] = $use_s2s;
$access_token = WC_Facebookcommerce_Pixel::get_access_token();
$this->settings[self::SETTING_ACCESS_TOKEN] = $access_token;
if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
include_once 'includes/fbutils.php';
}
WC_Facebookcommerce_Utils::$ems = $this->get_external_merchant_settings_id();
if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) {
include_once 'includes/fbgraph.php';
$this->fbgraph = new WC_Facebookcommerce_Graph_API( facebook_for_woocommerce()->get_connection_handler()->get_access_token() );
}
WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
// Hooks
if ( is_admin() ) {
$this->init_pixel();
if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
include_once 'includes/fbutils.php';
}
// 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() ) {
$should_query_tip =
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 ) {
if ( ! class_exists( 'WC_Facebookcommerce_Info_Banner' ) ) {
include_once 'includes/fbinfobanner.php';
}
WC_Facebookcommerce_Info_Banner::get_instance(
$this->get_external_merchant_settings_id(),
$this->fbgraph,
$should_query_tip
);
}
}
if ( ! class_exists( 'WC_Facebook_Integration_Test' ) ) {
include_once 'includes/test/facebook-integration-test.php';
}
$integration_test = WC_Facebook_Integration_Test::get_instance( $this );
$integration_test::$fbgraph = $this->fbgraph;
if ( ! $this->get_pixel_install_time() && $this->get_facebook_pixel_id() ) {
$this->update_pixel_install_time( time() );
}
add_action( 'admin_notices', array( $this, 'checks' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'load_assets' ) );
add_action(
'wp_ajax_ajax_sync_all_fb_products',
array( $this, 'ajax_sync_all_fb_products' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_sync_all_fb_products_using_feed',
array( $this, 'ajax_sync_all_fb_products_using_feed' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_check_feed_upload_status',
array( $this, 'ajax_check_feed_upload_status' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_reset_all_fb_products',
array( $this, 'ajax_reset_all_fb_products' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_display_test_result',
array( $this, 'ajax_display_test_result' )
);
add_action(
'wp_ajax_ajax_schedule_force_resync',
array( $this, 'ajax_schedule_force_resync' ),
self::FB_PRIORITY_MID
);
// don't duplicate product FBID meta
add_filter( 'woocommerce_duplicate_product_exclude_meta', [ $this, 'fb_duplicate_product_reset_meta' ] );
// add product processing hooks if the plugin is configured only
if ( $this->is_configured() && $this->get_product_catalog_id() ) {
// on_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information
add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 40 );
add_action(
'woocommerce_product_quick_edit_save',
array( $this, 'on_quick_and_bulk_edit_save' ),
10, // Action priority
1 // Args passed to on_quick_and_bulk_edit_save ('product')
);
add_action(
'woocommerce_product_bulk_edit_save',
array( $this, 'on_quick_and_bulk_edit_save' ),
10, // Action priority
1 // Args passed to on_quick_and_bulk_edit_save ('product')
);
add_action( 'before_delete_post', [ $this, 'on_product_delete' ] );
add_action( 'add_meta_boxes', array( $this, 'fb_product_metabox' ), 10, 1 );
add_action(
'transition_post_status',
array( $this, 'fb_change_product_published_status' ),
10,
3
);
add_action(
'wp_ajax_ajax_fb_toggle_visibility',
array( $this, 'ajax_fb_toggle_visibility' )
);
add_action(
'wp_ajax_ajax_reset_single_fb_product',
array( $this, 'ajax_reset_single_fb_product' )
);
add_action(
'wp_ajax_ajax_delete_fb_product',
array( $this, 'ajax_delete_fb_product' )
);
add_action(
'pmxi_after_xml_import',
array( $this, 'wp_all_import_compat' )
);
add_action(
'wp_ajax_wpmelon_adv_bulk_edit',
[ $this, 'ajax_woo_adv_bulk_edit_compat' ],
self::FB_PRIORITY_MID
);
// used to remove the 'you need to resync' message
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['remove_sticky'] ) ) {
$this->remove_sticky_message();
}
}
$this->load_background_sync_process();
}
// Must be outside of admin for cron to schedule correctly.
add_action( 'sync_all_fb_products_using_feed', [ $this, 'handle_scheduled_resync_action' ], self::FB_PRIORITY_MID );
// handle the special background feed generation action
add_action( 'wc_facebook_generate_product_catalog_feed', [ $this, 'handle_generate_product_catalog_feed' ] );
if ( $this->get_facebook_pixel_id() ) {
$aam_settings = $this->load_aam_settings_of_pixel();
$user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
$this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info, $aam_settings );
}
// initialize the messenger chat features
$this->messenger_chat = new WC_Facebookcommerce_MessengerChat( [
'fb_page_id' => $this->get_facebook_page_id(),
'facebook_jssdk_version' => $this->get_js_sdk_version(),
] );
// Product Set hooks
add_action( 'fb_wc_product_set_sync', array( $this, 'create_or_update_product_set_item' ), 99, 2 );
add_action( 'fb_wc_product_set_delete', array( $this, 'delete_product_set_item' ), 99 );
}
/**
* Returns the Automatic advanced matching of this pixel
*
* @since 2.0.3
*
* @return AAMSettings
*/
private function load_aam_settings_of_pixel() {
$installed_pixel = $this->get_facebook_pixel_id();
// If no pixel is installed, reading the DB is not needed
if(!$installed_pixel ){
return null;
}
$config_key = 'wc_facebook_aam_settings';
$saved_value = get_transient( $config_key );
$refresh_interval = 20*MINUTE_IN_SECONDS;
$aam_settings = null;
// If wc_facebook_aam_settings is present in the DB
// it is converted into an AAMSettings object
if( $saved_value !== false ){
$cached_aam_settings = new AAMSettings(json_decode($saved_value, true));
// This condition is added because
// it is possible that the AAMSettings saved do not belong to the current
// installed pixel
// because the admin could have changed the connection to Facebook
// during the refresh interval
if($cached_aam_settings->get_pixel_id() == $installed_pixel){
$aam_settings = $cached_aam_settings;
}
}
// If the settings are not present or invalid
// they are fetched from Facebook domain
// and cached in WP database if they are not null
if(!$aam_settings){
$aam_settings = AAMSettings::build_from_pixel_id( $installed_pixel );
if($aam_settings){
set_transient($config_key, strval($aam_settings), $refresh_interval);
}
}
return $aam_settings;
}
public function load_background_sync_process() {
// Attempt to load background processing (Woo 3.x.x only)
include_once 'includes/fbbackground.php';
if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
if ( ! isset( $this->background_processor ) ) {
$this->background_processor =
new WC_Facebookcommerce_Background_Process( $this );
}
}
add_action(
'wp_ajax_ajax_fb_background_check_queue',
array( $this, 'ajax_fb_background_check_queue' )
);
}
public function ajax_fb_background_check_queue() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'background check queue', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$request_time = null;
if ( isset( $_POST['request_time'] ) ) {
$request_time = esc_js( sanitize_text_field( wp_unslash( $_POST['request_time'] ) ) );
}
if ( facebook_for_woocommerce()->get_connection_handler()->get_access_token() ) {
if ( isset( $this->background_processor ) ) {
$is_processing = $this->background_processor->handle_cron_healthcheck();
$remaining = $this->background_processor->get_item_count();
$response = array(
'connected' => true,
'background' => true,
'processing' => $is_processing,
'remaining' => $remaining,
'request_time' => $request_time,
);
} else {
$response = array(
'connected' => true,
'background' => false,
);
}
} else {
$response = array(
'connected' => false,
'background' => false,
);
}
printf( json_encode( $response ) );
wp_die();
}
/**
* Adds a new tab to the Product edit page.
*
* @internal
* @deprecated since 1.10.0
*
* @param array $tabs array of tabs
* @return array
*/
public function fb_new_product_tab( $tabs ) {
wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab()' );
return $tabs;
}
/**
* Adds content to the new Facebook tab on the Product edit page.
*
* @internal
* @deprecated since 1.10.0
*/
public function fb_new_product_tab_content() {
wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab_content()' );
}
/**
* Filters the product columns in the admin edit screen.
*
* @internal
* @deprecated since 1.10.0
*
* @param array $existing_columns array of columns and labels
* @return array
*/
public function fb_product_columns( $existing_columns ) {
wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_column()' );
return $existing_columns;
}
/**
* Outputs content for the FB Shop column in the edit screen.
*
* @internal
* @deprecated since 1.10.0
*
* @param string $column name of the column to display
*/
public function fb_render_product_columns( $column ) {
wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_columns_content()' );
}
public function fb_product_metabox() {
$ajax_data = array(
'nonce' => wp_create_nonce( 'wc_facebook_metabox_jsx' ),
);
wp_enqueue_script(
'wc_facebook_metabox_jsx',
plugins_url(
'/assets/js/facebook-metabox.min.js',
__FILE__
),
[],
\WC_Facebookcommerce::PLUGIN_VERSION
);
wp_localize_script(
'wc_facebook_metabox_jsx',
'wc_facebook_metabox_jsx',
$ajax_data
);
add_meta_box(
'facebook_metabox', // Meta box ID
'Facebook', // Meta box Title
array( $this, 'fb_product_meta_box_html' ), // Callback
'product', // Screen to which to add the meta box
'side' // Context
);
}
/**
* Renders the content of the product meta box.
*/
public function fb_product_meta_box_html() {
global $post;
$woo_product = new WC_Facebook_Product( $post->ID );
$fb_product_group_id = null;
if ( $woo_product->woo_product instanceof \WC_Product && Products::product_should_be_synced( $woo_product->woo_product ) ) {
$fb_product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $post->ID, $woo_product );
}
?>
get_type() ) ) : ?>
get_variation_product_item_ids( $woo_product, $fb_product_group_id ) ) : ?>
get_children() as $variation_id ) {
if ( $variation = wc_get_product( $variation_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;
$product_item_ids_by_variation_id[ $variation_id ] = null;
}
}
}
// use the Graph API to try to find and store the product item IDs for variations that don't have a value yet
if ( $missing_product_item_ids ) {
$product_item_ids = $this->find_variation_product_item_ids( $product_group_id );
foreach ( $missing_product_item_ids as $retailer_id => $variation ) {
if ( isset( $product_item_ids[ $retailer_id ] ) ) {
$variation->update_meta_data( self::FB_PRODUCT_ITEM_ID, $product_item_ids[ $retailer_id ] );
$variation->save_meta_data();
$product_item_ids_by_variation_id[ $variation->get_id() ] = $product_item_ids[ $retailer_id ];
}
}
}
return $product_item_ids_by_variation_id;
}
/**
* Uses the Graph API to return a list of Product Item IDs indexed by the variation's retailer ID.
*
* @since 2.0.0
*
* @param string $product_group_id product group ID
* @return array
*/
private function find_variation_product_item_ids( $product_group_id ) {
$product_item_ids = [];
try {
$response = facebook_for_woocommerce()->get_api()->get_product_group_products( $product_group_id );
do {
$product_item_ids = array_merge( $product_item_ids, $response->get_ids() );
// get up to two additional pages of results
} while ( $response = facebook_for_woocommerce()->get_api()->next( $response, 2 ) );
} catch ( Framework\SV_WC_API_Exception $e ) {
$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() );
facebook_for_woocommerce()->log( $message );
}
return $product_item_ids;
}
/**
* Gets the total of published products.
*
* @return int
*/
public function get_product_count() {
$args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
];
$products = new WP_Query( $args );
wp_reset_postdata();
return $products->found_posts;
}
/**
* Load DIA specific JS Data
*/
public function load_assets() {
$ajax_data = array(
'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
);
// load banner assets
wp_enqueue_script(
'wc_facebook_infobanner_jsx',
plugins_url(
'/assets/js/facebook-infobanner.min.js',
__FILE__
),
[],
\WC_Facebookcommerce::PLUGIN_VERSION
);
wp_localize_script(
'wc_facebook_infobanner_jsx',
'wc_facebook_infobanner_jsx',
$ajax_data
);
wp_enqueue_style(
'wc_facebook_infobanner_css',
plugins_url(
'/assets/css/facebook-infobanner.css',
__FILE__
),
[],
\WC_Facebookcommerce::PLUGIN_VERSION
);
if ( ! facebook_for_woocommerce()->is_plugin_settings() ) {
return;
}
?>
wp_create_nonce( 'wc_facebook_settings_jsx' ),
];
wp_localize_script(
'wc_facebook_settings_jsx',
'wc_facebook_settings_jsx',
$ajax_data
);
wp_enqueue_style(
'wc_facebook_css',
plugins_url(
'/assets/css/facebook.css',
__FILE__
),
[],
\WC_Facebookcommerce::PLUGIN_VERSION
);
}
/**
* Gets the IDs of products marked for deletion from Facebook when removed from Sync.
*
* @internal
*
* @since 2.3.0
*
* @return array
*/
private function get_removed_from_sync_products_to_delete() {
$posted_products = Framework\SV_WC_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
*
* @since 1.10.0
*
* @param int $wp_id post ID
*/
public function on_product_save( $wp_id ) {
$product = wc_get_product( $wp_id );
if ( ! $product ) {
return;
}
$sync_mode = isset( $_POST['wc_facebook_sync_mode'] ) ? $_POST['wc_facebook_sync_mode'] : Admin::SYNC_MODE_SYNC_DISABLED;
$sync_enabled = Admin::SYNC_MODE_SYNC_DISABLED !== $sync_mode;
if ( Admin::SYNC_MODE_SYNC_AND_SHOW === $sync_mode && $product->is_virtual() ) {
// force to Sync and hide
$sync_mode = Admin::SYNC_MODE_SYNC_AND_HIDE;
}
$products_to_delete_from_facebook = $this->get_removed_from_sync_products_to_delete();
if ( $product->is_type( 'variable' ) ) {
// check variations for deletion
foreach ( $products_to_delete_from_facebook as $delete_product_id ) {
$delete_product = wc_get_product( $delete_product_id );
if ( empty( $delete_product ) ) {
continue;
}
if ( Products::is_sync_enabled_for_product( $delete_product ) ) {
continue;
}
$this->delete_fb_product( $delete_product );
}
} else {
if ( $sync_enabled ) {
Products::enable_sync_for_products( [ $product ] );
Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
$this->save_product_settings( $product );
} else {
// if previously enabled, add a notice on the next page load
if ( Products::is_sync_enabled_for_product( $product ) ) {
Admin::add_product_disabled_sync_notice();
}
Products::disable_sync_for_products( [ $product ] );
if ( in_array( $wp_id, $products_to_delete_from_facebook, true ) ) {
$this->delete_fb_product( $product );
}
}
}
if ( $sync_enabled ) {
Admin\Products::save_commerce_fields( $product );
switch ( $product->get_type() ) {
case 'simple':
case 'booking':
case 'external':
case 'composite':
$this->on_simple_product_publish( $wp_id );
break;
case 'variable':
$this->on_variable_product_publish( $wp_id );
break;
case 'subscription':
case 'variable-subscription':
case 'bundle':
$this->on_product_publish( $wp_id );
break;
}
}
}
/**
* Saves the submitted Facebook settings for a product.
*
* @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() );
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ) ) {
$woo_product->set_description( sanitize_text_field( wp_unslash( $_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 ] ) ) );
}
if ( isset( $_POST[ 'fb_product_image_source' ] ) ) {
$product->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, sanitize_key( wp_unslash( $_POST[ 'fb_product_image_source' ] ) ) );
$product->save_meta_data();
}
if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) {
$woo_product->set_product_image( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
/**
* Deletes a product from Facebook.
*
* @param int $product_id product ID
*/
public function on_product_delete( $product_id ) {
$product = wc_get_product( $product_id );
// bail if product does not exist
if ( ! $product instanceof \WC_Product ) {
return;
}
/**
* bail if not enabled for sync, except if explicitly deleting from the metabox
* @see ajax_delete_fb_product()
*/
if ( ( ! is_ajax() || ! isset( $_POST['action'] ) || 'ajax_delete_fb_product' !== $_POST['action'] )
&& ! Products::published_product_should_be_synced( $product ) ) {
return;
}
$this->delete_fb_product( $product );
}
/**
* Deletes Facebook product.
*
* @internal
*
* @since 2.3.0
*
* @param \WC_Product $product WooCommerce product object
*/
private function delete_fb_product( $product ) {
$product_id = $product->get_id();
if ( $product->is_type( 'variation' ) ) {
$retailer_id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
// enqueue variation to be deleted in the background
facebook_for_woocommerce()->get_products_sync_handler()->delete_products( [ $retailer_id ] );
} elseif ( $product->is_type( 'variable' ) ) {
$retailer_ids = [];
foreach ( $product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
if ( $variation instanceof \WC_Product ) {
$retailer_ids[] = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation );
}
}
// enqueue variations to be deleted in the background
facebook_for_woocommerce()->get_products_sync_handler()->delete_products( $retailer_ids );
$this->delete_product_group( $product_id );
} else {
$this->delete_product_item( $product_id );
$this->delete_product_group( $product_id );
}
// clear out both item and group IDs
delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
}
/**
* Updates Facebook Visibility upon trashing and restore.
*
* @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 ) {
return;
}
if ( ! $this->should_update_visibility_for_product_status_change( $new_status, $old_status ) ) {
return;
}
$visibility = $new_status === 'publish' ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN;
$product = wc_get_product( $post->ID );
// bail if we couldn't retrieve a valid product object or the product isn't enabled for sync
//
// Note that while moving a variable product to the trash, this method is called for each one of the
// variations before it gets called with the variable product. As a result, Products::product_should_be_synced()
// always returns false for the variable product (since all children are in the trash at that point).
// This causes update_fb_visibility() to be called on simple products and product variations only.
if ( ! $product instanceof \WC_Product || ! Products::published_product_should_be_synced( $product ) ) {
return;
}
$this->update_fb_visibility( $product, $visibility );
}
/**
* Determines whether the product visibility needs to be updated for the given status change.
*
* Change from publish status -> unpublish status (e.g. trash, draft, etc.)
* 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
*/
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' );
}
/**
* Generic function for use with any product publishing.
*
* Will determine product type (simple or variable) and delegate to
* appropriate handler.
*
* @param int $product_id product ID
*/
public function on_product_publish( $product_id ) {
// bail if the plugin is not configured properly
if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
return;
}
$product = wc_get_product( $product_id );
if ( ! $this->product_should_be_synced( $product ) ) {
return;
}
if ( $product->is_type( 'variable' ) ) {
$this->on_variable_product_publish( $product_id );
} else {
$this->on_simple_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.
*/
function delete_on_out_of_stock( $wp_id, $woo_product ) {
if ( Products::product_should_be_deleted( $woo_product ) ) {
$this->delete_product_item( $wp_id );
return true;
}
return false;
}
/**
* Syncs product to Facebook when saving a variable product.
*
* @param int $wp_id product post ID
* @param WC_Facebook_Product|null $woo_product product object
*/
function on_variable_product_publish( $wp_id, $woo_product = null ) {
if ( ! $woo_product instanceof \WC_Facebook_Product ) {
$woo_product = new \WC_Facebook_Product( $wp_id );
}
if ( ! $this->product_should_be_synced( $woo_product->woo_product ) ) {
return;
}
if ( $this->delete_on_out_of_stock( $wp_id, $woo_product->woo_product ) ) {
return;
}
// Check if product group has been published to FB. If not, it's new.
// If yes, loop through variants and see if product items are published.
$fb_product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $wp_id, $woo_product );
if ( $fb_product_group_id ) {
$woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
$this->update_product_group( $woo_product );
} else {
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product->woo_product );
$this->create_product_group( $woo_product, $retailer_id, true );
}
$variation_ids = [];
// scheduled update for each variation that should be synced
foreach ( $woo_product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
if ( $variation instanceof \WC_Product && $this->product_should_be_synced( $variation ) && ! $this->delete_on_out_of_stock( $variation_id, $variation ) ) {
$variation_ids[] = $variation_id;
}
}
facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( $variation_ids );
}
/**
* Syncs product to Facebook when saving a simple product.
*
* @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
*/
function on_simple_product_publish( $wp_id, $woo_product = null, &$parent_product = null ) {
if ( ! $woo_product instanceof \WC_Facebook_Product ) {
$woo_product = new \WC_Facebook_Product( $wp_id, $parent_product );
}
if ( ! $this->product_should_be_synced( $woo_product->woo_product ) ) {
return;
}
if ( $this->delete_on_out_of_stock( $wp_id, $woo_product->woo_product ) ) {
return;
}
// Check if this product has already been published to FB.
// If not, it's new!
$fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $wp_id, $woo_product );
if ( $fb_product_item_id ) {
$woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
$this->update_product_item( $woo_product, $fb_product_item_id );
return $fb_product_item_id;
} else {
// Check if this is a new product item for an existing product group
if ( $woo_product->get_parent_id() ) {
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$woo_product->get_parent_id(),
$woo_product
);
// New variant added
if ( $fb_product_group_id ) {
return $this->create_product_simple( $woo_product, $fb_product_group_id );
} else {
WC_Facebookcommerce_Utils::fblog(
'Wrong! simple_product_publish called without group ID for
a variable product!',
[],
true
);
}
} else {
return $this->create_product_simple( $woo_product ); // new product
}
}
}
/**
* Determines whether the product with the given ID should be synced.
*
* @since 2.0.0
*
* @param \WC_Product|false $product product object
*/
public function product_should_be_synced( $product ) {
$should_be_synced = $this->is_product_sync_enabled();
// can't sync if we don't have a valid product object
if ( $should_be_synced && ! $product instanceof \WC_Product ) {
$should_be_synced = false;
}
// make sure the given product is enabled for sync
if ( $should_be_synced && ! Products::product_should_be_synced( $product ) ) {
$should_be_synced = false;
}
return $should_be_synced;
}
/**
* Create product group and product, store fb-specific info
**/
function create_product_simple( $woo_product, $fb_product_group_id = null ) {
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
if ( ! $fb_product_group_id ) {
$fb_product_group_id = $this->create_product_group(
$woo_product,
$retailer_id
);
}
if ( $fb_product_group_id ) {
$fb_product_item_id = $this->create_product_item(
$woo_product,
$retailer_id,
$fb_product_group_id
);
return $fb_product_item_id;
}
}
function create_product_group( $woo_product, $retailer_id, $variants = false ) {
$product_group_data = array(
'retailer_id' => $retailer_id,
);
if ( $variants ) {
$product_group_data['variants'] =
$woo_product->prepare_variants_for_group();
}
$create_product_group_result = $this->check_api_result(
$this->fbgraph->create_product_group(
$this->get_product_catalog_id(),
$product_group_data
),
$product_group_data,
$woo_product->get_id()
);
// New variant added
if ( $create_product_group_result ) {
$decode_result = WC_Facebookcommerce_Utils::decode_json( $create_product_group_result['body'] );
$fb_product_group_id = $decode_result->id;
update_post_meta(
$woo_product->get_id(),
self::FB_PRODUCT_GROUP_ID,
$fb_product_group_id
);
/** TODO: restore when adopting FBE 2.0
$this->display_success_message(
'Created product group ' .
$fb_product_group_id . ' on Facebook.'
);
*/
return $fb_product_group_id;
}
}
function create_product_item( $woo_product, $retailer_id, $product_group_id ) {
$product_data = $woo_product->prepare_product( $retailer_id );
$product_result = $this->check_api_result(
$this->fbgraph->create_product_item(
$product_group_id,
$product_data
),
$product_data,
$woo_product->get_id()
);
if ( $product_result ) {
$decode_result = WC_Facebookcommerce_Utils::decode_json( $product_result['body'] );
$fb_product_item_id = $decode_result->id;
update_post_meta(
$woo_product->get_id(),
self::FB_PRODUCT_ITEM_ID,
$fb_product_item_id
);
/** TODO: restore when adopting FBE 2.0
$this->display_success_message(
'Created product item ' .
$fb_product_item_id . ' on Facebook.'
);
*/
return $fb_product_item_id;
}
}
/**
* Update existing product group (variant data only)
*
* @param \WC_Facebook_Product $woo_product
**/
function update_product_group( $woo_product ) {
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$woo_product->get_id(),
$woo_product
);
if ( ! $fb_product_group_id ) {
return;
}
$variants = $woo_product->prepare_variants_for_group();
if ( ! $variants ) {
WC_Facebookcommerce_Utils::log(
sprintf(
__(
'Nothing to update for product group for %1$s',
'facebook-for-woocommerce'
),
$fb_product_group_id
)
);
return;
}
// figure out the matching default variation
$default_product_fbid = null;
$woo_default_variation = $this->get_product_group_default_variation( $woo_product );
if ( $woo_default_variation ) {
$default_product_fbid = $this->get_product_fbid(
self::FB_PRODUCT_ITEM_ID,
$woo_default_variation['variation_id']
);
}
$product_group_data = [
'variants' => $variants,
];
if ( $default_product_fbid ) {
$product_group_data['default_product_id'] = $default_product_fbid;
}
$result = $this->check_api_result(
$this->fbgraph->update_product_group(
$fb_product_group_id,
$product_group_data
)
);
/** TODO: restore when adopting FBE 2.0
if ( $result ) {
$this->display_success_message(
'Updated product group ' . $fb_product_group_id .
' on Facebook.'
);
}
*/
}
/**
* Determines if there is a matching variation for the default attributes.
*
* @since 2.1.2
*
* @param \WC_Facebook_Product $woo_product
* @return array|null
*/
private function get_product_group_default_variation( $woo_product ) {
$default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' );
if ( empty( $default_attributes ) ) {
return null;
}
$default_variation = null;
$product_variations = $woo_product->woo_product->get_available_variations();
foreach ( $product_variations as $variation ) {
$variation_attributes = $this->get_product_variation_attributes( $variation );
$matching_attributes = array_intersect_assoc( $default_attributes, $variation_attributes );
if ( count( $matching_attributes ) === count( $variation_attributes ) ) {
$default_variation = $variation;
break;
}
}
return $default_variation;
}
/**
* Parses given product variation for it's attributes
*
* @since 2.1.2
*
* @param array $variation
* @return array
*/
private function get_product_variation_attributes( $variation ) {
$final_attributes = [];
$variation_attributes = $variation['attributes'];
foreach ( $variation_attributes as $attribute_name => $attribute_value ) {
$final_attributes[ str_replace( 'attribute_', '', $attribute_name ) ] = $attribute_value;
}
return $final_attributes;
}
/**
* Update existing product
**/
function update_product_item( $woo_product, $fb_product_item_id ) {
$product_data = $woo_product->prepare_product();
// send an empty string to clear the additional_image_urls property if the product has no additional images
if ( empty( $product_data['additional_image_urls'] ) ) {
$product_data['additional_image_urls'] = '';
}
$result = $this->check_api_result(
$this->fbgraph->update_product_item(
$fb_product_item_id,
$product_data
)
);
/** TODO: restore when adopting FBE 2.0
if ( $result ) {
$this->display_success_message(
'Updated product ' . $fb_product_item_id . ' on Facebook.'
);
}
*/
}
/**
* 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.
**/
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 );
// set data and execute API call
$method = empty( $fb_product_set_id ) ? 'create' : 'update';
$id = empty( $fb_product_set_id ) ? $this->get_product_catalog_id() : $fb_product_set_id;
$result = $this->check_api_result(
call_user_func_array(
array(
$this->fbgraph,
$method . '_product_set_item',
),
array(
$id,
$product_set_data,
)
)
);
// update product set to set FB Product Set ID
if ( $result && empty( $fb_product_set_id ) ) {
// decode and get ID from result body
$decode_result = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
$fb_product_set_id = $decode_result->id;
update_term_meta(
$product_set_id,
self::FB_PRODUCT_SET_ID,
$fb_product_set_id
);
}
}
/**
* Delete product set
*
* @since 2.3.0
*
* @param int $fb_product_set_id Facebook Product Set ID.
**/
public function delete_product_set_item( $fb_product_set_id ) {
$this->check_api_result( $this->fbgraph->delete_product_set_item( $fb_product_set_id ) );
}
/**
* Saves settings via AJAX (to preserve window context for onboarding).
*
* @internal
*
* @deprecated 2.0.0
*/
public function ajax_save_fb_settings() {
wc_deprecated_function( __METHOD__, '2.0.0' );
}
/**
* Delete all settings via AJAX
*
* @deprecated 2.0.0
*/
function ajax_delete_fb_settings() {
wc_deprecated_function( __METHOD__, '2.0.0' );
}
/**
* Checks the feed upload status (FBE v1.0).
*
* @internal
*/
public function ajax_check_feed_upload_status() {
$response = array(
'connected' => true,
'status' => 'complete',
);
printf( json_encode( $response ) );
wp_die();
}
/**
* Check Feed Upload Status (FBE v2.0)
* TODO: When migrating to FBE v2.0, remove above function and rename
* below function to ajax_check_feed_upload_status()
**/
public function ajax_check_feed_upload_status_v2() {
\WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
if ( $this->is_configured() ) {
$response = [
'connected' => true,
'status' => 'in progress',
];
if ( ! empty( $this->get_upload_id() ) ) {
if ( ! isset( $this->fbproductfeed ) ) {
if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
include_once 'includes/fbproductfeed.php';
}
$this->fbproductfeed = new \WC_Facebook_Product_Feed(
$this->get_product_catalog_id(),
$this->fbgraph
);
}
$status = $this->fbproductfeed->is_upload_complete( $this->settings );
$response['status'] = $status;
} else {
$response = [
'connected' => true,
'status' => 'error',
];
}
if ( 'complete' === $response['status'] ) {
update_option(
$this->get_option_key(),
apply_filters(
'woocommerce_settings_api_sanitized_fields_' . $this->id,
$this->settings
)
);
}
} else {
$response = [ 'connected' => false ];
}
printf( json_encode( $response ) );
wp_die();
}
/**
* Display custom success message (sugar)
**/
function display_success_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_success',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom warning message (sugar)
**/
function display_warning_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_warning',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom info message (sugar)
**/
function display_info_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_info',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom "sticky" info message.
* Call remove_sticky_message or wait for time out.
**/
function display_sticky_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_sticky',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Remove custom "sticky" info message
**/
function remove_sticky_message() {
delete_transient( 'facebook_plugin_api_sticky' );
}
function remove_resync_message() {
$msg = get_transient( 'facebook_plugin_api_sticky' );
if ( $msg && strpos( $msg, 'Sync' ) !== false ) {
delete_transient( 'facebook_plugin_resync_sticky' );
}
}
/**
* Logs and stores custom error message (sugar).
*
* @param string $msg
*/
function display_error_message( $msg ) {
WC_Facebookcommerce_Utils::log( $msg );
set_transient( 'facebook_plugin_api_error', $msg, self::FB_MESSAGE_DISPLAY_TIME );
}
/**
* Displays error message from API result (sugar).
*
* @param array $result
*/
function display_error_message_from_result( $result ) {
$error = json_decode( $result['body'] )->error;
$msg = ( 'Fatal' === $error->message && ! empty( $error->error_user_title ) ) ? $error->error_user_title : $error_message;
$this->display_error_message( $msg );
}
/**
* Deals with FB API responses, displays error if FB API returns error.
*
* @param WP_Error|array $result API response
* @param array|null $logdata additional data for logging
* @param int|null $wpid post ID
* @return array|null|void result if response is 200, null otherwise
*/
function check_api_result( $result, $logdata = null, $wpid = null ) {
if ( is_wp_error( $result ) ) {
WC_Facebookcommerce_Utils::log( $result->get_error_message() );
$message = sprintf(
/* translators: Placeholders %1$s - original error message from Facebook API */
esc_html__( 'There was an issue connecting to the Facebook API: %1$s', 'facebook-for-woocommerce' ),
$result->get_error_message()
);
$this->display_error_message( $message );
return;
}
if ( $result['response']['code'] != '200' ) {
// Catch 10800 fb error code ("Duplicate retailer ID") and capture FBID
// if possible, otherwise let user know we found dupe SKUs
$body = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
if ( $body && $body->error->code == '10800' ) {
$error_data = $body->error->error_data; // error_data may contain FBIDs
if ( $error_data && $wpid ) {
$existing_id = $this->get_existing_fbid( $error_data, $wpid );
if ( $existing_id ) {
// Add "existing_id" ID to result
$body->id = $existing_id;
$result['body'] = json_encode( $body );
return $result;
}
}
} else {
$this->display_error_message_from_result( $result );
}
WC_Facebookcommerce_Utils::log( $result );
$data = [
'result' => $result,
'data' => $logdata,
];
WC_Facebookcommerce_Utils::fblog(
'Non-200 error code from FB',
$data,
true
);
return null;
}
return $result;
}
/**
* Displays out of sync message if products are edited using WooCommerce Advanced Bulk Edit.
*
* @param $import_id
*/
function ajax_woo_adv_bulk_edit_compat( $import_id ) {
if ( ! WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'adv bulk edit', false ) ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
if ( strpos( $type, 'product' ) !== false && strpos( $type, 'load' ) === false ) {
$this->display_out_of_sync_message( 'advanced bulk edit' );
}
}
function wp_all_import_compat( $import_id ) {
$import = new PMXI_Import_Record();
$import->getById( $import_id );
if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], array( 'product', 'product_variation' ) ) ) {
$this->display_out_of_sync_message( 'import' );
}
}
function display_out_of_sync_message( $action_name ) {
$this->display_sticky_message(
sprintf(
'Products may be out of Sync with Facebook due to your recent ' . $action_name . '.' .
' Re-Sync them with FB.',
facebook_for_woocommerce()->get_settings_url()
)
);
}
/**
* If we get a product group ID or product item ID back for a dupe retailer
* id error, update existing ID.
*
* @return null
**/
function get_existing_fbid( $error_data, $wpid ) {
if ( isset( $error_data->product_group_id ) ) {
update_post_meta(
$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(
$wpid,
self::FB_PRODUCT_ITEM_ID,
(string) $error_data->product_item_id
);
return $error_data->product_item_id;
} else {
return;
}
}
/**
* Checks for API key and other API errors.
*/
public function checks() {
// TODO improve this by checking the settings page with Framework method and ensure error notices are displayed under the Integration sections {FN 2020-01-30}
if ( isset( $_GET['page'] ) && 'wc-facebook' === $_GET['page'] ) {
$this->display_errors();
}
$this->maybe_display_facebook_api_messages();
}
/**
* Gets a sample feed with up to 12 published products.
*
* @return string
*/
function get_sample_product_feed() {
ob_start();
// get up to 12 published posts that are products
$args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 12,
'fields' => 'ids',
];
$post_ids = get_posts( $args );
$items = [];
foreach ( $post_ids as $post_id ) {
$woo_product = new WC_Facebook_Product( $post_id );
$product_data = $woo_product->prepare_product();
$feed_item = [
'title' => strip_tags( $product_data['name'] ),
'availability' => $woo_product->is_in_stock() ? 'in stock' :
'out of stock',
'description' => strip_tags( $product_data['description'] ),
'id' => $product_data['retailer_id'],
'image_link' => $product_data['image_url'],
'brand' => Framework\SV_WC_Helper::str_truncate( wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() ), 100 ),
'link' => $product_data['url'],
'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
];
array_push( $items, $feed_item );
}
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
wp_reset_postdata();
ob_end_clean();
return json_encode( [ $items ] );
}
/**
* Loop through array of WPIDs to remove metadata.
**/
function delete_post_meta_loop( $products ) {
foreach ( $products as $product_id ) {
delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
delete_post_meta( $product_id, Products::VISIBILITY_META_KEY );
}
}
/**
* Remove FBIDs from all products when resetting store.
**/
function reset_all_products() {
if ( ! is_admin() ) {
WC_Facebookcommerce_Utils::log(
'Not resetting any FBIDs from products,
must call reset from admin context.'
);
return false;
}
$test_instance = WC_Facebook_Integration_Test::get_instance( $this );
$this->test_mode = $test_instance::$test_mode;
// Include draft products (omit 'post_status' => 'publish')
WC_Facebookcommerce_Utils::log( 'Removing FBIDs from all products' );
$post_ids = get_posts(
array(
'post_type' => 'product',
'posts_per_page' => -1,
'fields' => 'ids',
)
);
$children = array();
foreach ( $post_ids as $post_id ) {
$children = array_merge(
get_posts(
array(
'post_type' => 'product_variation',
'posts_per_page' => -1,
'post_parent' => $post_id,
'fields' => 'ids',
)
),
$children
);
}
$post_ids = array_merge( $post_ids, $children );
$this->delete_post_meta_loop( $post_ids );
WC_Facebookcommerce_Utils::log( 'Product FBIDs deleted' );
return true;
}
/**
* Remove FBIDs from a single WC product
**/
function reset_single_product( $wp_id ) {
$woo_product = new WC_Facebook_Product( $wp_id );
$products = array( $woo_product->get_id() );
if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
$products = array_merge( $products, $woo_product->get_children() );
}
$this->delete_post_meta_loop( $products );
WC_Facebookcommerce_Utils::log( 'Deleted FB Metadata for product ' . $wp_id );
}
function ajax_reset_all_fb_products() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset products', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$this->reset_all_products();
wp_reset_postdata();
wp_die();
}
function ajax_reset_single_fb_product() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset single product', true );
check_ajax_referer( 'wc_facebook_metabox_jsx' );
if ( ! isset( $_POST['wp_id'] ) ) {
wp_die();
}
$wp_id = sanitize_text_field( wp_unslash( $_POST['wp_id'] ) );
$woo_product = new WC_Facebook_Product( $wp_id );
if ( $woo_product ) {
$this->reset_single_product( $wp_id );
}
wp_reset_postdata();
wp_die();
}
function ajax_delete_fb_product() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'delete single product', true );
check_ajax_referer( 'wc_facebook_metabox_jsx' );
if ( ! isset( $_POST['wp_id'] ) ) {
wp_die();
}
$wp_id = sanitize_text_field( wp_unslash( $_POST['wp_id'] ) );
$this->on_product_delete( $wp_id );
$this->reset_single_product( $wp_id );
wp_reset_postdata();
wp_die();
}
/**
* Special function to run all visible products through on_product_publish
*
* @internal
*/
public function ajax_sync_all_fb_products() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$this->sync_facebook_products( 'background' );
}
/**
* Syncs Facebook products using the GraphAPI.
*
* It can either use a Feed upload or update each product individually based on the selecetd method.
* Ends the request sending a JSON response indicating success or failure.
*
* @since 1.10.2
*
* @param string $method either 'feed' or 'background'
*/
private function sync_facebook_products( $method ) {
try {
if ( 'feed' === $method ) {
$this->sync_facebook_products_using_feed();
} elseif ( 'background' === $method ) {
// if syncs starts, the background processor will continue executing until the request ends and no response will be sent back to the browser
$this->sync_facebook_products_using_background_processor();
}
wp_send_json_success();
} catch ( Framework\SV_WC_Plugin_Exception $e ) {
// Access token has expired
if ( 190 === $e->getCode() ) {
$error_message = __( 'Your connection has expired.', 'facebook-for-woocommerce' ) . ' ' . __( 'Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook.', 'facebook-for-woocommerce' ) . '';
} else {
$error_message = $e->getMessage();
}
$message = sprintf(
/* translators: Placeholders %s - error message */
__( 'There was an error trying to sync the products to Facebook. %s', 'facebook-for-woocommerce' ),
$error_message
);
wp_send_json_error( [ 'error' => $message ] );
}
}
/**
* Syncs Facebook products using the background processor.
*
* @since 1.10.2
*
* @throws Framework\SV_WC_Plugin_Exception
* @return bool
*/
private function sync_facebook_products_using_background_processor() {
if ( ! $this->is_product_sync_enabled() ) {
WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
throw new Framework\SV_WC_Plugin_Exception( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
}
if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
WC_Facebookcommerce_Utils::log( sprintf( 'Not syncing, the plugin is not configured or the Catalog ID is missing' ) );
throw new Framework\SV_WC_Plugin_Exception( __( 'The plugin is not configured or the Catalog ID is missing.', 'facebook-for-woocommerce' ) );
}
$this->remove_resync_message();
$currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
if ( isset( $this->background_processor ) ) {
if ( $this->background_processor->is_updating() ) {
$this->background_processor->handle_cron_healthcheck();
$currently_syncing = 1;
}
}
if ( $currently_syncing ) {
WC_Facebookcommerce_Utils::log( 'Not syncing again, sync already in progress' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync during an in-progress sync!',
array(),
true
);
throw new Framework\SV_WC_Plugin_Exception( __( 'A product sync is in progress. Please wait until the sync finishes before starting a new one.', 'facebook-for-woocommerce' ) );
}
if ( ! $this->fbgraph->is_product_catalog_valid( $this->get_product_catalog_id() ) ) {
WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync with an invalid product catalog!',
array(),
true
);
throw new Framework\SV_WC_Plugin_Exception( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
}
// Get all published posts. First unsynced then already-synced.
$post_ids_new = WC_Facebookcommerce_Utils::get_wp_posts(
self::FB_PRODUCT_GROUP_ID,
'NOT EXISTS'
);
$post_ids_old = WC_Facebookcommerce_Utils::get_wp_posts(
self::FB_PRODUCT_GROUP_ID,
'EXISTS'
);
$total_new = count( $post_ids_new );
$total_old = count( $post_ids_old );
$post_ids = array_merge( $post_ids_new, $post_ids_old );
$total = count( $post_ids );
WC_Facebookcommerce_Utils::fblog(
'Attempting to sync ' . $total . ' ( ' .
$total_new . ' new) products with settings: ',
$this->settings,
false
);
// Check for background processing (Woo 3.x.x)
if ( isset( $this->background_processor ) ) {
$starting_message = sprintf(
'Starting background sync to Facebook: %d products...',
$total
);
set_transient(
self::FB_SYNC_IN_PROGRESS,
true,
self::FB_SYNC_TIMEOUT
);
set_transient(
self::FB_SYNC_REMAINING,
(int) $total
);
$this->display_info_message( $starting_message );
WC_Facebookcommerce_Utils::log( $starting_message );
foreach ( $post_ids as $post_id ) {
WC_Facebookcommerce_Utils::log( 'Pushing post to queue: ' . $post_id );
$this->background_processor->push_to_queue( $post_id );
}
$this->background_processor->save()->dispatch();
// reset FB_SYNC_REMAINING to avoid race condition
set_transient(
self::FB_SYNC_REMAINING,
(int) $total
);
// handle_cron_healthcheck must be called
// https://github.com/A5hleyRich/wp-background-processing/issues/34
$this->background_processor->handle_cron_healthcheck();
} else {
// Oldschool sync for WooCommerce 2.x
$count = ( $total_old === $total ) ? 0 : $total_old;
foreach ( $post_ids as $post_id ) {
// Repeatedly overwrite sync total while in actual sync loop
set_transient(
self::FB_SYNC_IN_PROGRESS,
true,
self::FB_SYNC_TIMEOUT
);
$this->display_sticky_message(
sprintf(
'Syncing products to Facebook: %d out of %d...',
// Display different # when resuming to avoid confusion.
min( $count, $total ),
$total
),
true
);
$this->on_product_publish( $post_id );
$count++;
}
WC_Facebookcommerce_Utils::log( 'Synced ' . $count . ' products' );
$this->remove_sticky_message();
$this->display_info_message( 'Facebook product sync complete!' );
delete_transient( self::FB_SYNC_IN_PROGRESS );
WC_Facebookcommerce_Utils::fblog(
'Product sync complete. Total products synced: ' . $count
);
}
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
wp_reset_postdata();
return true;
}
/**
* Special function to run all visible products by uploading feed.
*
* @internal
*/
public function ajax_sync_all_fb_products_using_feed() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
'syncall products using feed',
! $this->test_mode
);
check_ajax_referer( 'wc_facebook_settings_jsx' );
$this->sync_facebook_products( 'feed' );
}
/**
* Syncs Facebook products using a Feed.
*
* @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
*
* @since 1.10.2
*
* @throws Framework\SV_WC_Plugin_Exception
* @return bool
*/
public function sync_facebook_products_using_feed() {
if ( ! $this->is_product_sync_enabled() ) {
WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
throw new Framework\SV_WC_Plugin_Exception( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
}
if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
WC_Facebookcommerce_Utils::log( sprintf( 'Not syncing, the plugin is not configured or the Catalog ID is missing' ) );
throw new Framework\SV_WC_Plugin_Exception( __( 'The plugin is not configured or the Catalog ID is missing.', 'facebook-for-woocommerce' ) );
}
$this->remove_resync_message();
if ( ! $this->fbgraph->is_product_catalog_valid( $this->get_product_catalog_id() ) ) {
WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync with an invalid product catalog!',
array(),
true
);
throw new Framework\SV_WC_Plugin_Exception( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
}
if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
include_once 'includes/fbproductfeed.php';
}
if ( $this->test_mode ) {
$this->fbproductfeed = new WC_Facebook_Product_Feed_Test_Mock(
$this->get_product_catalog_id(),
$this->fbgraph,
$this->get_feed_id()
);
} else {
$this->fbproductfeed = new WC_Facebook_Product_Feed(
$this->get_product_catalog_id(),
$this->fbgraph,
$this->get_feed_id()
);
}
if ( ! $this->fbproductfeed->sync_all_products_using_feed() ) {
WC_Facebookcommerce_Utils::fblog( 'Sync all products using feed, curl failed', [], true );
throw new Framework\SV_WC_Plugin_Exception( __( "We couldn't create the feed or upload the product information.", 'facebook-for-woocommerce' ) );
}
$this->update_feed_id( $this->fbproductfeed->feed_id );
$this->update_upload_id( $this->fbproductfeed->upload_id );
update_option(
$this->get_option_key(),
apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings )
);
wp_reset_postdata();
return true;
}
/**
* Syncs Facebook products using a Feed.
*
* TODO: deprecate this methid in 1.11.0 or newer {WV 2020-03-12}
*
* @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
*
* @return bool
*/
public function sync_all_fb_products_using_feed() {
try {
$sync_started = $this->sync_facebook_products_using_feed();
} catch ( Framework\SV_WC_Plugin_Exception $e ) {
$sync_started = false;
}
return $sync_started;
}
/**
* Toggles product visibility via AJAX.
*
* @internal
* @deprecated since 1.10.0
**/
public function ajax_fb_toggle_visibility() {
wc_deprecated_function( __METHOD__, '1.10.0' );
}
/**
* Initializes the settings form fields.
*
* @since 1.0.0
* @deprecated 2.0.0
*
* @internal
*/
public function init_form_fields() {
wc_deprecated_function( __METHOD__, '2.0.0' );
}
/**
* Processes and saves options.
*
* TODO: remove this or move it to the new settings processing
*
* @internal
*
* @since 1.10.0
* @deprecated 2.0.0
*/
public function process_admin_options() {
wc_deprecated_function( __METHOD__, '2.0.0' );
}
/** Getter methods ************************************************************************************************/
/**
* Gets the page access token.
*
* TODO: remove this method by version 3.0.0 or by 2021-08-21 {WV 2020-08-21}
*
* @since 1.10.0
* @deprecated 2.1.0
*
* @return string
*/
public function get_page_access_token() {
wc_deprecated_function( __METHOD__, '2.1.0', Connection::class . '::get_page_access_token()' );
$access_token = facebook_for_woocommerce()->get_connection_handler()->get_page_access_token();
/**
* Filters the Facebook page access token.
*
* @since 1.10.0
* @deprecated 2.1.0
*
* @param string $page_access_token Facebook page access token
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_page_access_token', ! $this->is_feed_migrated() ? $access_token : '', $this );
}
/**
* Gets the product catalog ID.
*
* @since 1.10.0
*
* @return string
*/
public function get_product_catalog_id() {
if ( ! is_string( $this->product_catalog_id ) ) {
$value = get_option( self::OPTION_PRODUCT_CATALOG_ID, '' );
$this->product_catalog_id = is_string( $value ) ? $value : '';
}
/**
* 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
*/
return (string) apply_filters( 'wc_facebook_product_catalog_id', $this->product_catalog_id, $this );
}
/**
* Gets the external merchant settings ID.
*
* @since 1.10.0
*
* @return string
*/
public function get_external_merchant_settings_id() {
if ( ! is_string( $this->external_merchant_settings_id ) ) {
$value = get_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, '' );
$this->external_merchant_settings_id = is_string( $value ) ? $value : '';
}
/**
* 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
*/
return (string) apply_filters( 'wc_facebook_external_merchant_settings_id', $this->external_merchant_settings_id, $this );
}
/**
* Gets the feed ID.
*
* @since 1.10.0
*
* @return string
*/
public function get_feed_id() {
if ( ! is_string( $this->feed_id ) ) {
$value = get_option( self::OPTION_FEED_ID, '' );
$this->feed_id = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook feed ID.
*
* @since 1.10.0
*
* @param string $feed_id Facebook feed ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_feed_id', $this->feed_id, $this );
}
/***
* Gets the Facebook Upload ID.
*
* @since 1.11.0
*
* @return string
*/
public function get_upload_id() {
if ( ! is_string( $this->upload_id ) ) {
$value = get_option( self::OPTION_UPLOAD_ID, '' );
$this->upload_id = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook upload ID.
*
* @since 1.11.0
*
* @param string $upload_id Facebook upload ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_upload_id', $this->upload_id, $this );
}
/**
* Gets the Facebook pixel install time in UTC seconds.
*
* @since 1.10.0
*
* @return int
*/
public function get_pixel_install_time() {
if ( ! (int) $this->pixel_install_time ) {
$value = (int) get_option( self::OPTION_PIXEL_INSTALL_TIME, 0 );
$this->pixel_install_time = $value ?: null;
}
/**
* 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
*/
return (int) apply_filters( 'wc_facebook_pixel_install_time', $this->pixel_install_time, $this );
}
/**
* Gets the configured JS SDK version.
*
* @since 1.10.0
*
* @return string
*/
public function get_js_sdk_version() {
if ( ! is_string( $this->js_sdk_version ) ) {
$value = get_option( self::OPTION_JS_SDK_VERSION, '' );
$this->js_sdk_version = is_string( $value ) ? $value : '';
}
/**
* 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
*/
return (string) apply_filters( 'wc_facebook_js_sdk_version', $this->js_sdk_version, $this );
}
/**
* Gets the configured Facebook page ID.
*
* @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
*/
return (string) apply_filters( 'wc_facebook_page_id', get_option( self::SETTING_FACEBOOK_PAGE_ID, '' ), $this );
}
/**
* Gets the configured Facebook pixel ID.
*
* @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
*/
return (string) apply_filters( 'wc_facebook_pixel_id', get_option( self::SETTING_FACEBOOK_PIXEL_ID, '' ), $this );
}
/**
* Gets the configured use s2s flag.
*
* @return bool
*/
public function is_use_s2s_enabled() {
return WC_Facebookcommerce_Pixel::get_use_s2s();
}
/**
* Gets the configured access token
*
* @return string
*/
public function get_access_token() {
return WC_Facebookcommerce_Pixel::get_access_token();
}
/**
* Gets the IDs of the categories to be excluded from sync.
*
* @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
*/
return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, [] ), $this );
}
/**
* Gets the IDs of the tags to be excluded from sync.
*
* @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
*/
return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, [] ), $this );
}
/**
* Gets the configured product description mode.
*
* @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
*/
$mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
$valid_modes = [
self::PRODUCT_DESCRIPTION_MODE_STANDARD,
self::PRODUCT_DESCRIPTION_MODE_SHORT,
];
if ( ! in_array( $mode, $valid_modes, true ) ) {
$mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
}
return $mode;
}
/**
* Gets the configured scheduled re-sync offset in seconds.
*
* Returns null if no offset is configured.
*
* @since 1.10.0
* @deprecated 2.0.0
*
* @return int|null
*/
public function get_scheduled_resync_offset() {
wc_deprecated_function( __METHOD__, '2.0.0' );
}
/**
* Gets the configured Facebook messenger locale.
*
* @since 1.10.0
*
* @return string
*/
public function get_messenger_locale() {
/**
* Filters the configured Facebook messenger locale.
*
* @since 1.10.0
*
* @param string $locale the configured Facebook messenger locale
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_messenger_locale', get_option( self::SETTING_MESSENGER_LOCALE, 'en_US' ), $this );
}
/**
* Gets the configured Facebook messenger greeting.
*
* @since 1.10.0
*
* @return string
*/
public function get_messenger_greeting() {
/**
* Filters the configured Facebook messenger greeting.
*
* @since 1.10.0
*
* @param string $greeting the configured Facebook messenger greeting
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$greeting = (string) apply_filters( 'wc_facebook_messenger_greeting', get_option( self::SETTING_MESSENGER_GREETING, __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' ) ), $this );
return Framework\SV_WC_Helper::str_truncate( $greeting, $this->get_messenger_greeting_max_characters(), '' );
}
/**
* Gets the maximum number of characters allowed in the messenger greeting.
*
* @since 1.10.0
*
* @return int
*/
public function get_messenger_greeting_max_characters() {
$default = 80;
/**
* Filters the maximum number of characters allowed in the messenger greeting.
*
* @since 1.10.0
*
* @param int $max the maximum number of characters allowed in the messenger greeting
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$max = (int) apply_filters( 'wc_facebook_messenger_greeting_max_characters', $default, $this );
return $max < 1 ? $default : $max;
}
/**
* Gets the configured Facebook messenger color hex.
*
* This is used to style the messenger UI.
*
* @since 1.10.0
*
* @return string
*/
public function get_messenger_color_hex() {
/**
* Filters the configured Facebook messenger color hex.
*
* @since 1.10.0
*
* @param string $hex the configured Facebook messenger color hex
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_messenger_color_hex', get_option( self::SETTING_MESSENGER_COLOR_HEX, '#0084ff' ), $this );
}
/** Setter methods ************************************************************************************************/
/**
* Updates the Facebook page access token.
*
* TODO: remove this method by version 3.0.0 or by 2021-08-21 {WV 2020-08-21}
*
* @since 1.10.0
* @deprecated 2.1.0
*
* @param string $value page access token value
*/
public function update_page_access_token( $value ) {
wc_deprecated_function( __METHOD__, '2.1.0', Connection::class . '::update_page_access_token()' );
facebook_for_woocommerce()->get_connection_handler()->update_page_access_token( $value );
}
/**
* Updates the Facebook product catalog ID.
*
* @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 );
update_option( self::OPTION_PRODUCT_CATALOG_ID, $this->product_catalog_id );
}
/**
* Updates the Facebook external merchant settings ID.
*
* @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 );
update_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, $this->external_merchant_settings_id );
}
/**
* Updates the Facebook feed ID.
*
* @since 1.10.0
*
* @param string $value feed ID value
*/
public function update_feed_id( $value ) {
$this->feed_id = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_FEED_ID, $this->feed_id );
}
/**
* Updates the Facebook upload ID.
*
* @since 1.11.0
*
* @param string $value upload ID value
*/
public function update_upload_id( $value ) {
$this->upload_id = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_UPLOAD_ID, $this->upload_id );
}
/**
* Updates the Facebook pixel install time.
*
* @since 1.10.0
*
* @param int $value pixel install time, in UTC seconds
*/
public function update_pixel_install_time( $value ) {
$value = (int) $value;
$this->pixel_install_time = $value ?: null;
update_option( self::OPTION_PIXEL_INSTALL_TIME, $value ?: '' );
}
/**
* Updates the Facebook 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 );
update_option( self::OPTION_JS_SDK_VERSION, $this->js_sdk_version );
}
/**
* Sanitizes a value that's a Facebook credential.
*
* @since 1.10.0
*
* @param string $value value to sanitize
* @return string
*/
private function sanitize_facebook_credential( $value ) {
return wc_clean( is_string( $value ) ? $value : '' );
}
/** Conditional methods *******************************************************************************************/
/**
* Determines whether Facebook for WooCommerce is configured.
*
* @since 1.10.0
*
* @return bool
*/
public function is_configured() {
return $this->get_facebook_page_id() && facebook_for_woocommerce()->get_connection_handler()->is_connected();
}
/**
* Determines whether advanced matching is enabled.
*
* @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
*/
return (bool) apply_filters( 'wc_facebook_is_advanced_matching_enabled', true, $this );
}
/**
* Determines whether product sync is enabled.
*
* @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
*/
return (bool) apply_filters( 'wc_facebook_is_product_sync_enabled', 'yes' === get_option( self::SETTING_ENABLE_PRODUCT_SYNC, 'yes' ), $this );
}
/**
* Determines whether the scheduled re-sync is enabled.
*
* @since 1.10.0
* @deprecated 2.0.0
*
* @return bool
*/
public function is_scheduled_resync_enabled() {
wc_deprecated_function( __METHOD__, '2.0.0' );
return false;
}
/**
* Determines whether the Facebook messenger is enabled.
*
* @since 1.10.0
*
* @return bool
*/
public function is_messenger_enabled() {
/**
* Filters whether the Facebook messenger is enabled.
*
* @since 1.10.0
*
* @param bool $is_enabled whether the Facebook messenger is enabled
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (bool) apply_filters( 'wc_facebook_is_messenger_enabled', 'yes' === get_option( self::SETTING_ENABLE_MESSENGER ), $this );
}
/**
* Determines whether debug mode is enabled.
*
* @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
*/
return (bool) apply_filters( 'wc_facebook_is_debug_mode_enabled', 'yes' === get_option( self::SETTING_ENABLE_DEBUG_MODE ), $this );
}
/***
* Determines if the feed has been migrated from FBE 1 to FBE 1.5
*
* @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;
}
/**
* Gets message HTML.
*
* @return string
*/
private function get_message_html( $message, $type = 'error' ) {
ob_start();
?>