Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions facebook-commerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -853,9 +853,6 @@ public function on_product_save( int $wp_id ) {
$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 );
Expand Down
206 changes: 93 additions & 113 deletions includes/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ class Admin {
/** @var string the "sync disabled" sync mode slug */
const SYNC_MODE_SYNC_DISABLED = 'sync_disabled';

/** @var string the "include" sync mode for bulk edit */
const BULK_EDIT_SYNC = 'bulk_edit_sync';

/** @var string the "exclude" sync mode for bulk edit */
const BULK_EDIT_DELETE = 'bulk_edit_delete';

/** @var Product_Categories the product category admin handler */
protected $product_categories;

Expand Down Expand Up @@ -93,8 +99,8 @@ public function __construct() {
add_filter( 'request', array( $this, 'filter_products_by_sync_enabled' ) );

// add bulk actions to manage products sync
add_filter( 'bulk_actions-edit-product', array( $this, 'add_products_sync_bulk_actions' ), 40 );
add_action( 'handle_bulk_actions-edit-product', array( $this, 'handle_products_sync_bulk_actions' ) );
add_action( 'woocommerce_product_bulk_edit_end', array( $this, 'add_facebook_sync_bulk_edit_dropdown_at_bottom' ) );
add_action( 'woocommerce_product_bulk_edit_save', array( $this, 'handle_products_sync_bulk_actions' ), 10, 1 );

// add Product data tab
add_filter( 'woocommerce_product_data_tabs', array( $this, 'add_product_settings_tab' ) );
Expand Down Expand Up @@ -440,7 +446,7 @@ public function get_product_categories_handler() {
* @return array
*/
public function add_product_list_table_columns( $columns ) {
$columns['facebook_sync'] = __( 'Facebook sync', 'facebook-for-woocommerce' );
$columns['facebook_sync'] = __( 'Synced to Meta catalog', 'facebook-for-woocommerce' );
return $columns;
}

Expand Down Expand Up @@ -474,13 +480,9 @@ public function add_product_list_table_columns_content( $column ) {
}

if ( $should_sync ) {
if ( Products::is_product_visible( $product ) ) {
esc_html_e( 'Sync and show', 'facebook-for-woocommerce' );
} else {
esc_html_e( 'Sync and hide', 'facebook-for-woocommerce' );
}
esc_html_e( 'Synced', 'facebook-for-woocommerce' );
} else {
esc_html_e( 'Do not sync', 'facebook-for-woocommerce' );
esc_html_e( 'Not synced', 'facebook-for-woocommerce' );
if ( ! empty( $no_sync_reason ) ) {
echo wc_help_tip( $no_sync_reason );
}
Expand Down Expand Up @@ -513,6 +515,36 @@ public function add_products_by_sync_enabled_input_filter() {
<?php
}


/**
* Adds a dropdown input to Include or Exclude product in Facebook Bulk Sync.
*
* @internal
*/
public function add_facebook_sync_bulk_edit_dropdown_at_bottom() {
global $typenow;

if ( 'product' !== $typenow ) {
return;
}

// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$choice = isset( $_GET['facebook_bulk_sync_options'] ) ? (string) sanitize_text_field( wp_unslash( $_GET['facebook_bulk_sync_options'] ) ) : '';

?>
<label>
<span class="title"><?php esc_html_e( 'Sync to Meta catalog', 'facebook-for-woocommerce' ); ?></span>
<span class="input-text-wrap">
<select class="facebook_bulk_sync_options" name="facebook_bulk_sync_options">
<option value=""> <?php esc_html_e( '— No Change —', 'facebook-for-woocommerce' ); ?></option>;
<option value="<?php echo esc_attr( self::BULK_EDIT_SYNC ); ?>" <?php selected( $choice, self::BULK_EDIT_SYNC ); ?>><?php esc_html_e( 'Sync', 'facebook-for-woocommerce' ); ?></option>
<option value="<?php echo esc_attr( self::BULK_EDIT_DELETE ); ?>" <?php selected( $choice, self::BULK_EDIT_DELETE ); ?>><?php esc_html_e( 'Do not sync', 'facebook-for-woocommerce' ); ?></option>
</select>
</span>
</label>
<?php
}

/**
* Filters products by Facebook sync setting.
*
Expand Down Expand Up @@ -853,116 +885,80 @@ private function add_query_vars_to_find_hidden_products( array $query_vars ) {
return $query_vars;
}


/**
* Adds bulk actions in the products edit screen.
*
* @internal
*
* @since 1.10.0
*
* @param array $bulk_actions array of bulk action keys and labels
* @return array
*/
public function add_products_sync_bulk_actions( $bulk_actions ) {
$bulk_actions['facebook_include'] = __( 'Include in Facebook sync', 'facebook-for-woocommerce' );
$bulk_actions['facebook_exclude'] = __( 'Exclude from Facebook sync', 'facebook-for-woocommerce' );
return $bulk_actions;
}


/**
* Handles a Facebook product sync bulk action.
* Called every time for a product
*
* @internal
*
* @since 1.10.0
*
* @param string $redirect admin URL used by WordPress to redirect after performing the bulk action
* @return string
* @param string $product_edit the product metadata that is being edited.
*/
public function handle_products_sync_bulk_actions( $redirect ) {

// primary dropdown at the top of the list table
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_REQUEST['action'] ) && -1 !== (int) $_REQUEST['action'] ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : null;

// secondary dropdown at the bottom of the list table
if ( ! $action ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_REQUEST['action2'] ) && -1 !== (int) $_REQUEST['action2'] ? sanitize_text_field( wp_unslash( $_REQUEST['action2'] ) ) : null;
}

if ( $action && in_array( $action, array( 'facebook_include', 'facebook_exclude' ), true ) ) {
$products = [];
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$product_ids = isset( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ? array_map( 'absint', $_REQUEST['post'] ) : [];
if ( ! empty( $product_ids ) ) {
/** @var \WC_Product[] $enabling_sync_virtual_products virtual products that are being included */
$enabling_sync_virtual_products = [];
/** @var \WC_Product_Variation[] $enabling_sync_virtual_variations virtual variations that are being included */
$enabling_sync_virtual_variations = [];
foreach ( $product_ids as $product_id ) {
$product = wc_get_product( $product_id );
if ( $product ) {
$products[] = $product;
if ( 'facebook_include' === $action ) {
if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) {
$enabling_sync_virtual_products[ $product->get_id() ] = $product;
} elseif ( $product->is_type( 'variable' ) ) {
// collect the virtual variations
foreach ( $product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) {
$enabling_sync_virtual_products[ $product->get_id() ] = $product;
$enabling_sync_virtual_variations[ $variation->get_id() ] = $variation;
}
}
}//end if
}//end if
public function handle_products_sync_bulk_actions( $product_edit ) {

$sync_mode = isset( $_GET['facebook_bulk_sync_options'] ) ? (string) sanitize_text_field( wp_unslash( $_GET['facebook_bulk_sync_options'] ) ) : null;

if ( $sync_mode ) {
/** @var \WC_Product[] $enabling_sync_virtual_products virtual products that are being included */
$enabling_sync_virtual_products = [];
/** @var \WC_Product_Variation[] $enabling_sync_virtual_variations virtual variations that are being included */
$enabling_sync_virtual_variations = [];
/** @var \WC_Product $product to store the product meta data */
$product = wc_get_product( $product_edit );

if ( $product && $this::BULK_EDIT_SYNC === $sync_mode ) {
if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) {
$enabling_sync_virtual_products[ $product->get_id() ] = $product;
} elseif ( $product->is_type( 'variable' ) ) {
// collect the virtual variations
foreach ( $product->get_children() as $variation_id ) {
$variation = wc_get_product( $variation_id );
if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) {
$enabling_sync_virtual_variations[ $variation->get_id() ] = $variation;
}
}//end foreach
if ( ! empty( $enabling_sync_virtual_variations ) ) {
$enabling_sync_virtual_products[ $product->get_id() ] = $product;
}//end if
}//end foreach
}//end if
}//end if

if ( ! empty( $enabling_sync_virtual_products ) || ! empty( $enabling_sync_virtual_variations ) ) {
// display notice if enabling sync for virtual products or variations
set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id(), true, 15 * MINUTE_IN_SECONDS );
set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id(), array_keys( $enabling_sync_virtual_products ), 15 * MINUTE_IN_SECONDS );
if ( ! empty( $enabling_sync_virtual_products ) || ! empty( $enabling_sync_virtual_variations ) ) {
// display notice if enabling sync for virtual products or variations
set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id(), true, 15 * MINUTE_IN_SECONDS );
set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id(), array_keys( $enabling_sync_virtual_products ), 15 * MINUTE_IN_SECONDS );

// set visibility for virtual products
foreach ( $enabling_sync_virtual_products as $product ) {
// set visibility for virtual products
foreach ( $enabling_sync_virtual_products as $product ) {

// do not set visibility for variable products
if ( ! $product->is_type( 'variable' ) ) {
Products::set_product_visibility( $product, false );
}
// do not set visibility for variable products
if ( ! $product->is_type( 'variable' ) ) {
Products::set_product_visibility( $product, false );
}
}

// set visibility for virtual variations
foreach ( $enabling_sync_virtual_variations as $variation ) {
// set visibility for virtual variations
foreach ( $enabling_sync_virtual_variations as $variation ) {

Products::set_product_visibility( $variation, false );
}
}//end if
Products::set_product_visibility( $variation, false );
}
}//end if

if ( 'facebook_include' === $action ) {
$products[] = $product;

Products::enable_sync_for_products( $products );
if ( $this::BULK_EDIT_SYNC === $sync_mode ) {

$this->resync_products( $products );
Products::enable_sync_for_products( $products );

} elseif ( 'facebook_exclude' === $action ) {
$this->resync_products( $products );

Products::disable_sync_for_products( $products );
} elseif ( $this::BULK_EDIT_DELETE === $sync_mode ) {

self::add_product_disabled_sync_notice( count( $products ) );
}
}//end if
}//end if
Products::disable_sync_for_products( $products );

return $redirect;
}
} //end if
}


/**
* Re-syncs the given products.
*
Expand Down Expand Up @@ -994,22 +990,6 @@ private function resync_products( array $products ) {
}
}


/**
* Adds a transient so an informational notice is displayed on the next page load.
*
* @since 2.0.0
*
* @param int $count number of products
*/
public static function add_product_disabled_sync_notice( $count = 1 ) {

if ( ! facebook_for_woocommerce()->get_admin_notice_handler()->is_notice_dismissed( 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-product-disabled-sync' ) ) {
set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_show_product_disabled_sync_notice_' . get_current_user_id(), $count, MINUTE_IN_SECONDS );
}
}


/**
* Adds a message for after a product or set of products get excluded from sync.
*
Expand Down
2 changes: 1 addition & 1 deletion includes/Products.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private static function set_sync_for_products( array $products, $enabled ) {
}

// Remove excluded product from FB.
if ( "no" === $enabled && self::product_should_be_deleted( $product ) ) {
if ( "no" === $enabled ) {
facebook_for_woocommerce()->get_integration()->delete_fb_product( $product );
}

Expand Down
17 changes: 10 additions & 7 deletions tests/Unit/WCFacebookCommerceIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,17 @@ public function test_on_product_save_existing_simple_product_sync_enabled_update
*/
public function test_on_product_save_existing_simple_product_sync_disabled_updates_the_product() {
$product_to_update = WC_Helper_Product::create_simple_product();
$product_to_delete = WC_Helper_Product::create_simple_product();

// The idea of the following mock is to overide the delete_product_item.
// The test is that the product item is being deleted as when it is marked for do not sync.
// The mock below is hit otherwise it would generate a random Mock_Response and throw error
$integration_mock = $this->createMock(WC_Facebookcommerce_Integration::class);
$integration_mock->method('delete_product_item');
$this->integration = $integration_mock;

$_POST['wc_facebook_sync_mode'] = Admin::SYNC_MODE_SYNC_DISABLED;

$_POST[ WC_Facebook_Product::FB_REMOVE_FROM_SYNC ] = $product_to_delete->get_id();
$_POST[ WC_Facebook_Product::FB_REMOVE_FROM_SYNC ] = $product_to_update->get_id();

$product_to_update->set_stock_status( 'instock' );

Expand All @@ -586,11 +592,8 @@ public function test_on_product_save_existing_simple_product_sync_disabled_updat

$this->integration->on_product_save( $product_to_update->get_id() );

$this->assertEquals( null, get_post_meta( $product_to_delete->get_id(), WC_Facebookcommerce_Integration::FB_PRODUCT_ITEM_ID, true ) );
$this->assertEquals( null, get_post_meta( $product_to_delete->get_id(), WC_Facebookcommerce_Integration::FB_PRODUCT_GROUP_ID, true ) );
$this->assertEquals( 'no', get_post_meta( $product_to_update->get_id(), Products::SYNC_ENABLED_META_KEY, true ) );
$this->assertEquals( 'no', get_post_meta( $product_to_update->get_id(), Products::VISIBILITY_META_KEY, true ) );

$this->assertEquals( 'facebook-product-item-id', get_post_meta( $product_to_update->get_id(), WC_Facebookcommerce_Integration::FB_PRODUCT_ITEM_ID, true ) );
$this->assertEquals( null, get_post_meta( $product_to_update->get_id(), WC_Facebookcommerce_Integration::FB_PRODUCT_GROUP_ID, true ) );
}

/**
Expand Down