Version Description
Download this release
Release Info
Developer | SkyVerge |
Plugin | Facebook for WooCommerce |
Version | 2.0.3 |
Comparing to | |
See all releases |
Code changes from version 2.0.2 to 2.0.3
- changelog.txt +5 -0
- class-wc-facebookcommerce.php +28 -1
- facebook-commerce-events-tracker.php +65 -8
- facebook-commerce-pixel-event.php +14 -2
- facebook-commerce.php +46 -2
- facebook-for-woocommerce.php +1 -1
- i18n/languages/facebook-for-woocommerce.pot +30 -30
- includes/Events/AAMSettings.php +170 -0
- includes/Events/Event.php +33 -2
- includes/Events/Normalizer.php +229 -0
- includes/Lifecycle.php +52 -0
- includes/Utilities/Background_Handle_Virtual_Products_Variations.php +98 -22
- includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php +194 -0
- includes/fbutils.php +36 -16
- readme.txt +7 -2
changelog.txt
CHANGED
@@ -1,5 +1,10 @@
|
|
1 |
*** Facebook for WooCommerce Changelog ***
|
2 |
|
|
|
|
|
|
|
|
|
|
|
3 |
2020.09.25 - version 2.0.2
|
4 |
* Tweak - Allow simple and variable products with zero/empty price to sync to Facebook
|
5 |
* Tweak - Use the bundle price for Product Bundles products with individually priced items
|
1 |
*** Facebook for WooCommerce Changelog ***
|
2 |
|
3 |
+
2020.10.02 - version 2.0.3
|
4 |
+
* Tweak - Pixel events now can include advanced matching information
|
5 |
+
* Fix - Send contents parameter for ViewContent event using the correct format
|
6 |
+
* Fix - Remove duplicate visibility meta entries from postmeta table
|
7 |
+
|
8 |
2020.09.25 - version 2.0.2
|
9 |
* Tweak - Allow simple and variable products with zero/empty price to sync to Facebook
|
10 |
* Tweak - Use the bundle price for Product Bundles products with individually priced items
|
class-wc-facebookcommerce.php
CHANGED
@@ -11,6 +11,7 @@
|
|
11 |
use SkyVerge\WooCommerce\Facebook\API;
|
12 |
use SkyVerge\WooCommerce\Facebook\Lifecycle;
|
13 |
use SkyVerge\WooCommerce\Facebook\Utilities\Background_Handle_Virtual_Products_Variations;
|
|
|
14 |
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
|
15 |
|
16 |
if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
@@ -21,7 +22,7 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
|
21 |
|
22 |
|
23 |
/** @var string the plugin version */
|
24 |
-
const VERSION = '2.0.
|
25 |
|
26 |
/** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
|
27 |
const PLUGIN_VERSION = self::VERSION;
|
@@ -57,6 +58,9 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
|
57 |
/** @var Background_Handle_Virtual_Products_Variations instance */
|
58 |
protected $background_handle_virtual_products_variations;
|
59 |
|
|
|
|
|
|
|
60 |
/** @var \SkyVerge\WooCommerce\Facebook\Products\Sync products sync handler */
|
61 |
private $products_sync_handler;
|
62 |
|
@@ -114,6 +118,8 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
|
114 |
require_once __DIR__ . '/includes/fbproductfeed.php';
|
115 |
require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
|
116 |
require_once __DIR__ . '/includes/Events/Event.php';
|
|
|
|
|
117 |
|
118 |
$this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
|
119 |
$this->products_sync_handler = new \SkyVerge\WooCommerce\Facebook\Products\Sync();
|
@@ -133,6 +139,14 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
|
133 |
require_once __DIR__ . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php';
|
134 |
|
135 |
$this->background_handle_virtual_products_variations = new Background_Handle_Virtual_Products_Variations();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
}
|
137 |
|
138 |
$this->connection_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\Connection( $this );
|
@@ -498,6 +512,19 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
|
498 |
}
|
499 |
|
500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
501 |
/**
|
502 |
* Gets the products sync handler.
|
503 |
*
|
11 |
use SkyVerge\WooCommerce\Facebook\API;
|
12 |
use SkyVerge\WooCommerce\Facebook\Lifecycle;
|
13 |
use SkyVerge\WooCommerce\Facebook\Utilities\Background_Handle_Virtual_Products_Variations;
|
14 |
+
use SkyVerge\WooCommerce\Facebook\Utilities\Background_Remove_Duplicate_Visibility_Meta;
|
15 |
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
|
16 |
|
17 |
if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
|
22 |
|
23 |
|
24 |
/** @var string the plugin version */
|
25 |
+
const VERSION = '2.0.3';
|
26 |
|
27 |
/** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
|
28 |
const PLUGIN_VERSION = self::VERSION;
|
58 |
/** @var Background_Handle_Virtual_Products_Variations instance */
|
59 |
protected $background_handle_virtual_products_variations;
|
60 |
|
61 |
+
/** @var Background_Remove_Duplicate_Visibility_Meta job handler instance */
|
62 |
+
protected $background_remove_duplicate_visibility_meta;
|
63 |
+
|
64 |
/** @var \SkyVerge\WooCommerce\Facebook\Products\Sync products sync handler */
|
65 |
private $products_sync_handler;
|
66 |
|
118 |
require_once __DIR__ . '/includes/fbproductfeed.php';
|
119 |
require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
|
120 |
require_once __DIR__ . '/includes/Events/Event.php';
|
121 |
+
require_once __DIR__ . '/includes/Events/Normalizer.php';
|
122 |
+
require_once __DIR__ . '/includes/Events/AAMSettings.php';
|
123 |
|
124 |
$this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
|
125 |
$this->products_sync_handler = new \SkyVerge\WooCommerce\Facebook\Products\Sync();
|
139 |
require_once __DIR__ . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php';
|
140 |
|
141 |
$this->background_handle_virtual_products_variations = new Background_Handle_Virtual_Products_Variations();
|
142 |
+
|
143 |
+
}
|
144 |
+
|
145 |
+
if ( 'yes' !== get_option( 'wc_facebook_background_remove_duplicate_visibility_meta_complete', 'no' ) ) {
|
146 |
+
|
147 |
+
require_once __DIR__ . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php';
|
148 |
+
|
149 |
+
$this->background_remove_duplicate_visibility_meta = new Background_Remove_Duplicate_Visibility_Meta();
|
150 |
}
|
151 |
|
152 |
$this->connection_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\Connection( $this );
|
512 |
}
|
513 |
|
514 |
|
515 |
+
/**
|
516 |
+
* Gets the background remove duplicate visibility meta data handler instance.
|
517 |
+
*
|
518 |
+
* @since 2.0.3
|
519 |
+
*
|
520 |
+
* @return Background_Remove_Duplicate_Visibility_Meta
|
521 |
+
*/
|
522 |
+
public function get_background_remove_duplicate_visibility_meta_instance() {
|
523 |
+
|
524 |
+
return $this->background_remove_duplicate_visibility_meta;
|
525 |
+
}
|
526 |
+
|
527 |
+
|
528 |
/**
|
529 |
* Gets the products sync handler.
|
530 |
*
|
facebook-commerce-events-tracker.php
CHANGED
@@ -29,10 +29,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
29 |
|
30 |
/** @var Event search event instance */
|
31 |
private $search_event;
|
|
|
|
|
|
|
|
|
32 |
|
33 |
-
|
34 |
-
public function __construct( $user_info ) {
|
35 |
$this->pixel = new WC_Facebookcommerce_Pixel( $user_info );
|
|
|
|
|
36 |
|
37 |
add_action( 'wp_head', array( $this, 'apply_filters' ) );
|
38 |
|
@@ -170,6 +175,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
170 |
'content_type' => $content_type,
|
171 |
'contents' => $contents,
|
172 |
],
|
|
|
173 |
];
|
174 |
|
175 |
$event = new Event( $event_data );
|
@@ -270,6 +276,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
270 |
'value' => Framework\SV_WC_Helper::number_format( $total_value ),
|
271 |
'currency' => get_woocommerce_currency(),
|
272 |
],
|
|
|
273 |
];
|
274 |
|
275 |
$this->search_event = new Event( $event_data );
|
@@ -329,19 +336,22 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
329 |
'content_name' => $product->get_title(),
|
330 |
'content_ids' => wp_json_encode( \WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) ),
|
331 |
'content_type' => $content_type,
|
332 |
-
'contents' =>
|
333 |
[
|
334 |
-
|
335 |
-
|
|
|
|
|
336 |
]
|
337 |
-
|
338 |
'content_category' => $categories['name'],
|
339 |
'value' => $product->get_price(),
|
340 |
'currency' => get_woocommerce_currency(),
|
341 |
],
|
|
|
342 |
];
|
343 |
|
344 |
-
$event = new
|
345 |
|
346 |
$this->send_api_event( $event );
|
347 |
|
@@ -385,6 +395,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
385 |
'value' => $this->get_cart_total(),
|
386 |
'currency' => get_woocommerce_currency(),
|
387 |
],
|
|
|
388 |
];
|
389 |
|
390 |
$event = new SkyVerge\WooCommerce\Facebook\Events\Event( $event_data );
|
@@ -617,6 +628,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
617 |
'value' => $this->get_cart_total(),
|
618 |
'currency' => get_woocommerce_currency(),
|
619 |
],
|
|
|
620 |
];
|
621 |
|
622 |
// if there is only one item in the cart, send its first category
|
@@ -715,7 +727,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
715 |
$contents[] = $content;
|
716 |
}
|
717 |
}
|
718 |
-
|
719 |
$event_data = [
|
720 |
'event_name' => $event_name,
|
721 |
'custom_data' => [
|
@@ -726,6 +738,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
726 |
'value' => $order->get_total(),
|
727 |
'currency' => get_woocommerce_currency(),
|
728 |
],
|
|
|
729 |
];
|
730 |
|
731 |
$event = new Event( $event_data );
|
@@ -772,6 +785,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
772 |
'value' => $subscription->get_total(),
|
773 |
'currency' => get_woocommerce_currency(),
|
774 |
],
|
|
|
775 |
];
|
776 |
|
777 |
$event = new Event( $event_data );
|
@@ -831,6 +845,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
831 |
* @return bool
|
832 |
*/
|
833 |
protected function send_api_event( Event $event ) {
|
|
|
834 |
|
835 |
try {
|
836 |
|
@@ -958,6 +973,48 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
|
|
958 |
return WC()->cart ? WC()->cart->total : 0;
|
959 |
}
|
960 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
961 |
|
962 |
}
|
963 |
|
29 |
|
30 |
/** @var Event search event instance */
|
31 |
private $search_event;
|
32 |
+
/** @var array with events tracked */
|
33 |
+
private $tracked_events;
|
34 |
+
/** @var AAMSettings aam settings instance, used to filter advanced matching fields*/
|
35 |
+
private $aam_settings;
|
36 |
|
37 |
+
public function __construct( $user_info, $aam_settings ) {
|
|
|
38 |
$this->pixel = new WC_Facebookcommerce_Pixel( $user_info );
|
39 |
+
$this->aam_settings = $aam_settings;
|
40 |
+
$this->tracked_events = array();
|
41 |
|
42 |
add_action( 'wp_head', array( $this, 'apply_filters' ) );
|
43 |
|
175 |
'content_type' => $content_type,
|
176 |
'contents' => $contents,
|
177 |
],
|
178 |
+
'user_data' => $this->pixel->get_user_info()
|
179 |
];
|
180 |
|
181 |
$event = new Event( $event_data );
|
276 |
'value' => Framework\SV_WC_Helper::number_format( $total_value ),
|
277 |
'currency' => get_woocommerce_currency(),
|
278 |
],
|
279 |
+
'user_data' => $this->pixel->get_user_info()
|
280 |
];
|
281 |
|
282 |
$this->search_event = new Event( $event_data );
|
336 |
'content_name' => $product->get_title(),
|
337 |
'content_ids' => wp_json_encode( \WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) ),
|
338 |
'content_type' => $content_type,
|
339 |
+
'contents' => wp_json_encode(
|
340 |
[
|
341 |
+
[
|
342 |
+
'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
|
343 |
+
'quantity' => 1,
|
344 |
+
]
|
345 |
]
|
346 |
+
),
|
347 |
'content_category' => $categories['name'],
|
348 |
'value' => $product->get_price(),
|
349 |
'currency' => get_woocommerce_currency(),
|
350 |
],
|
351 |
+
'user_data' => $this->pixel->get_user_info(),
|
352 |
];
|
353 |
|
354 |
+
$event = new Event( $event_data );
|
355 |
|
356 |
$this->send_api_event( $event );
|
357 |
|
395 |
'value' => $this->get_cart_total(),
|
396 |
'currency' => get_woocommerce_currency(),
|
397 |
],
|
398 |
+
'user_data' => $this->pixel->get_user_info(),
|
399 |
];
|
400 |
|
401 |
$event = new SkyVerge\WooCommerce\Facebook\Events\Event( $event_data );
|
628 |
'value' => $this->get_cart_total(),
|
629 |
'currency' => get_woocommerce_currency(),
|
630 |
],
|
631 |
+
'user_data' => $this->pixel->get_user_info()
|
632 |
];
|
633 |
|
634 |
// if there is only one item in the cart, send its first category
|
727 |
$contents[] = $content;
|
728 |
}
|
729 |
}
|
730 |
+
// Advanced matching information is extracted from the order
|
731 |
$event_data = [
|
732 |
'event_name' => $event_name,
|
733 |
'custom_data' => [
|
738 |
'value' => $order->get_total(),
|
739 |
'currency' => get_woocommerce_currency(),
|
740 |
],
|
741 |
+
'user_data' => $this->get_user_data_from_billing_address($order)
|
742 |
];
|
743 |
|
744 |
$event = new Event( $event_data );
|
785 |
'value' => $subscription->get_total(),
|
786 |
'currency' => get_woocommerce_currency(),
|
787 |
],
|
788 |
+
'user_data' => $this->pixel->get_user_info()
|
789 |
];
|
790 |
|
791 |
$event = new Event( $event_data );
|
845 |
* @return bool
|
846 |
*/
|
847 |
protected function send_api_event( Event $event ) {
|
848 |
+
$this->tracked_events[] = $event;
|
849 |
|
850 |
try {
|
851 |
|
973 |
return WC()->cart ? WC()->cart->total : 0;
|
974 |
}
|
975 |
|
976 |
+
/**
|
977 |
+
* Gets advanced matching information from a given order
|
978 |
+
*
|
979 |
+
* @since 2.0.3
|
980 |
+
*
|
981 |
+
* @return array
|
982 |
+
*/
|
983 |
+
private function get_user_data_from_billing_address($order) {
|
984 |
+
if($this->aam_settings == null || !$this->aam_settings->get_enable_automatic_matching() ){
|
985 |
+
return array();
|
986 |
+
}
|
987 |
+
$user_data= array();
|
988 |
+
$user_data['fn'] = $order->get_billing_first_name();
|
989 |
+
$user_data['ln'] = $order->get_billing_last_name();
|
990 |
+
$user_data['em'] = $order->get_billing_email();
|
991 |
+
// get_user_id() returns 0 if the current user is a guest
|
992 |
+
$user_data['external_id'] = $order->get_user_id() === 0 ? null : strval($order->get_user_id());
|
993 |
+
$user_data['zp'] = $order->get_billing_postcode();
|
994 |
+
$user_data['st'] = $order->get_billing_state();
|
995 |
+
// We can use country as key because this information is for CAPI events only
|
996 |
+
$user_data['country'] = $order->get_billing_country();
|
997 |
+
$user_data['ct'] = $order->get_billing_city();
|
998 |
+
$user_data['ph'] = $order->get_billing_phone();
|
999 |
+
// The fields contain country, so we do not need to add a condition
|
1000 |
+
foreach ($user_data as $field => $value) {
|
1001 |
+
if( $value === null || $value === '' ||
|
1002 |
+
!in_array($field, $this->aam_settings->get_enabled_automatic_matching_fields())
|
1003 |
+
){
|
1004 |
+
unset($user_data[$field]);
|
1005 |
+
}
|
1006 |
+
}
|
1007 |
+
return $user_data;
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
/**
|
1011 |
+
* Gets the events tracked by this object
|
1012 |
+
*
|
1013 |
+
* @return array
|
1014 |
+
*/
|
1015 |
+
public function get_tracked_events(){
|
1016 |
+
return $this->tracked_events;
|
1017 |
+
}
|
1018 |
|
1019 |
}
|
1020 |
|
facebook-commerce-pixel-event.php
CHANGED
@@ -415,11 +415,13 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
|
|
415 |
}
|
416 |
|
417 |
if ( ! empty( $event_id ) ) {
|
418 |
-
|
419 |
$event = sprintf(
|
420 |
"/* %s Facebook Integration Event Tracking */\n" .
|
|
|
421 |
"fbq('%s', '%s', %s, %s);",
|
422 |
WC_Facebookcommerce_Utils::getIntegrationName(),
|
|
|
|
|
423 |
esc_js( $method ),
|
424 |
esc_js( $event_name ),
|
425 |
json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
|
@@ -430,8 +432,11 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
|
|
430 |
|
431 |
$event = sprintf(
|
432 |
"/* %s Facebook Integration Event Tracking */\n" .
|
|
|
433 |
"fbq('%s', '%s', %s);",
|
434 |
WC_Facebookcommerce_Utils::getIntegrationName(),
|
|
|
|
|
435 |
esc_js( $method ),
|
436 |
esc_js( $event_name ),
|
437 |
json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
|
@@ -626,7 +631,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
|
|
626 |
return '';
|
627 |
}
|
628 |
|
629 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
}
|
631 |
|
632 |
endif;
|
415 |
}
|
416 |
|
417 |
if ( ! empty( $event_id ) ) {
|
|
|
418 |
$event = sprintf(
|
419 |
"/* %s Facebook Integration Event Tracking */\n" .
|
420 |
+
"fbq('set', 'agent', '%s', '%s');\n".
|
421 |
"fbq('%s', '%s', %s, %s);",
|
422 |
WC_Facebookcommerce_Utils::getIntegrationName(),
|
423 |
+
Event::get_platform_identifier(),
|
424 |
+
self::get_pixel_id(),
|
425 |
esc_js( $method ),
|
426 |
esc_js( $event_name ),
|
427 |
json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
|
432 |
|
433 |
$event = sprintf(
|
434 |
"/* %s Facebook Integration Event Tracking */\n" .
|
435 |
+
"fbq('set', 'agent', '%s', '%s');\n".
|
436 |
"fbq('%s', '%s', %s);",
|
437 |
WC_Facebookcommerce_Utils::getIntegrationName(),
|
438 |
+
Event::get_platform_identifier(),
|
439 |
+
self::get_pixel_id(),
|
440 |
esc_js( $method ),
|
441 |
esc_js( $event_name ),
|
442 |
json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
|
631 |
return '';
|
632 |
}
|
633 |
|
634 |
+
/**
|
635 |
+
* Gets the logged in user info
|
636 |
+
*
|
637 |
+
* @return string[]
|
638 |
+
*/
|
639 |
+
public function get_user_info(){
|
640 |
+
return $this->user_info;
|
641 |
+
}
|
642 |
}
|
643 |
|
644 |
endif;
|
facebook-commerce.php
CHANGED
@@ -12,6 +12,7 @@ use SkyVerge\WooCommerce\Facebook\Admin;
|
|
12 |
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
|
13 |
use SkyVerge\WooCommerce\Facebook\Products;
|
14 |
use SkyVerge\WooCommerce\Facebook\Products\Feed;
|
|
|
15 |
|
16 |
if ( ! defined( 'ABSPATH' ) ) {
|
17 |
exit; // Exit if accessed directly
|
@@ -412,8 +413,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
|
|
412 |
add_action( 'wc_facebook_generate_product_catalog_feed', [ $this, 'handle_generate_product_catalog_feed' ] );
|
413 |
|
414 |
if ( $this->get_facebook_pixel_id() ) {
|
415 |
-
$
|
416 |
-
$
|
|
|
417 |
}
|
418 |
|
419 |
// initialize the messenger chat features
|
@@ -423,6 +425,48 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
|
|
423 |
] );
|
424 |
}
|
425 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
426 |
public function load_background_sync_process() {
|
427 |
// Attempt to load background processing (Woo 3.x.x only)
|
428 |
include_once 'includes/fbbackground.php';
|
12 |
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
|
13 |
use SkyVerge\WooCommerce\Facebook\Products;
|
14 |
use SkyVerge\WooCommerce\Facebook\Products\Feed;
|
15 |
+
use SkyVerge\WooCommerce\Facebook\Events\AAMSettings;
|
16 |
|
17 |
if ( ! defined( 'ABSPATH' ) ) {
|
18 |
exit; // Exit if accessed directly
|
413 |
add_action( 'wc_facebook_generate_product_catalog_feed', [ $this, 'handle_generate_product_catalog_feed' ] );
|
414 |
|
415 |
if ( $this->get_facebook_pixel_id() ) {
|
416 |
+
$aam_settings = $this->load_aam_settings_of_pixel();
|
417 |
+
$user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
|
418 |
+
$this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info, $aam_settings );
|
419 |
}
|
420 |
|
421 |
// initialize the messenger chat features
|
425 |
] );
|
426 |
}
|
427 |
|
428 |
+
/**
|
429 |
+
* Returns the Automatic advanced matching of this pixel
|
430 |
+
*
|
431 |
+
* @since 2.0.3
|
432 |
+
*
|
433 |
+
* @return AAMSettings
|
434 |
+
*/
|
435 |
+
private function load_aam_settings_of_pixel() {
|
436 |
+
$installed_pixel = $this->get_facebook_pixel_id();
|
437 |
+
// If no pixel is installed, reading the DB is not needed
|
438 |
+
if(!$installed_pixel ){
|
439 |
+
return null;
|
440 |
+
}
|
441 |
+
$config_key = 'wc_facebook_aam_settings';
|
442 |
+
$saved_value = get_transient( $config_key );
|
443 |
+
$refresh_interval = 20*MINUTE_IN_SECONDS;
|
444 |
+
$aam_settings = null;
|
445 |
+
// If wc_facebook_aam_settings is present in the DB
|
446 |
+
// it is converted into an AAMSettings object
|
447 |
+
if( $saved_value !== false ){
|
448 |
+
$cached_aam_settings = new AAMSettings(json_decode($saved_value, true));
|
449 |
+
// This condition is added because
|
450 |
+
// it is possible that the AAMSettings saved do not belong to the current
|
451 |
+
// installed pixel
|
452 |
+
// because the admin could have changed the connection to Facebook
|
453 |
+
// during the refresh interval
|
454 |
+
if($cached_aam_settings->get_pixel_id() == $installed_pixel){
|
455 |
+
$aam_settings = $cached_aam_settings;
|
456 |
+
}
|
457 |
+
}
|
458 |
+
// If the settings are not present or invalid
|
459 |
+
// they are fetched from Facebook domain
|
460 |
+
// and cached in WP database if they are not null
|
461 |
+
if(!$aam_settings){
|
462 |
+
$aam_settings = AAMSettings::build_from_pixel_id( $installed_pixel );
|
463 |
+
if($aam_settings){
|
464 |
+
set_transient($config_key, strval($aam_settings), $refresh_interval);
|
465 |
+
}
|
466 |
+
}
|
467 |
+
return $aam_settings;
|
468 |
+
}
|
469 |
+
|
470 |
public function load_background_sync_process() {
|
471 |
// Attempt to load background processing (Woo 3.x.x only)
|
472 |
include_once 'includes/fbbackground.php';
|
facebook-for-woocommerce.php
CHANGED
@@ -10,7 +10,7 @@
|
|
10 |
* 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.
|
11 |
* Author: Facebook
|
12 |
* Author URI: https://www.facebook.com/
|
13 |
-
* Version: 2.0.
|
14 |
* Text Domain: facebook-for-woocommerce
|
15 |
* WC requires at least: 3.5.0
|
16 |
* WC tested up to: 4.5.2
|
10 |
* 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.
|
11 |
* Author: Facebook
|
12 |
* Author URI: https://www.facebook.com/
|
13 |
+
* Version: 2.0.3
|
14 |
* Text Domain: facebook-for-woocommerce
|
15 |
* WC requires at least: 3.5.0
|
16 |
* WC tested up to: 4.5.2
|
i18n/languages/facebook-for-woocommerce.pot
CHANGED
@@ -2,10 +2,10 @@
|
|
2 |
# This file is distributed under the same license as the Facebook for WooCommerce package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Facebook for WooCommerce 2.0.
|
6 |
"Report-Msgid-Bugs-To: "
|
7 |
"https://woocommerce.com/my-account/marketplace-ticket-form/\n"
|
8 |
-
"POT-Creation-Date: 2020-
|
9 |
"MIME-Version: 1.0\n"
|
10 |
"Content-Type: text/plain; charset=utf-8\n"
|
11 |
"Content-Transfer-Encoding: 8bit\n"
|
@@ -13,7 +13,7 @@ msgstr ""
|
|
13 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
14 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
15 |
|
16 |
-
#: class-wc-facebookcommerce.php:
|
17 |
#. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
|
18 |
#. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
|
19 |
msgid ""
|
@@ -21,7 +21,7 @@ msgid ""
|
|
21 |
"Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!"
|
22 |
msgstr ""
|
23 |
|
24 |
-
#: class-wc-facebookcommerce.php:
|
25 |
#. translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link
|
26 |
#. HTML tag
|
27 |
msgid ""
|
@@ -29,7 +29,7 @@ msgid ""
|
|
29 |
"under %1$sWooCommerce > Facebook%2$s."
|
30 |
msgstr ""
|
31 |
|
32 |
-
#: class-wc-facebookcommerce.php:
|
33 |
#. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
|
34 |
#. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
|
35 |
msgid ""
|
@@ -37,7 +37,7 @@ msgid ""
|
|
37 |
"configuration, %3$scomplete the setup steps%4$s."
|
38 |
msgstr ""
|
39 |
|
40 |
-
#: class-wc-facebookcommerce.php:
|
41 |
#. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
|
42 |
#. <a> tag, %4$s - </a> tag
|
43 |
msgid ""
|
@@ -46,7 +46,7 @@ msgid ""
|
|
46 |
"Connection%4$s area."
|
47 |
msgstr ""
|
48 |
|
49 |
-
#: class-wc-facebookcommerce.php:
|
50 |
#. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
|
51 |
#. <a> tag, %4$s - </a> tag
|
52 |
msgid ""
|
@@ -55,7 +55,7 @@ msgid ""
|
|
55 |
"products."
|
56 |
msgstr ""
|
57 |
|
58 |
-
#: class-wc-facebookcommerce.php:
|
59 |
msgid "Cannot create the API instance because the access token is missing."
|
60 |
msgstr ""
|
61 |
|
@@ -63,69 +63,69 @@ msgstr ""
|
|
63 |
msgid "Facebook for WooCommerce"
|
64 |
msgstr ""
|
65 |
|
66 |
-
#: facebook-commerce.php:
|
67 |
msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
|
68 |
msgstr ""
|
69 |
|
70 |
-
#: facebook-commerce.php:
|
71 |
msgid "Facebook ID:"
|
72 |
msgstr ""
|
73 |
|
74 |
-
#: facebook-commerce.php:
|
75 |
msgid "Variant IDs:"
|
76 |
msgstr ""
|
77 |
|
78 |
-
#: facebook-commerce.php:
|
79 |
msgid "Reset Facebook metadata"
|
80 |
msgstr ""
|
81 |
|
82 |
-
#: facebook-commerce.php:
|
83 |
msgid "Delete product(s) on Facebook"
|
84 |
msgstr ""
|
85 |
|
86 |
-
#: facebook-commerce.php:
|
87 |
msgid "This product is not yet synced to Facebook."
|
88 |
msgstr ""
|
89 |
|
90 |
-
#: facebook-commerce.php:
|
91 |
msgid "Nothing to update for product group for %1$s"
|
92 |
msgstr ""
|
93 |
|
94 |
-
#: facebook-commerce.php:
|
95 |
#. translators: Placeholders %1$s - original error message from Facebook API
|
96 |
msgid "There was an issue connecting to the Facebook API: %1$s"
|
97 |
msgstr ""
|
98 |
|
99 |
-
#: facebook-commerce.php:
|
100 |
msgid "Your connection has expired."
|
101 |
msgstr ""
|
102 |
|
103 |
-
#: facebook-commerce.php:
|
104 |
msgid ""
|
105 |
"Please click Manage connection > Advanced Options > Update Token to refresh "
|
106 |
"your connection to Facebook."
|
107 |
msgstr ""
|
108 |
|
109 |
-
#: facebook-commerce.php:
|
110 |
#. translators: Placeholders %s - error message
|
111 |
msgid "There was an error trying to sync the products to Facebook. %s"
|
112 |
msgstr ""
|
113 |
|
114 |
-
#: facebook-commerce.php:
|
115 |
msgid "Product sync is disabled."
|
116 |
msgstr ""
|
117 |
|
118 |
-
#: facebook-commerce.php:
|
119 |
msgid "The plugin is not configured or the Catalog ID is missing."
|
120 |
msgstr ""
|
121 |
|
122 |
-
#: facebook-commerce.php:
|
123 |
msgid ""
|
124 |
"A product sync is in progress. Please wait until the sync finishes before "
|
125 |
"starting a new one."
|
126 |
msgstr ""
|
127 |
|
128 |
-
#: facebook-commerce.php:
|
129 |
msgid ""
|
130 |
"We've detected that your Facebook Product Catalog is no longer valid. This "
|
131 |
"may happen if it was deleted, but could also be a temporary error. If the "
|
@@ -133,33 +133,33 @@ msgid ""
|
|
133 |
"and setup the plugin again."
|
134 |
msgstr ""
|
135 |
|
136 |
-
#: facebook-commerce.php:
|
137 |
msgid "We couldn't create the feed or upload the product information."
|
138 |
msgstr ""
|
139 |
|
140 |
-
#: facebook-commerce.php:
|
141 |
msgid "Hi! We're here to answer any questions you may have."
|
142 |
msgstr ""
|
143 |
|
144 |
-
#: facebook-commerce.php:
|
145 |
msgid "Facebook for WooCommerce error:"
|
146 |
msgstr ""
|
147 |
|
148 |
-
#: facebook-commerce.php:
|
149 |
msgid ""
|
150 |
"There was an error trying to retrieve information about the Facebook page: "
|
151 |
"%s"
|
152 |
msgstr ""
|
153 |
|
154 |
-
#: facebook-commerce.php:
|
155 |
msgid "Get started with Messenger Customer Chat"
|
156 |
msgstr ""
|
157 |
|
158 |
-
#: facebook-commerce.php:
|
159 |
msgid "Get started with Instagram Shopping"
|
160 |
msgstr ""
|
161 |
|
162 |
-
#: facebook-commerce.php:
|
163 |
#. translators: Placeholders %1$s - original error message from Facebook API
|
164 |
msgid "There was an issue connecting to the Facebook API: %s"
|
165 |
msgstr ""
|
2 |
# This file is distributed under the same license as the Facebook for WooCommerce package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Facebook for WooCommerce 2.0.3\n"
|
6 |
"Report-Msgid-Bugs-To: "
|
7 |
"https://woocommerce.com/my-account/marketplace-ticket-form/\n"
|
8 |
+
"POT-Creation-Date: 2020-10-02 23:23:52+00:00\n"
|
9 |
"MIME-Version: 1.0\n"
|
10 |
"Content-Type: text/plain; charset=utf-8\n"
|
11 |
"Content-Transfer-Encoding: 8bit\n"
|
13 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
14 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
15 |
|
16 |
+
#: class-wc-facebookcommerce.php:201
|
17 |
#. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
|
18 |
#. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
|
19 |
msgid ""
|
21 |
"Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!"
|
22 |
msgstr ""
|
23 |
|
24 |
+
#: class-wc-facebookcommerce.php:216
|
25 |
#. translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link
|
26 |
#. HTML tag
|
27 |
msgid ""
|
29 |
"under %1$sWooCommerce > Facebook%2$s."
|
30 |
msgstr ""
|
31 |
|
32 |
+
#: class-wc-facebookcommerce.php:231
|
33 |
#. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
|
34 |
#. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
|
35 |
msgid ""
|
37 |
"configuration, %3$scomplete the setup steps%4$s."
|
38 |
msgstr ""
|
39 |
|
40 |
+
#: class-wc-facebookcommerce.php:259
|
41 |
#. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
|
42 |
#. <a> tag, %4$s - </a> tag
|
43 |
msgid ""
|
46 |
"Connection%4$s area."
|
47 |
msgstr ""
|
48 |
|
49 |
+
#: class-wc-facebookcommerce.php:276
|
50 |
#. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
|
51 |
#. <a> tag, %4$s - </a> tag
|
52 |
msgid ""
|
55 |
"products."
|
56 |
msgstr ""
|
57 |
|
58 |
+
#: class-wc-facebookcommerce.php:329
|
59 |
msgid "Cannot create the API instance because the access token is missing."
|
60 |
msgstr ""
|
61 |
|
63 |
msgid "Facebook for WooCommerce"
|
64 |
msgstr ""
|
65 |
|
66 |
+
#: facebook-commerce.php:216
|
67 |
msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
|
68 |
msgstr ""
|
69 |
|
70 |
+
#: facebook-commerce.php:633
|
71 |
msgid "Facebook ID:"
|
72 |
msgstr ""
|
73 |
|
74 |
+
#: facebook-commerce.php:641
|
75 |
msgid "Variant IDs:"
|
76 |
msgstr ""
|
77 |
|
78 |
+
#: facebook-commerce.php:659
|
79 |
msgid "Reset Facebook metadata"
|
80 |
msgstr ""
|
81 |
|
82 |
+
#: facebook-commerce.php:664
|
83 |
msgid "Delete product(s) on Facebook"
|
84 |
msgstr ""
|
85 |
|
86 |
+
#: facebook-commerce.php:672
|
87 |
msgid "This product is not yet synced to Facebook."
|
88 |
msgstr ""
|
89 |
|
90 |
+
#: facebook-commerce.php:1418
|
91 |
msgid "Nothing to update for product group for %1$s"
|
92 |
msgstr ""
|
93 |
|
94 |
+
#: facebook-commerce.php:1684
|
95 |
#. translators: Placeholders %1$s - original error message from Facebook API
|
96 |
msgid "There was an issue connecting to the Facebook API: %1$s"
|
97 |
msgstr ""
|
98 |
|
99 |
+
#: facebook-commerce.php:2021
|
100 |
msgid "Your connection has expired."
|
101 |
msgstr ""
|
102 |
|
103 |
+
#: facebook-commerce.php:2021
|
104 |
msgid ""
|
105 |
"Please click Manage connection > Advanced Options > Update Token to refresh "
|
106 |
"your connection to Facebook."
|
107 |
msgstr ""
|
108 |
|
109 |
+
#: facebook-commerce.php:2028
|
110 |
#. translators: Placeholders %s - error message
|
111 |
msgid "There was an error trying to sync the products to Facebook. %s"
|
112 |
msgstr ""
|
113 |
|
114 |
+
#: facebook-commerce.php:2051 facebook-commerce.php:2224
|
115 |
msgid "Product sync is disabled."
|
116 |
msgstr ""
|
117 |
|
118 |
+
#: facebook-commerce.php:2058 facebook-commerce.php:2231
|
119 |
msgid "The plugin is not configured or the Catalog ID is missing."
|
120 |
msgstr ""
|
121 |
|
122 |
+
#: facebook-commerce.php:2081
|
123 |
msgid ""
|
124 |
"A product sync is in progress. Please wait until the sync finishes before "
|
125 |
"starting a new one."
|
126 |
msgstr ""
|
127 |
|
128 |
+
#: facebook-commerce.php:2093 facebook-commerce.php:2245
|
129 |
msgid ""
|
130 |
"We've detected that your Facebook Product Catalog is no longer valid. This "
|
131 |
"may happen if it was deleted, but could also be a temporary error. If the "
|
133 |
"and setup the plugin again."
|
134 |
msgstr ""
|
135 |
|
136 |
+
#: facebook-commerce.php:2269
|
137 |
msgid "We couldn't create the feed or upload the product information."
|
138 |
msgstr ""
|
139 |
|
140 |
+
#: facebook-commerce.php:2736
|
141 |
msgid "Hi! We're here to answer any questions you may have."
|
142 |
msgstr ""
|
143 |
|
144 |
+
#: facebook-commerce.php:3080
|
145 |
msgid "Facebook for WooCommerce error:"
|
146 |
msgstr ""
|
147 |
|
148 |
+
#: facebook-commerce.php:3162
|
149 |
msgid ""
|
150 |
"There was an error trying to retrieve information about the Facebook page: "
|
151 |
"%s"
|
152 |
msgstr ""
|
153 |
|
154 |
+
#: facebook-commerce.php:3213
|
155 |
msgid "Get started with Messenger Customer Chat"
|
156 |
msgstr ""
|
157 |
|
158 |
+
#: facebook-commerce.php:3214
|
159 |
msgid "Get started with Instagram Shopping"
|
160 |
msgstr ""
|
161 |
|
162 |
+
#: facebook-commerce.php:3449
|
163 |
#. translators: Placeholders %1$s - original error message from Facebook API
|
164 |
msgid "There was an issue connecting to the Facebook API: %s"
|
165 |
msgstr ""
|
includes/Events/AAMSettings.php
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace SkyVerge\WooCommerce\Facebook\Events;
|
12 |
+
|
13 |
+
defined( 'ABSPATH' ) or exit;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Base Automatic advanced matching settings object
|
17 |
+
*
|
18 |
+
*/
|
19 |
+
class AAMSettings {
|
20 |
+
|
21 |
+
/** @var bool is enable automatic matching enabled for this pixel */
|
22 |
+
private $enable_automatic_matching;
|
23 |
+
|
24 |
+
/** @var string[] advanced matching fields to extract when $enable_automatic_matching is true*/
|
25 |
+
private $enabled_automatic_matching_fields;
|
26 |
+
|
27 |
+
/** @var string pixel id associated with this settings*/
|
28 |
+
private $pixel_id;
|
29 |
+
|
30 |
+
const SIGNALS_JSON_CONFIG_PATH = 'signals/config/json';
|
31 |
+
|
32 |
+
const CONNECT_FACEBOOK_DOMAIN = 'https://connect.facebook.net/';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* AAMSettings constructor
|
36 |
+
*
|
37 |
+
* @since 2.0.3
|
38 |
+
*
|
39 |
+
* @param array $data
|
40 |
+
*/
|
41 |
+
public function __construct( $data = array() ) {
|
42 |
+
$this->enable_automatic_matching = isset($data['enableAutomaticMatching']) ? $data['enableAutomaticMatching'] : null;
|
43 |
+
$this->enabled_automatic_matching_fields = isset($data['enabledAutomaticMatchingFields']) ? $data['enabledAutomaticMatchingFields'] : null;
|
44 |
+
$this->pixel_id = isset($data['pixelId']) ? $data['pixelId'] : null;
|
45 |
+
}
|
46 |
+
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Gets the URL used to retrieve the advanced matching settings for the given pixel ID.
|
50 |
+
*
|
51 |
+
* @since 2.0.3
|
52 |
+
*
|
53 |
+
* @param string $pixel_id pixel ID
|
54 |
+
* @return string
|
55 |
+
*/
|
56 |
+
public static function get_url( $pixel_id ){
|
57 |
+
|
58 |
+
return self::CONNECT_FACEBOOK_DOMAIN.self::SIGNALS_JSON_CONFIG_PATH.'/'.$pixel_id;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Factory method that builds an AAMSettings object given a pixel id
|
63 |
+
* by sending a request to connect.facebook.net domain
|
64 |
+
*
|
65 |
+
* @since 2.0.3
|
66 |
+
*
|
67 |
+
* @param string $pixel_id
|
68 |
+
*/
|
69 |
+
public static function build_from_pixel_id( $pixel_id ){
|
70 |
+
$url = self::get_url($pixel_id);
|
71 |
+
$response = wp_remote_get($url);
|
72 |
+
if ( is_wp_error( $response ) ) {
|
73 |
+
return null;
|
74 |
+
}
|
75 |
+
else{
|
76 |
+
$response_body = json_decode( wp_remote_retrieve_body( $response ), true );
|
77 |
+
if (!array_key_exists('errorMessage', $response_body)){
|
78 |
+
$response_body['matchingConfig']['pixelId'] = $pixel_id;
|
79 |
+
return new AAMSettings($response_body['matchingConfig']);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
return null;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Gets enable automatic matching flag
|
87 |
+
*
|
88 |
+
* @since 2.0.3
|
89 |
+
*
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public function get_enable_automatic_matching(){
|
93 |
+
return $this->enable_automatic_matching;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Gets enabled automatic matching fields array
|
98 |
+
*
|
99 |
+
* @since 2.0.3
|
100 |
+
*
|
101 |
+
* @return string[]
|
102 |
+
*/
|
103 |
+
public function get_enabled_automatic_matching_fields(){
|
104 |
+
return $this->enabled_automatic_matching_fields;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Gets the pixel id
|
109 |
+
*
|
110 |
+
* @since 2.0.3
|
111 |
+
*
|
112 |
+
* @return string
|
113 |
+
*/
|
114 |
+
public function get_pixel_id(){
|
115 |
+
return $this->pixel_id;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Sets the enable automatic matching flag
|
120 |
+
*
|
121 |
+
* @since 2.0.3
|
122 |
+
*
|
123 |
+
* @return AAMSettings
|
124 |
+
*/
|
125 |
+
public function set_enable_automatic_matching($enable_automatic_matching){
|
126 |
+
$this->enable_automatic_matching = $enable_automatic_matching;
|
127 |
+
return $this;
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Sets the enabled automatic matching fields flag
|
132 |
+
*
|
133 |
+
* @since 2.0.3
|
134 |
+
*
|
135 |
+
* @return AAMSettings
|
136 |
+
*/
|
137 |
+
public function set_enabled_automatic_matching_fields($enabled_automatic_matching_fields){
|
138 |
+
$this->enabled_automatic_matching_fields = $enabled_automatic_matching_fields;
|
139 |
+
return $this;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Sets the pixel id
|
144 |
+
*
|
145 |
+
* @since 2.0.3
|
146 |
+
*
|
147 |
+
* @return AAMSettings
|
148 |
+
*/
|
149 |
+
public function set_pixel_id($pixel_id){
|
150 |
+
$this->pixel_id = $pixel_id;
|
151 |
+
return $this;
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Returns the json string representing this object
|
156 |
+
*
|
157 |
+
* @since 2.0.3
|
158 |
+
*
|
159 |
+
* @return string
|
160 |
+
*/
|
161 |
+
public function __toString(){
|
162 |
+
return json_encode(
|
163 |
+
array(
|
164 |
+
'enableAutomaticMatching' => $this->enable_automatic_matching,
|
165 |
+
'enabledAutomaticMatchingFields' => $this->enabled_automatic_matching_fields,
|
166 |
+
'pixelId' => $this->pixel_id
|
167 |
+
)
|
168 |
+
);
|
169 |
+
}
|
170 |
+
}
|
includes/Events/Event.php
CHANGED
@@ -109,15 +109,47 @@ class Event {
|
|
109 |
* @param array $data user data
|
110 |
*/
|
111 |
protected function prepare_user_data( $data ) {
|
112 |
-
|
113 |
$this->data['user_data'] = wp_parse_args( $data, [
|
114 |
'client_ip_address' => $this->get_client_ip(),
|
115 |
'client_user_agent' => $this->get_client_user_agent(),
|
116 |
'click_id' => $this->get_click_id(),
|
117 |
'browser_id' => $this->get_browser_id(),
|
118 |
] );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
/**
|
123 |
* Generates a UUIDv4 unique ID for the event.
|
@@ -328,5 +360,4 @@ class Event {
|
|
328 |
return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : [];
|
329 |
}
|
330 |
|
331 |
-
|
332 |
}
|
109 |
* @param array $data user data
|
110 |
*/
|
111 |
protected function prepare_user_data( $data ) {
|
|
|
112 |
$this->data['user_data'] = wp_parse_args( $data, [
|
113 |
'client_ip_address' => $this->get_client_ip(),
|
114 |
'client_user_agent' => $this->get_client_user_agent(),
|
115 |
'click_id' => $this->get_click_id(),
|
116 |
'browser_id' => $this->get_browser_id(),
|
117 |
] );
|
118 |
+
|
119 |
+
// Country key is not the same in pixel and CAPI events, see:
|
120 |
+
// https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
|
121 |
+
// https://developers.facebook.com/docs/marketing-api/conversions-api/parameters
|
122 |
+
if(array_key_exists('cn', $this->data['user_data'])){
|
123 |
+
$country = $this->data['user_data']['cn'];
|
124 |
+
$this->data['user_data']['country'] = $country;
|
125 |
+
unset($this->data['user_data']['cn']);
|
126 |
+
}
|
127 |
+
|
128 |
+
$this->data['user_data'] = Normalizer::normalize_array( $this->data['user_data'], false );
|
129 |
+
|
130 |
+
$this->data['user_data'] = $this->hash_pii_data( $this->data['user_data'] );
|
131 |
}
|
132 |
|
133 |
+
/**
|
134 |
+
* Hashes the user data
|
135 |
+
*
|
136 |
+
* @see https://developers.facebook.com/docs/marketing-api/server-side-api/parameters/user-data
|
137 |
+
*
|
138 |
+
* @since 2.0.3
|
139 |
+
*
|
140 |
+
* @param array $user_data user data
|
141 |
+
*
|
142 |
+
* @return array
|
143 |
+
*/
|
144 |
+
protected function hash_pii_data( $user_data ){
|
145 |
+
$keys_to_hash = ['em', 'fn', 'ln', 'ph', 'ct', 'st', 'zp', 'country', 'external_id'];
|
146 |
+
foreach( $keys_to_hash as $key ){
|
147 |
+
if(array_key_exists($key, $user_data)){
|
148 |
+
$user_data[$key] = hash('sha256', $user_data[$key], false);
|
149 |
+
}
|
150 |
+
}
|
151 |
+
return $user_data;
|
152 |
+
}
|
153 |
|
154 |
/**
|
155 |
* Generates a UUIDv4 unique ID for the event.
|
360 |
return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : [];
|
361 |
}
|
362 |
|
|
|
363 |
}
|
includes/Events/Normalizer.php
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace SkyVerge\WooCommerce\Facebook\Events;
|
12 |
+
|
13 |
+
use InvalidArgumentException;
|
14 |
+
|
15 |
+
defined( 'ABSPATH' ) or exit;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Normalizer class.
|
19 |
+
*
|
20 |
+
*/
|
21 |
+
class Normalizer {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Normalizes $data according to its type
|
25 |
+
*
|
26 |
+
* @since 2.0.3
|
27 |
+
*
|
28 |
+
* @param string $field to be normalized.
|
29 |
+
* @param string $data value to be normalized
|
30 |
+
* @return string
|
31 |
+
* @throws InvalidArgumentException
|
32 |
+
*/
|
33 |
+
public static function normalize($field, $data) {
|
34 |
+
if ($data == null || strlen($data) == 0) {
|
35 |
+
return null;
|
36 |
+
}
|
37 |
+
|
38 |
+
$data = trim(strtolower($data));
|
39 |
+
$normalized_data = $data;
|
40 |
+
|
41 |
+
switch ($field) {
|
42 |
+
case 'em':
|
43 |
+
$normalized_data = Normalizer::normalizeEmail($data);
|
44 |
+
break;
|
45 |
+
|
46 |
+
case 'ph':
|
47 |
+
$normalized_data = Normalizer::normalizePhone($data);
|
48 |
+
break;
|
49 |
+
|
50 |
+
case 'zp':
|
51 |
+
$normalized_data = Normalizer::normalizeZipCode($data);
|
52 |
+
break;
|
53 |
+
|
54 |
+
case 'ct':
|
55 |
+
$normalized_data = Normalizer::normalizeCity($data);
|
56 |
+
break;
|
57 |
+
|
58 |
+
case 'st':
|
59 |
+
$normalized_data = Normalizer::normalizeState($data);
|
60 |
+
break;
|
61 |
+
|
62 |
+
case 'country':
|
63 |
+
$normalized_data = Normalizer::normalizeCountry($data);
|
64 |
+
break;
|
65 |
+
|
66 |
+
case 'cn':
|
67 |
+
$normalized_data = Normalizer::normalizeCountry($data);
|
68 |
+
break;
|
69 |
+
|
70 |
+
default:
|
71 |
+
}
|
72 |
+
|
73 |
+
return $normalized_data;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Normalizes an array containing user data
|
78 |
+
*
|
79 |
+
* @since 2.0.3
|
80 |
+
*
|
81 |
+
* @param string[] array with user data to be normalized
|
82 |
+
* @return string[]
|
83 |
+
*/
|
84 |
+
public static function normalize_array($data, $is_pixel_data){
|
85 |
+
// Country is encoded as cn in Pixel events and country in CAPI events
|
86 |
+
$keys_to_normalize = ['fn', 'ln', 'em', 'ph', 'zp', 'ct', 'st'];
|
87 |
+
if($is_pixel_data){
|
88 |
+
$keys_to_normalize[] = 'cn';
|
89 |
+
}
|
90 |
+
else{
|
91 |
+
$keys_to_normalize[] = 'country';
|
92 |
+
}
|
93 |
+
foreach($keys_to_normalize as $key){
|
94 |
+
if(array_key_exists($key, $data)){
|
95 |
+
//If the data is invalid, it is erased from the array
|
96 |
+
try{
|
97 |
+
$data[$key] = self::normalize($key, $data[$key]);
|
98 |
+
}
|
99 |
+
catch(InvalidArgumentException $e){
|
100 |
+
unset($data[$key]);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
return $data;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Normalizes an email
|
109 |
+
*
|
110 |
+
* @since 2.0.3
|
111 |
+
*
|
112 |
+
* @param string $email Email address to be normalized.
|
113 |
+
* @return string
|
114 |
+
* @throws InvalidArgumentException
|
115 |
+
*/
|
116 |
+
private static function normalizeEmail($email) {
|
117 |
+
// Validates email against RFC 822
|
118 |
+
$result = filter_var($email, FILTER_SANITIZE_EMAIL);
|
119 |
+
|
120 |
+
if (!filter_var($result, FILTER_VALIDATE_EMAIL)) {
|
121 |
+
throw new InvalidArgumentException('Invalid email format for the passed email: ' . $email . 'Please check the passed email format.');
|
122 |
+
}
|
123 |
+
|
124 |
+
return $result;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Normalizes a city name
|
129 |
+
*
|
130 |
+
* @since 2.0.3
|
131 |
+
*
|
132 |
+
* @param string $city city name to be normalized.
|
133 |
+
* @return string
|
134 |
+
*/
|
135 |
+
private static function normalizeCity($city) {
|
136 |
+
return trim(preg_replace('/[0-9.\s\-()]/', '', $city));
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Normalizes a state code
|
141 |
+
*
|
142 |
+
* @since 2.0.3
|
143 |
+
*
|
144 |
+
* @param string $state state name to be normalized.
|
145 |
+
* @return string
|
146 |
+
*/
|
147 |
+
private static function normalizeState($state) {
|
148 |
+
return preg_replace('/[^a-z]/', '', $state);
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Normalizes a country code
|
153 |
+
*
|
154 |
+
* @since 2.0.3
|
155 |
+
*
|
156 |
+
* @param string $country country code to be normalized(ISO 3166-2).
|
157 |
+
* @return string
|
158 |
+
* @throws InvalidArgumentException
|
159 |
+
*/
|
160 |
+
private static function normalizeCountry($country) {
|
161 |
+
$result = preg_replace('/[^a-z]/i', '', $country);
|
162 |
+
|
163 |
+
if (strlen($result) != 2) {
|
164 |
+
throw new InvalidArgumentException('Invalid country format passed(' . $country . '). Country Code should be a two-letter ISO Country Code');
|
165 |
+
}
|
166 |
+
|
167 |
+
return $result;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Normalizes a zip code
|
172 |
+
*
|
173 |
+
* @since 2.0.3
|
174 |
+
*
|
175 |
+
* @param string $zip postal code to be normalized.
|
176 |
+
* @return string
|
177 |
+
*/
|
178 |
+
private static function normalizeZipCode($zip) {
|
179 |
+
// Removing the spaces from the zip code. Eg:
|
180 |
+
$zip = preg_replace('/[ ]/', '', $zip);
|
181 |
+
|
182 |
+
// If the code has more than one part, retain the first part.
|
183 |
+
$zip = explode('-', $zip)[0];
|
184 |
+
return $zip;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Normalizes a phone number
|
189 |
+
*
|
190 |
+
* @since 2.0.3
|
191 |
+
*
|
192 |
+
* @param string $phone phone number to be normalized.
|
193 |
+
* @return string
|
194 |
+
*/
|
195 |
+
private static function normalizePhone($phone) {
|
196 |
+
$result = trim(preg_replace('/[a-z()-]/', '', $phone));
|
197 |
+
|
198 |
+
if (Normalizer::isInternationalNumber($result)) {
|
199 |
+
$result = preg_replace('/[\-\s+]/', '', $result);
|
200 |
+
}
|
201 |
+
|
202 |
+
return $result;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Checks if a phone number is international
|
207 |
+
*
|
208 |
+
* @since 2.0.3
|
209 |
+
*
|
210 |
+
* @param string $phone_number Phone number to be normalized.
|
211 |
+
* @return bool
|
212 |
+
*/
|
213 |
+
private static function isInternationalNumber($phone_number) {
|
214 |
+
// Remove spaces and hyphens
|
215 |
+
$phone_number = preg_replace('/[\-\s]/', '', $phone_number);
|
216 |
+
|
217 |
+
// Strip + and up to 2 leading 0s
|
218 |
+
$phone_number = preg_replace('/^\+?0{0,2}/', '', $phone_number);
|
219 |
+
|
220 |
+
if (substr($phone_number, 0, 1) === '0') {
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
|
224 |
+
// International Phone number with country calling code.
|
225 |
+
$international_number_regex = '/^\d{1,4}\(?\d{2,3}\)?\d{4,}$/';
|
226 |
+
|
227 |
+
return preg_match($international_number_regex, $phone_number);
|
228 |
+
}
|
229 |
+
}
|
includes/Lifecycle.php
CHANGED
@@ -40,6 +40,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
|
|
40 |
'1.10.1',
|
41 |
'1.11.0',
|
42 |
'2.0.0',
|
|
|
43 |
];
|
44 |
}
|
45 |
|
@@ -266,4 +267,55 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
|
|
266 |
}
|
267 |
|
268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
}
|
40 |
'1.10.1',
|
41 |
'1.11.0',
|
42 |
'2.0.0',
|
43 |
+
'2.0.3',
|
44 |
];
|
45 |
}
|
46 |
|
267 |
}
|
268 |
|
269 |
|
270 |
+
/**
|
271 |
+
* Upgrades to version 2.0.3
|
272 |
+
*
|
273 |
+
* @since 2.0.3
|
274 |
+
*/
|
275 |
+
protected function upgrade_to_2_0_3() {
|
276 |
+
|
277 |
+
if ( ! $this->should_create_remove_duplicate_visibility_meta_background_job() ) {
|
278 |
+
return;
|
279 |
+
}
|
280 |
+
|
281 |
+
// if an unfinished job is stuck, give the handler a chance to complete it
|
282 |
+
if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
|
283 |
+
$handler->dispatch();
|
284 |
+
}
|
285 |
+
|
286 |
+
// create a job to remove duplicate visibility meta data entries
|
287 |
+
if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
|
288 |
+
|
289 |
+
// create_job() expects an non-empty array of attributes
|
290 |
+
$handler->create_job( [ 'created_at' => current_time( 'mysql' ) ] );
|
291 |
+
$handler->dispatch();
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Determines whether we need to run a background job to remove duplicate visibility meta.
|
298 |
+
*
|
299 |
+
* @since 2.0.3
|
300 |
+
*
|
301 |
+
* @return bool
|
302 |
+
*/
|
303 |
+
private function should_create_remove_duplicate_visibility_meta_background_job() {
|
304 |
+
|
305 |
+
// we should try to remove duplicate meta if the virtual product variations job ran
|
306 |
+
if ( 'yes' === get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) ) {
|
307 |
+
return true;
|
308 |
+
}
|
309 |
+
|
310 |
+
$handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance();
|
311 |
+
|
312 |
+
// the virtual product variations job is not marked as complete but there is at least one job in the database
|
313 |
+
if ( $handler && $handler->get_jobs() ) {
|
314 |
+
return true;
|
315 |
+
}
|
316 |
+
|
317 |
+
return false;
|
318 |
+
}
|
319 |
+
|
320 |
+
|
321 |
}
|
includes/Utilities/Background_Handle_Virtual_Products_Variations.php
CHANGED
@@ -132,11 +132,47 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
|
|
132 |
private function sync_and_hide() {
|
133 |
global $wpdb;
|
134 |
|
135 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
-
// get post IDs to update
|
138 |
$sql = "
|
139 |
-
SELECT DISTINCT
|
140 |
FROM {$wpdb->posts} AS posts
|
141 |
INNER JOIN {$wpdb->postmeta} AS virtual_meta ON ( posts.ID = virtual_meta.post_id AND virtual_meta.meta_key = '_virtual' AND virtual_meta.meta_value = 'yes' )
|
142 |
LEFT JOIN {$wpdb->postmeta} AS sync_meta ON ( posts.ID = sync_meta.post_id AND sync_meta.meta_key = '_wc_facebook_sync_enabled' )
|
@@ -147,40 +183,80 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
|
|
147 |
LIMIT 1000
|
148 |
";
|
149 |
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
|
152 |
if ( empty( $post_ids ) ) {
|
|
|
|
|
153 |
|
154 |
-
|
155 |
|
156 |
-
|
|
|
|
|
157 |
|
158 |
-
|
|
|
|
|
|
|
|
|
159 |
|
160 |
-
|
161 |
|
162 |
-
|
163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
-
$values_str = implode( ',', $values );
|
166 |
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
-
|
|
|
|
|
174 |
|
175 |
-
|
|
|
|
|
|
|
176 |
|
177 |
-
|
178 |
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
181 |
}
|
182 |
|
183 |
-
return (int) $
|
184 |
}
|
185 |
|
186 |
|
132 |
private function sync_and_hide() {
|
133 |
global $wpdb;
|
134 |
|
135 |
+
$results = $this->get_posts_to_update();
|
136 |
+
|
137 |
+
if ( empty( $results ) ) {
|
138 |
+
|
139 |
+
facebook_for_woocommerce()->log( 'There are no products or products variations to update.' );
|
140 |
+
return 0;
|
141 |
+
}
|
142 |
+
|
143 |
+
$insert = $update = [];
|
144 |
+
|
145 |
+
foreach ( $results as $result ) {
|
146 |
+
|
147 |
+
if ( $result->visibility ) {
|
148 |
+
$update[] = $result->id;
|
149 |
+
} else {
|
150 |
+
$insert[] = $result->id;
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
$rows_inserted = $this->set_product_visibility_meta( $insert );
|
155 |
+
$rows_updated = $this->update_product_visibility_meta( $update );
|
156 |
+
|
157 |
+
return $rows_inserted + $rows_updated;
|
158 |
+
}
|
159 |
+
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Gets the ID and current visibility setting for virtual products that are enabled for sync.
|
163 |
+
*
|
164 |
+
* The method returns data for products that have visibility set to 'yes' or is not defined.
|
165 |
+
* Products that have visibility set to 'no' are ignored.
|
166 |
+
*
|
167 |
+
* @since 2.0.3
|
168 |
+
*
|
169 |
+
* @return array|null
|
170 |
+
*/
|
171 |
+
private function get_posts_to_update() {
|
172 |
+
global $wpdb;
|
173 |
|
|
|
174 |
$sql = "
|
175 |
+
SELECT DISTINCT posts.ID id, visibility_meta.meta_value as visibility
|
176 |
FROM {$wpdb->posts} AS posts
|
177 |
INNER JOIN {$wpdb->postmeta} AS virtual_meta ON ( posts.ID = virtual_meta.post_id AND virtual_meta.meta_key = '_virtual' AND virtual_meta.meta_value = 'yes' )
|
178 |
LEFT JOIN {$wpdb->postmeta} AS sync_meta ON ( posts.ID = sync_meta.post_id AND sync_meta.meta_key = '_wc_facebook_sync_enabled' )
|
183 |
LIMIT 1000
|
184 |
";
|
185 |
|
186 |
+
return $wpdb->get_results( $sql );
|
187 |
+
}
|
188 |
+
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Adds new visibility meta set to 'no' for the given post IDs.
|
192 |
+
*
|
193 |
+
* @since 2.0.3
|
194 |
+
*
|
195 |
+
* @param int[] $post_ids post IDs to update
|
196 |
+
* @return int
|
197 |
+
*/
|
198 |
+
private function set_product_visibility_meta( $post_ids ) {
|
199 |
+
global $wpdb;
|
200 |
|
201 |
if ( empty( $post_ids ) ) {
|
202 |
+
return 0;
|
203 |
+
}
|
204 |
|
205 |
+
$values_str = '';
|
206 |
|
207 |
+
foreach ( $post_ids as $post_id ) {
|
208 |
+
$values_str .= "('{$post_id}', 'fb_visibility', 'no')";
|
209 |
+
}
|
210 |
|
211 |
+
// we need to explicitly insert the metadata and set it to no, because not having it means it is visible
|
212 |
+
$sql = "
|
213 |
+
INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value )
|
214 |
+
VALUES {$values_str}
|
215 |
+
";
|
216 |
|
217 |
+
$rows_inserted = $wpdb->query( $sql );
|
218 |
|
219 |
+
if ( false === $rows_inserted ) {
|
220 |
+
|
221 |
+
$message = sprintf( 'There was an error trying to set products and variations meta data. %s', $wpdb->last_error );
|
222 |
+
|
223 |
+
facebook_for_woocommerce()->log( $message );
|
224 |
+
}
|
225 |
+
|
226 |
+
return (int) $rows_inserted;
|
227 |
+
}
|
228 |
|
|
|
229 |
|
230 |
+
/**
|
231 |
+
* Updates the value of the visibility meta for the given post IDs.
|
232 |
+
*
|
233 |
+
* @since 2.0.3
|
234 |
+
*
|
235 |
+
* @param int[] $post_ids post IDs to update
|
236 |
+
* @return int
|
237 |
+
*/
|
238 |
+
private function update_product_visibility_meta( $post_ids ) {
|
239 |
+
global $wpdb;
|
240 |
|
241 |
+
if ( empty( $post_ids ) ) {
|
242 |
+
return 0;
|
243 |
+
}
|
244 |
|
245 |
+
$sql = sprintf(
|
246 |
+
"UPDATE {$wpdb->postmeta} SET meta_value = 'no' WHERE meta_key = 'fb_visibility' AND post_id IN (%s)",
|
247 |
+
implode( ', ', array_map( 'intval', $post_ids ) )
|
248 |
+
);
|
249 |
|
250 |
+
$rows_updated = $wpdb->query( $sql );
|
251 |
|
252 |
+
if ( false === $rows_updated ) {
|
253 |
+
|
254 |
+
$message = sprintf( 'There was an error trying to update products and variations meta data. %s', $wpdb->last_error );
|
255 |
+
|
256 |
+
facebook_for_woocommerce()->log( $message );
|
257 |
}
|
258 |
|
259 |
+
return (int) $rows_updated;
|
260 |
}
|
261 |
|
262 |
|
includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
namespace SkyVerge\WooCommerce\Facebook\Utilities;
|
12 |
+
|
13 |
+
defined( 'ABSPATH' ) or exit;
|
14 |
+
|
15 |
+
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
|
16 |
+
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Background job handler to remove duplicate fb_visibility entries from the postmeta table.
|
20 |
+
*
|
21 |
+
* The background job handler to hide virtual products from the catalog had a bug that allowed it to create many entries for each product.
|
22 |
+
*
|
23 |
+
* @since 2.0.3
|
24 |
+
*/
|
25 |
+
class Background_Remove_Duplicate_Visibility_Meta extends Framework\SV_WP_Background_Job_Handler {
|
26 |
+
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Background job constructor.
|
30 |
+
*
|
31 |
+
* @since 2.0.3
|
32 |
+
*/
|
33 |
+
public function __construct() {
|
34 |
+
|
35 |
+
$this->prefix = 'wc_facebook';
|
36 |
+
$this->action = 'background_remove_duplicate_visibility_meta';
|
37 |
+
|
38 |
+
parent::__construct();
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Processes job.
|
44 |
+
*
|
45 |
+
* This job continues to update products and product variations meta data until we run out of memory
|
46 |
+
* or exceed the time limit. There is no list of items to loop over.
|
47 |
+
*
|
48 |
+
* @since 2.0.3
|
49 |
+
*
|
50 |
+
* @param object $job
|
51 |
+
* @param int $items_per_batch number of items to process in a single request. Defaults to unlimited.
|
52 |
+
* @return object
|
53 |
+
*/
|
54 |
+
public function process_job( $job, $items_per_batch = null ) {
|
55 |
+
|
56 |
+
// don't do anything until the job used to hide virtual variations is done
|
57 |
+
$handler = facebook_for_woocommerce()->get_background_handle_virtual_products_variations_instance();
|
58 |
+
|
59 |
+
if ( $handler && $handler->get_jobs( [ 'status' => [ 'processing', 'queued' ] ] ) ) {
|
60 |
+
return $job;
|
61 |
+
}
|
62 |
+
|
63 |
+
if ( ! isset( $job->total ) ) {
|
64 |
+
$job->total = $this->count_remaining_products();
|
65 |
+
}
|
66 |
+
|
67 |
+
if ( ! isset( $job->progress ) ) {
|
68 |
+
$job->progress = 0;
|
69 |
+
}
|
70 |
+
|
71 |
+
while ( $job->progress < $job->total ) {
|
72 |
+
|
73 |
+
$job->progress += $this->remove_duplicates();
|
74 |
+
|
75 |
+
// update job progress
|
76 |
+
$job = $this->update_job( $job );
|
77 |
+
|
78 |
+
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
79 |
+
break;
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
// job complete! :)
|
84 |
+
if ( $this->count_remaining_products() === 0 ) {
|
85 |
+
|
86 |
+
update_option( 'wc_facebook_background_remove_duplicate_visibility_meta_complete', 'yes' );
|
87 |
+
|
88 |
+
$this->complete_job( $job );
|
89 |
+
}
|
90 |
+
|
91 |
+
return $job;
|
92 |
+
}
|
93 |
+
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Counts the number of virtual products or product variations with sync enabled and visible.
|
97 |
+
*
|
98 |
+
* @since 2.0.3
|
99 |
+
*
|
100 |
+
* @return bool
|
101 |
+
*/
|
102 |
+
private function count_remaining_products() {
|
103 |
+
global $wpdb;
|
104 |
+
|
105 |
+
$sql = "
|
106 |
+
SELECT COUNT(post_id)
|
107 |
+
FROM (
|
108 |
+
SELECT post_id, COUNT(meta_key) entries
|
109 |
+
FROM {$wpdb->postmeta}
|
110 |
+
WHERE meta_key = 'fb_visibility'
|
111 |
+
GROUP BY post_id
|
112 |
+
HAVING entries > 1
|
113 |
+
) AS duplicate_entries
|
114 |
+
";
|
115 |
+
|
116 |
+
return (int) $wpdb->get_var( $sql );
|
117 |
+
}
|
118 |
+
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Removes duplicate visibility meta data entries for products.
|
122 |
+
*
|
123 |
+
* @since 2.0.3
|
124 |
+
*
|
125 |
+
* @return int
|
126 |
+
*/
|
127 |
+
private function remove_duplicates() {
|
128 |
+
global $wpdb;
|
129 |
+
|
130 |
+
$results = $this->get_posts_to_update();
|
131 |
+
|
132 |
+
if ( empty( $results ) ) {
|
133 |
+
facebook_for_woocommerce()->log( 'There are no products or products variations with duplicate visibility meta data.' );
|
134 |
+
return 0;
|
135 |
+
}
|
136 |
+
|
137 |
+
$products_updated = 0;
|
138 |
+
|
139 |
+
foreach ( $results as $result ) {
|
140 |
+
|
141 |
+
$sql = "DELETE FROM wp_postmeta WHERE post_id = %d AND meta_key = 'fb_visibility' AND meta_id != %d";
|
142 |
+
|
143 |
+
if ( false === $wpdb->query( $wpdb->prepare( $sql, $result->post_id, $result->last_meta_id ) ) ) {
|
144 |
+
|
145 |
+
facebook_for_woocommerce()->log( sprintf(
|
146 |
+
'There was an error trying to set products and variations meta data. %s',
|
147 |
+
$wpdb->last_error
|
148 |
+
) );
|
149 |
+
|
150 |
+
continue;
|
151 |
+
}
|
152 |
+
|
153 |
+
$products_updated++;
|
154 |
+
}
|
155 |
+
|
156 |
+
return $products_updated;
|
157 |
+
}
|
158 |
+
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Gets the ID of products that duplicate visibility meta data.
|
162 |
+
*
|
163 |
+
* The method also returns the number of meta data entries and ID of the last meta data entry for each product.
|
164 |
+
*
|
165 |
+
* @since 2.0.3
|
166 |
+
*
|
167 |
+
* @return array|null
|
168 |
+
*/
|
169 |
+
private function get_posts_to_update() {
|
170 |
+
global $wpdb;
|
171 |
+
|
172 |
+
$sql = "
|
173 |
+
SELECT post_id, COUNT(meta_key) entries, MAX(meta_id) last_meta_id
|
174 |
+
FROM {$wpdb->postmeta}
|
175 |
+
WHERE meta_key = 'fb_visibility'
|
176 |
+
GROUP BY post_id
|
177 |
+
HAVING entries > 1
|
178 |
+
";
|
179 |
+
|
180 |
+
return $wpdb->get_results( $sql );
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
/**
|
185 |
+
* No-op
|
186 |
+
*
|
187 |
+
* @since 2.0.3
|
188 |
+
*/
|
189 |
+
protected function process_item( $item, $job ) {
|
190 |
+
// void
|
191 |
+
}
|
192 |
+
|
193 |
+
|
194 |
+
}
|
includes/fbutils.php
CHANGED
@@ -12,6 +12,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
|
|
|
|
|
15 |
if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
|
16 |
|
17 |
/**
|
@@ -204,28 +206,46 @@ if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
|
|
204 |
* Returns user info for the current WP user.
|
205 |
*
|
206 |
* @access public
|
207 |
-
* @param
|
208 |
* @return array
|
209 |
*/
|
210 |
-
public static function get_user_info( $
|
211 |
$current_user = wp_get_current_user();
|
212 |
-
if ( 0 === $current_user->ID || $
|
213 |
-
// User not logged in or
|
214 |
return array();
|
215 |
} else {
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
'ln' => $current_user->user_lastname,
|
224 |
-
),
|
225 |
-
function ( $value ) {
|
226 |
-
return $value !== null && $value !== '';
|
227 |
-
}
|
228 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
}
|
230 |
}
|
231 |
|
12 |
exit;
|
13 |
}
|
14 |
|
15 |
+
use SkyVerge\WooCommerce\Facebook\Events\Normalizer;
|
16 |
+
|
17 |
if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
|
18 |
|
19 |
/**
|
206 |
* Returns user info for the current WP user.
|
207 |
*
|
208 |
* @access public
|
209 |
+
* @param AAMSettings $aam_settings
|
210 |
* @return array
|
211 |
*/
|
212 |
+
public static function get_user_info( $aam_settings ) {
|
213 |
$current_user = wp_get_current_user();
|
214 |
+
if ( 0 === $current_user->ID || $aam_settings == null || !$aam_settings->get_enable_automatic_matching() ) {
|
215 |
+
// User not logged in or pixel not configured with automatic advance matching
|
216 |
return array();
|
217 |
} else {
|
218 |
+
// Keys documented in
|
219 |
+
// https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
|
220 |
+
$user_data = array(
|
221 |
+
'em' => $current_user->user_email,
|
222 |
+
'fn' => $current_user->user_firstname,
|
223 |
+
'ln' => $current_user->user_lastname,
|
224 |
+
'external_id' => strval($current_user->ID),
|
|
|
|
|
|
|
|
|
|
|
225 |
);
|
226 |
+
$user_id = $current_user->ID;
|
227 |
+
$user_data['ct'] = get_user_meta($user_id, 'billing_city', true);
|
228 |
+
$user_data['zp'] = get_user_meta($user_id, 'billing_postcode', true);
|
229 |
+
$user_data['country'] = get_user_meta($user_id, 'billing_country', true);
|
230 |
+
$user_data['st'] = get_user_meta($user_id, 'billing_state', true);
|
231 |
+
$user_data['ph'] = get_user_meta($user_id, 'billing_phone', true);
|
232 |
+
// Each field that is not present in AAM settings or is empty is deleted from user data
|
233 |
+
foreach ($user_data as $field => $value) {
|
234 |
+
if( $value === null || $value === ''
|
235 |
+
|| !in_array($field, $aam_settings->get_enabled_automatic_matching_fields())
|
236 |
+
){
|
237 |
+
unset($user_data[$field]);
|
238 |
+
}
|
239 |
+
}
|
240 |
+
// Country is a special case, it is returned as country in AAM settings
|
241 |
+
// But used as cn in pixel
|
242 |
+
if(array_key_exists('country', $user_data)){
|
243 |
+
$country = $user_data['country'];
|
244 |
+
$user_data['cn'] = $country;
|
245 |
+
unset($user_data['country']);
|
246 |
+
}
|
247 |
+
$user_data = Normalizer::normalize_array($user_data, true);
|
248 |
+
return $user_data;
|
249 |
}
|
250 |
}
|
251 |
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
Contributors: facebook, automattic, woothemes
|
3 |
Tags: facebook, shop, catalog, advertise, pixel, product
|
4 |
Requires at least: 4.4
|
5 |
-
Tested up to: 5.
|
6 |
-
Stable tag: 2.0.
|
7 |
Requires PHP: 5.6 or greater
|
8 |
MySQL: 5.6 or greater
|
9 |
License: GPLv2 or later
|
@@ -39,6 +39,11 @@ When opening a bug on GitHub, please give us as many details as possible.
|
|
39 |
|
40 |
== Changelog ==
|
41 |
|
|
|
|
|
|
|
|
|
|
|
42 |
= 2020.09.25 - version 2.0.2 =
|
43 |
* Tweak - Allow simple and variable products with zero/empty price to sync to Facebook
|
44 |
* Tweak - Use the bundle price for Product Bundles products with individually priced items
|
2 |
Contributors: facebook, automattic, woothemes
|
3 |
Tags: facebook, shop, catalog, advertise, pixel, product
|
4 |
Requires at least: 4.4
|
5 |
+
Tested up to: 5.5.1
|
6 |
+
Stable tag: 2.0.3
|
7 |
Requires PHP: 5.6 or greater
|
8 |
MySQL: 5.6 or greater
|
9 |
License: GPLv2 or later
|
39 |
|
40 |
== Changelog ==
|
41 |
|
42 |
+
= 2020.10.02 - version 2.0.3 =
|
43 |
+
* Tweak - Pixel events now can include advanced matching information
|
44 |
+
* Fix - Send contents parameter for ViewContent event using the correct format
|
45 |
+
* Fix - Remove duplicate visibility meta entries from postmeta table
|
46 |
+
|
47 |
= 2020.09.25 - version 2.0.2 =
|
48 |
* Tweak - Allow simple and variable products with zero/empty price to sync to Facebook
|
49 |
* Tweak - Use the bundle price for Product Bundles products with individually priced items
|