Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0412d26
Added external_variant_id to the feed file (#2998)
mshymon Apr 2, 2025
455d5df
Added support for syncing product type (#3013)
vinkmeta Apr 4, 2025
8fe9cae
Relocating bulk actions (#2943)
SayanPandey Apr 4, 2025
2af138a
Filtration on All Products page | Synced and Not Synced (#2999)
SayanPandey Apr 4, 2025
3f9014d
Updated PR Template (#3019)
vinkmeta Apr 7, 2025
99ba590
Fixed Null check exceptions (#3015)
vinkmeta Apr 7, 2025
c1ef800
Relaxing sync validation (#2969)
raymon1 Apr 8, 2025
3516faf
Feature/truncate title description (#3023)
raymon1 Apr 8, 2025
d256ef7
Updated PR template (#3053)
vinkmeta Apr 13, 2025
755f209
Updated the plugin tags, and removed the legacy contributors
vahidkay-meta Mar 20, 2025
9cc7d1e
Fixed the item not found error by using filter in the product endpoin…
vinkmeta Apr 14, 2025
27ceac6
fix: Update descriptive tooltip for MPN text input (#3034)
Apr 9, 2025
b02ac71
docs: Document get_unmapped_attributes from Woo to Meta (#3033)
Apr 9, 2025
2989c43
fix: Add parent product material inheritance for variations (#3035)
Apr 10, 2025
aabef28
fix: mismatch between tooltip messages for Skirt Length and Sleeve Le…
Apr 10, 2025
eaaea89
Updating version to 3.4.7
Apr 17, 2025
eb8b120
Fixing meta to Meta (#3063)
SayanPandey Apr 17, 2025
9e244a9
feat(product-sync): Add separate short_description field to Facebook …
Apr 9, 2025
e6443a1
Feature/sync short description remove dropdown (#3031)
Apr 9, 2025
1c7e71b
Feature: Short Description Fallback (#3048)
Apr 14, 2025
652ce11
Fixing meta to Meta (#3063)
SayanPandey Apr 17, 2025
f9b89c4
Fix for the gap in Purchase events through CAPI (#3060)
vahidkay-meta Apr 23, 2025
988b293
Remove type cast for GPC (#3078)
Apr 23, 2025
c0ad22f
Disable unmatched fields to batch api (#3079)
Apr 23, 2025
e6894e9
Updating changelog.txt and readme.txt for v3.4.7
Apr 24, 2025
43f4a3e
Fix product variation fields not saving correctly (#3090)
Apr 24, 2025
bec1ad3
Update changelog.txt and readme.txt for v3.4.7
Apr 28, 2025
a97de9e
Removed failing test due to merge conflicts (#3103)
vinkmeta Apr 28, 2025
2722a15
More updates to changelog.txt and readme.txt for v3.4.7
Apr 28, 2025
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
18 changes: 11 additions & 7 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ List any dependencies that are required for this change.

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Syntax change (non-breaking change which fixes code modularity, linting or phpcs issues)
- Breaking change (fix or feature that would cause existing functionality to not work as expected)


## Screenshots
Expand All @@ -29,10 +30,13 @@ Please also list any relevant details for your test configuration.

## Checklist

- [ ] I followed general Pull Request best practices. Meta employees to follow this [wiki]([url](https://fburl.com/wiki/2cgfduwc))
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added tests and all the new and existing unit tests pass locally with my changes
- [ ] I have completed dogfooding and QA testing, or I have conducted thorough due diligence to ensure that it does not break existing functionality.
- [] I have commented my code, particularly in hard-to-understand areas.
- [] I have confirmed that my changes do not introduce any new PHPCS warnings or errors.
- [] I have checked plugin debug logs that my changes do not introduce any new PHP warnings or FATAL errors.
- [] I followed general Pull Request best practices. Meta employees to follow this [wiki]([url](https://fburl.com/wiki/2cgfduwc)).
- [] I have added tests (if necessary) and all the new and existing unit tests pass locally with my changes.
- [] I have completed dogfooding and QA testing, or I have conducted thorough due diligence to ensure that it does not break existing functionality.
- [] I have updated or requested update to plugin documentations (if necessary). Meta employees to follow this [wiki]([url](https://fburl.com/wiki/nhx73tgs)).


## Changelog entry
Expand Down
597 changes: 239 additions & 358 deletions bin/fb_google_category_to_attribute_mapping.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
*** Facebook for WooCommerce Changelog ***

= 3.4.7 - 2025-04-17 =
* Tweak - Added external_variant_id to the feed file by @mshymon in #2998
* Tweak - Added support for syncing product type by @vinkmeta in #3013
* Tweak - Relocating bulk actions by @SayanPandey in #2943
* Tweak - Filtration on All Products page | Synced and Not Synced by @SayanPandey in #2999
* Tweak - Updated PR Template by @vinkmeta in #3019
* Fix - Null check exceptions by @vinkmeta in #3015
* Tweak - Relaxing sync validations by @raymon1 in #2969
* Tweak - Truncates extra characters from title and description by @raymon1 in #3023
* Tweak - Updated PR template by @vinkmeta in #3053
* Fix - The item not found error by using filter in the product endpoint @vinkmeta in #3054
* Fix - Bug where MPN input box had no tooltip by @devbodaghe in #3034
* Tweak - Investigation: WooCommerce to Facebook Product Attribute Syncing by @devbodaghe in #3033
* Fix - Add parent product material inheritance for variations by @devbodaghe in #3035
* Fix - Tooltip Messages for Skirt Length and Sleeve Length by @devbodaghe in #3039
* Fix - Typo in Admin.php by @SayanPandey in #3063
* Add - Add separate short_description field to Facebook product data by @devbodaghe in #3029
* Tweak - Sync short description remove dropdown by @devbodaghe in #3031
* Tweak - Short Description Fallback by @devbodaghe in #3048
* Fix - A problem where Purchase event was not firing if thankyou page was not shown or Purchase state updated through Woo dashboard by @vahidkay-meta in #3060
* Tweak - Remove type casting for gpc to int by @devbodaghe in 3078
* Tweak - Disable unmapped fields to batch api by @devbodaghe in #3079
* Fix - Product variation fields not saving correctly by @devbodaghe in #3090
* Fix - Removed failing test due to merge conflicts @vinkmeta in #3103

= 3.4.6 - 2025-04-04 =
* Fix - Product availability syncing by @vinkmeta in #3010
* Fix - Product attribute sort error which prevented product edits in certain scenarios by @iodic in #3012
Expand Down
2 changes: 1 addition & 1 deletion data/google_category_to_attribute_mapping_fields.json

Large diffs are not rendered by default.

90 changes: 32 additions & 58 deletions facebook-commerce-events-tracker.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ private function add_hooks() {
add_action( 'woocommerce_after_single_product', array( $this, 'inject_view_content_event' ) );
add_action( 'woocommerce_after_single_product', array( $this, 'maybe_inject_search_event' ) );

// ViewCategory events
add_action( 'woocommerce_after_shop_loop', array( $this, 'inject_view_category_event' ) );

// Search events
add_action( 'pre_get_posts', array( $this, 'inject_search_event' ) );
add_filter( 'woocommerce_redirect_single_search_result', array( $this, 'maybe_add_product_search_event_to_session' ) );

Expand All @@ -122,23 +124,23 @@ private function add_hooks() {

// InitiateCheckout events
add_action( 'woocommerce_after_checkout_form', array( $this, 'inject_initiate_checkout_event' ) );

// InitiateCheckout events for checkout block.
add_action( 'woocommerce_blocks_checkout_enqueue_data', array( $this, 'inject_initiate_checkout_event' ) );

// Purchase and Subscribe events
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'inject_purchase_event' ) );
add_action( 'woocommerce_new_order', array( $this, 'inject_purchase_event' ) );
add_action( 'woocommerce_payment_complete', array( $this, 'inject_purchase_event' ), 10 );
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'inject_purchase_event' ), 20 );
add_action( 'woocommerce_thankyou', array( $this, 'inject_purchase_event' ), 40 );
add_action( 'woocommerce_order_status_processing', array( $this, 'inject_purchase_event' ), 50 );
add_action( 'woocommerce_order_status_completed', array( $this, 'inject_purchase_event' ), 50 );
add_action( 'woocommerce_process_shop_order_meta', array( $this, 'inject_purchase_event' ), 60 );

// Checkout update order meta from the Checkout Block.
if ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '7.2.0', '>=' ) ) {
add_action( 'woocommerce_store_api_checkout_update_order_meta', array( $this, 'inject_order_meta_event_for_checkout_block_flow' ), 10, 1 );
} elseif ( version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '6.3.0', '>=' ) ) {
add_action( 'woocommerce_blocks_checkout_update_order_meta', array( $this, 'inject_order_meta_event_for_checkout_block_flow' ), 10, 1 );
} else {
add_action( '__experimental_woocommerce_blocks_checkout_update_order_meta', array( $this, 'inject_order_meta_event_for_checkout_block_flow' ), 10, 1 );
}

// TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
// Lead events through Contact Form 7
add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );

// Flush pending events on shutdown
add_action( 'shutdown', array( $this, 'send_pending_events' ) );
}

Expand Down Expand Up @@ -795,6 +797,8 @@ public function inject_initiate_checkout_event() {
*
* This may happen either when:
* - WooCommerce signals a payment transaction complete (most gateways)
* - The order status is changed through the Woo dashboard to Processing or Completed
* - The Payment Completed event is fired, which happens in case of some external payment gateways.
* - Customer reaches Thank You page skipping payment (for gateways that do not require payment, e.g. Cheque, BACS, Cash on delivery...)
*
* The method checks if the event was not triggered already avoiding a duplicate.
Expand All @@ -808,7 +812,9 @@ public function inject_purchase_event( $order_id ) {

$event_name = 'Purchase';

if ( ! $this->is_pixel_enabled() || $this->pixel->is_last_event( $event_name ) ) {
$valid_purchase_order_states = array( 'processing', 'completed' );

if ( ! $this->is_pixel_enabled() ) {
return;
}

Expand All @@ -817,23 +823,26 @@ public function inject_purchase_event( $order_id ) {
if ( ! $order ) {
return;
}

// Get the status of the order to ensure we track the actual purchases and not the ones that have a failed payment.
$order_state = $order->get_status();

// use a session flag to ensure an order is tracked with any payment method, also when the order is placed through AJAX
$order_placed_flag = '_wc_' . facebook_for_woocommerce()->get_id() . '_order_placed_' . $order_id;

// use a session flag to ensure a Purchase event is not tracked multiple times
// use a session flag to ensure this Purchase event is not tracked multiple times
$purchase_tracked_flag = '_wc_' . facebook_for_woocommerce()->get_id() . '_purchase_tracked_' . $order_id;

// when saving the order meta data: add a flag to mark the order tracked
if ( 'woocommerce_checkout_update_order_meta' === current_action() ) {
set_transient( $order_placed_flag, 'yes', 15 * MINUTE_IN_SECONDS );
// Return if this Purchase event has already been tracked
if ( 'yes' === get_transient( $purchase_tracked_flag ) || $order->meta_exists( '_meta_purchase_tracked' ) || ! in_array( $order_state, $valid_purchase_order_states ) ) {
return;
}

// bail if by the time we are on the thank you page the meta has not been set or we already tracked a Purchase event
if ( 'yes' !== get_transient( $order_placed_flag ) || 'yes' === get_transient( $purchase_tracked_flag ) ) {
return;
}
// Mark the order as tracked for the session
set_transient( $purchase_tracked_flag, 'yes', 15 * MINUTE_IN_SECONDS );

// Set a flag to ensure this Purchase event is not going to be sent across different sessions
$order->add_meta_data( '_meta_purchase_tracked', true, true );

// Save the metadata
$order->save();

$content_type = 'product';
$contents = array();
Expand Down Expand Up @@ -885,41 +894,6 @@ public function inject_purchase_event( $order_id ) {

$this->inject_subscribe_event( $order_id );

// mark the order as tracked
set_transient( $purchase_tracked_flag, 'yes', 15 * MINUTE_IN_SECONDS );

}

/**
* Inject order meta gor WooCommerce Checkout Blocks flow.
* The blocks flow does not trigger the woocommerce_checkout_update_order_meta so we can't rely on it.
* The Checkout Block has its own hook that allows us to inject the meta at
* the appropriate moment: woocommerce_store_api_checkout_update_order_meta.
*
* Note: __experimental_woocommerce_blocks_checkout_update_order_meta has been deprecated
* as of WooCommerce Blocks 6.3.0
*
* @since 2.6.6
*
* @param WC_Order|int $the_order Order object or id.
*/
public function inject_order_meta_event_for_checkout_block_flow( $the_order ) {

$event_name = 'Purchase';

if ( ! $this->is_pixel_enabled() || $this->pixel->is_last_event( $event_name ) ) {
return;
}

$order = wc_get_order($the_order);

if ( ! $order ) {
return;
}

$order_placed_flag = '_wc_' . facebook_for_woocommerce()->get_id() . '_order_placed_' . $order->get_id();
set_transient( $order_placed_flag, 'yes', 15 * MINUTE_IN_SECONDS );

}


Expand Down
54 changes: 10 additions & 44 deletions facebook-commerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
/** @var string request headers in the debug log */
const SETTING_REQUEST_HEADERS_IN_DEBUG_MODE = 'wc_facebook_request_headers_in_debug_log';

/** @var string the standard product description mode name */
const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';

/** @var string the short product description mode name */
const PRODUCT_DESCRIPTION_MODE_SHORT = 'short';

/** @var string custom taxonomy Facebook Product Set ID */
const FB_PRODUCT_SET_ID = 'fb_product_set_id';

Expand Down Expand Up @@ -811,9 +805,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 Expand Up @@ -2504,36 +2495,6 @@ public function get_excluded_product_tag_ids() {
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;
}

/** Setter methods ************************************************************************************************/


Expand Down Expand Up @@ -3006,17 +2967,22 @@ public function get_product_fbid( string $fbid_type, int $wp_id, $woo_product =
$fb_retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );

try {
$facebook_ids = $this->facebook_for_woocommerce->get_api()->get_product_facebook_ids(
$response = $this->facebook_for_woocommerce->get_api()->get_product_facebook_ids(
$this->get_product_catalog_id(),
$fb_retailer_id
);

if ( $facebook_ids->id ) {
if ( $response->data && $response->data[0] && $response->data[0]['id'] ) {
$fb_id = $fbid_type == self::FB_PRODUCT_GROUP_ID
? $facebook_ids->get_facebook_product_group_id()
: $facebook_ids->id;
? $response->data[0]['product_group']['id']
: $response->data[0]['id'];
update_post_meta( $wp_id, $fbid_type, $fb_id );
return $fb_id;
} elseif ( $response->id ) {
$fb_id = $fbid_type == self::FB_PRODUCT_GROUP_ID
? $response->get_facebook_product_group_id()
: $response->id;
update_post_meta( $wp_id, $fbid_type, $fb_id );

return $fb_id;
}
} catch ( Exception $e ) {
Expand Down
4 changes: 2 additions & 2 deletions facebook-for-woocommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
* Author: Facebook
* Author URI: https://www.facebook.com/
* Version: 3.4.6
* Version: 3.4.7
* Requires at least: 5.6
* Requires PHP: 7.4
* Text Domain: facebook-for-woocommerce
Expand Down Expand Up @@ -48,7 +48,7 @@ class WC_Facebook_Loader {
/**
* @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
*/
const PLUGIN_VERSION = '3.4.6'; // WRCS: DEFINED_VERSION.
const PLUGIN_VERSION = '3.4.7'; // WRCS: DEFINED_VERSION.

// Minimum PHP version required by this plugin.
const MINIMUM_PHP_VERSION = '7.4.0';
Expand Down
23 changes: 22 additions & 1 deletion includes/API/FBE/Configuration/Read/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Response extends API\Response {
* @return boolean
*/
public function is_ig_shopping_enabled(): bool {

if ( empty( $this->response_data['ig_shopping'] ) ) {
return false;
}
return (bool) $this->response_data['ig_shopping']['enabled'] ?? false;
}

Expand All @@ -27,6 +31,23 @@ public function is_ig_shopping_enabled(): bool {
* @return boolean
*/
public function is_ig_cta_enabled(): bool {
return (bool) $this->response_data['ig_cta']['enabled'];

if ( empty( $this->response_data['ig_cta'] ) ) {
return false;
}
return (bool) $this->response_data['ig_cta']['enabled'] ?? false;
}

/**
* Gets the commerce extension URI.
*
* @return string Commerce extension URI or empty string if not available.
*/
public function get_commerce_extension_uri(): string {

if ( empty( $this->response_data['commerce_extension'] ) ) {
return '';
}
return $this->response_data['commerce_extension']['uri'] ?? '';
}
}
19 changes: 15 additions & 4 deletions includes/API/ProductCatalog/Products/Id/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
defined( 'ABSPATH' ) || exit;

/**
* Request object for Product Catalog > Product Groups > Products > Update Graph Api.
* Request object for Product Catalog > Products > Get Graph Api.
*
* @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
* @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/products/
*/
class Request extends ApiRequest {

Expand All @@ -19,7 +19,18 @@ class Request extends ApiRequest {
* @param string $facebook_product_retailer_id Facebook Product Retailer ID.
*/
public function __construct( string $facebook_product_catalog_id, string $facebook_product_retailer_id ) {
$path = "catalog:{$facebook_product_catalog_id}:" . base64_encode( $facebook_product_retailer_id ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
parent::__construct( "/{$path}/?fields=id,product_group{id}", 'GET' );

/**
* We use the endpoint with filter to get the product id and group id for new products to check if the product is already synced to Facebook.
*/
$path = "/{$facebook_product_catalog_id}/products";
parent::__construct( $path, 'GET' );

$this->set_params(
array(
'filter' => '{"retailer_id":{"eq":"' . $facebook_product_retailer_id . '"}}',
'fields' => 'id,product_group{id}',
)
);
}
}
2 changes: 1 addition & 1 deletion includes/API/ProductCatalog/Products/Id/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Response object for Product Catalog > Product Groups > Update Graph Api.
*
* @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
* @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/products/
* @property-read string id Either request was successful or not.
* @property-read array product_group Product group data container containing facebook product group id
* e.g. product_group => [ id => <facebook product group id>]
Expand Down
Loading