Facebook for WooCommerce - Version 3.0.0

Version Description

  • 2022-11-17 =
  • Dev - Adding API Unit Tests.
  • Dev - Adding unit test workflow.
  • Dev - Adjusting php code styling.
  • Dev - Refactoring multiple Facebook APIs into a single one.
  • Dev - Removing SkyVerge dependency.
  • Dev - Removing deprecations.
  • Tweak - WC 5.4 compatibility.
Download this release

Release Info

Developer automattic
Plugin Icon Facebook for WooCommerce
Version 3.0.0
Comparing to
See all releases

Code changes from version 2.6.30 to 3.0.0

Files changed (188) hide show
  1. changelog.txt +56 -47
  2. class-wc-facebookcommerce.php +646 -1069
  3. facebook-commerce-events-tracker.php +6 -48
  4. facebook-commerce-messenger-chat.php +1 -18
  5. facebook-commerce-pixel-event.php +6 -44
  6. facebook-commerce.php +531 -1312
  7. facebook-config-warmer.php +7 -14
  8. facebook-for-woocommerce.php +3 -21
  9. i18n/languages/facebook-for-woocommerce.pot +384 -290
  10. includes/AJAX.php +29 -28
  11. includes/API.php +231 -299
  12. includes/API/Catalog/Product_Group/Products/Read/Request.php +2 -2
  13. includes/API/Catalog/Product_Group/Products/Read/Response.php +2 -2
  14. includes/API/Catalog/Product_Item/Find/Request.php +2 -2
  15. includes/API/Catalog/Product_Item/Response.php +2 -2
  16. includes/API/Catalog/Request.php +8 -30
  17. includes/API/Catalog/Response.php +8 -29
  18. includes/API/Catalog/Send_Item_Updates/Request.php +2 -2
  19. includes/API/Catalog/Send_Item_Updates/Response.php +2 -8
  20. includes/API/Exceptions/ConnectApiException.php +14 -0
  21. includes/API/Exceptions/Request_Limit_Reached.php +3 -10
  22. includes/API/FBE/Configuration/Messenger.php +3 -3
  23. includes/API/FBE/Configuration/Read/Response.php +11 -51
  24. includes/API/FBE/Configuration/Request.php +5 -26
  25. includes/API/FBE/Configuration/Update/Request.php +2 -4
  26. includes/API/FBE/Configuration/Update/Response.php +16 -0
  27. includes/API/FBE/Installation/Read/Request.php +2 -8
  28. includes/API/FBE/Installation/Read/Response.php +20 -54
  29. includes/API/FBE/Installation/Request.php +2 -6
  30. includes/API/Log/Create/Request.php +28 -0
  31. includes/API/Log/Create/Response.php +13 -0
  32. includes/API/Orders/Abstract_Request.php +2 -2
  33. includes/API/Orders/Acknowledge/Request.php +2 -2
  34. includes/API/Orders/Cancel/Request.php +2 -2
  35. includes/API/Orders/Fulfillment/Request.php +2 -2
  36. includes/API/Orders/Order.php +1 -1
  37. includes/API/Orders/Read/Request.php +2 -2
  38. includes/API/Orders/Read/Response.php +4 -4
  39. includes/API/Orders/Refund/Request.php +2 -2
  40. includes/API/Orders/Request.php +1 -1
  41. includes/API/Orders/Response.php +3 -3
  42. includes/API/Pages/Read/Request.php +6 -34
  43. includes/API/Pages/Read/Response.php +7 -42
  44. includes/API/Pixel/Events/Request.php +3 -3
  45. includes/API/ProductCatalog/ItemsBatch/Create/Request.php +30 -0
  46. includes/API/ProductCatalog/ItemsBatch/Create/Response.php +17 -0
  47. includes/API/ProductCatalog/ProductFeedUploads/Read/Request.php +23 -0
  48. includes/API/ProductCatalog/ProductFeedUploads/Read/Response.php +16 -0
  49. includes/API/ProductCatalog/ProductFeeds/Create/Request.php +25 -0
  50. includes/API/ProductCatalog/ProductFeeds/Create/Response.php +16 -0
  51. includes/API/ProductCatalog/ProductFeeds/Read/Request.php +23 -0
  52. includes/API/ProductCatalog/ProductFeeds/Read/Response.php +16 -0
  53. includes/API/ProductCatalog/ProductFeeds/ReadAll/Request.php +23 -0
  54. includes/API/ProductCatalog/ProductFeeds/ReadAll/Response.php +16 -0
  55. includes/API/ProductCatalog/ProductGroups/Create/Request.php +25 -0
  56. includes/API/ProductCatalog/ProductGroups/Create/Response.php +16 -0
  57. includes/API/ProductCatalog/ProductGroups/Delete/Request.php +23 -0
  58. includes/API/ProductCatalog/ProductGroups/Delete/Response.php +16 -0
  59. includes/API/ProductCatalog/ProductGroups/Read/Request.php +24 -0
  60. includes/API/ProductCatalog/ProductGroups/Read/Response.php +35 -0
  61. includes/API/ProductCatalog/ProductGroups/Update/Request.php +25 -0
  62. includes/API/ProductCatalog/ProductGroups/Update/Response.php +16 -0
  63. includes/API/ProductCatalog/ProductSets/Create/Request.php +25 -0
  64. includes/API/ProductCatalog/ProductSets/Create/Response.php +16 -0
  65. includes/API/ProductCatalog/ProductSets/Delete/Request.php +26 -0
  66. includes/API/ProductCatalog/ProductSets/Delete/Response.php +16 -0
  67. includes/API/ProductCatalog/ProductSets/Update/Request.php +25 -0
  68. includes/API/ProductCatalog/ProductSets/Update/Response.php +16 -0
  69. includes/API/ProductCatalog/Products/Create/Request.php +25 -0
  70. includes/API/ProductCatalog/Products/Create/Response.php +16 -0
  71. includes/API/ProductCatalog/Products/Delete/Request.php +23 -0
  72. includes/API/ProductCatalog/Products/Delete/Response.php +16 -0
  73. includes/API/ProductCatalog/Products/Id/Request.php +25 -0
  74. includes/API/ProductCatalog/Products/Id/Response.php +27 -0
  75. includes/API/ProductCatalog/Products/Update/Request.php +25 -0
  76. includes/API/ProductCatalog/Products/Update/Response.php +16 -0
  77. includes/API/Request.php +6 -26
  78. includes/API/Response.php +8 -18
  79. includes/API/Tip/Log/Request.php +29 -0
  80. includes/API/Tip/Log/Response.php +13 -0
  81. includes/API/Tip/Read/Request.php +21 -0
  82. includes/API/Tip/Read/Response.php +46 -0
  83. includes/API/Traits/Idempotent_Request.php +1 -1
  84. includes/API/Traits/Paginated_Response.php +1 -1
  85. includes/API/Traits/Rate_Limited_API.php +1 -12
  86. includes/API/Traits/Rate_Limited_Request.php +1 -5
  87. includes/API/Traits/Rate_Limited_Response.php +1 -1
  88. includes/API/User/Permissions/Delete/Request.php +4 -16
  89. includes/API/User/Permissions/Delete/Response.php +15 -0
  90. includes/API/User/Request.php +7 -29
  91. includes/API/User/Response.php +8 -16
  92. includes/Admin.php +51 -265
  93. includes/Admin/Abstract_Settings_Screen.php +6 -16
  94. includes/Admin/Enhanced_Catalog_Attribute_Fields.php +3 -3
  95. includes/Admin/Google_Product_Category_Field.php +1 -2
  96. includes/Admin/Notes/SettingsMoved.php +0 -88
  97. includes/Admin/Product_Categories.php +21 -38
  98. includes/Admin/Product_Sets.php +8 -21
  99. includes/Admin/Product_Sync_Meta_Box.php +2 -1
  100. includes/Admin/Products.php +6 -37
  101. includes/Admin/Settings.php +19 -72
  102. includes/Admin/Settings_Screens/Advertise.php +4 -17
  103. includes/Admin/Settings_Screens/Connection.php +6 -8
  104. includes/Admin/Settings_Screens/Messenger.php +13 -50
  105. includes/Admin/Settings_Screens/Product_Sets.php +3 -6
  106. includes/Admin/Settings_Screens/Product_Sync.php +9 -38
  107. includes/Admin/Tasks/Setup.php +1 -1
  108. includes/Commerce.php +2 -5
  109. includes/Commerce/Orders.php +25 -30
  110. includes/Debug/ProfilingLogger.php +1 -1
  111. includes/Debug/ProfilingLoggerProcess.php +1 -1
  112. includes/Events/AAMSettings.php +1 -1
  113. includes/Events/Event.php +2 -36
  114. includes/Events/Normalizer.php +1 -1
  115. includes/Exceptions/ConnectWCAPIException.php +0 -15
  116. includes/Feed/FeedConfigurationDetection.php +30 -48
  117. vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wp-admin-message-handler.php → includes/Framework/AdminMessageHandler.php +11 -63
  118. vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-admin-notice-handler.php → includes/Framework/AdminNoticeHandler.php +12 -56
  119. vendor/skyverge/wc-plugin-framework/woocommerce/api/class-sv-wc-api-base.php → includes/Framework/Api/Base.php +45 -164
  120. includes/Framework/Api/Exception.php +14 -0
  121. includes/Framework/Api/JSONRequest.php +94 -0
  122. includes/Framework/Api/JSONResponse.php +74 -0
  123. includes/Framework/Api/Request.php +70 -0
  124. includes/Framework/Api/Response.php +35 -0
  125. includes/Framework/Helper.php +434 -0
  126. {vendor/skyverge/wc-plugin-framework/woocommerce → includes/Framework}/Lifecycle.php +26 -213
  127. vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin.php → includes/Framework/Plugin.php +40 -711
  128. includes/Framework/Plugin/Compatibility.php +155 -0
  129. vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin-dependencies.php → includes/Framework/Plugin/Dependencies.php +19 -137
  130. includes/Framework/Plugin/Exception.php +14 -0
  131. vendor/skyverge/wc-plugin-framework/woocommerce/utilities/class-sv-wp-async-request.php → includes/Framework/Utilities/AsyncRequest.php +6 -33
  132. vendor/skyverge/wc-plugin-framework/woocommerce/utilities/class-sv-wp-background-job-handler.php → includes/Framework/Utilities/BackgroundJobHandler.php +17 -97
  133. includes/Handlers/Connection.php +26 -177
  134. includes/Handlers/WebHook.php +2 -12
  135. includes/Integrations/Bookings.php +1 -1
  136. includes/Jobs/AbstractChainedJob.php +1 -1
  137. includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php +6 -5
  138. includes/Jobs/GenerateProductFeed.php +1 -2
  139. includes/Jobs/JobManager.php +1 -1
  140. includes/Jobs/LoggingTrait.php +1 -3
  141. includes/Lifecycle.php +5 -52
  142. includes/Locale.php +1 -1
  143. includes/ProductSets/Sync.php +2 -4
  144. includes/ProductSync/ProductExcludedException.php +1 -1
  145. includes/ProductSync/ProductInvalidException.php +1 -1
  146. includes/ProductSync/ProductValidator.php +9 -8
  147. includes/Product_Categories.php +1 -7
  148. includes/Products.php +18 -126
  149. includes/Products/FBCategories.php +1 -47
  150. includes/Products/Feed.php +12 -39
  151. includes/Products/GoogleProductTaxonomy.php +2 -1
  152. includes/Products/Stock.php +2 -15
  153. includes/Products/Sync.php +1 -4
  154. includes/Products/Sync/Background.php +47 -97
  155. includes/Utilities/Background_Handle_Virtual_Products_Variations.php +3 -15
  156. includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php +3 -4
  157. includes/Utilities/Heartbeat.php +1 -1
  158. includes/Utilities/Shipment.php +1 -1
  159. includes/Utilities/Tracker.php +14 -14
  160. includes/fbasync.php +14 -21
  161. includes/fbbackground.php +2 -8
  162. includes/fbgraph.php +0 -677
  163. includes/fbinfobanner.php +186 -225
  164. includes/fbproduct.php +738 -762
  165. includes/fbproductfeed.php +453 -587
  166. includes/fbutils.php +519 -538
  167. includes/fbwpml.php +113 -119
  168. includes/test/facebook-integration-test.php +0 -548
  169. readme.txt +58 -49
  170. vendor/autoload.php +1 -1
  171. vendor/composer/InstalledVersions.php +7 -7
  172. vendor/composer/autoload_classmap.php +148 -92
  173. vendor/composer/autoload_psr4.php +1 -1
  174. vendor/composer/autoload_real.php +4 -4
  175. vendor/composer/autoload_static.php +155 -99
  176. vendor/composer/installed.json +0 -28
  177. vendor/composer/installed.php +9 -18
  178. vendor/composer/platform_check.php +2 -2
  179. vendor/skyverge/wc-plugin-framework/.dist.env +0 -17
  180. vendor/skyverge/wc-plugin-framework/.editorconfig +0 -21
  181. vendor/skyverge/wc-plugin-framework/.gitignore +0 -26
  182. vendor/skyverge/wc-plugin-framework/.travis.yml +0 -102
  183. vendor/skyverge/wc-plugin-framework/.tx/config +0 -9
  184. vendor/skyverge/wc-plugin-framework/Gruntfile.js +0 -82
  185. vendor/skyverge/wc-plugin-framework/codeception.dist.yml +0 -22
  186. vendor/skyverge/wc-plugin-framework/codeception.local.yml +0 -12
  187. vendor/skyverge/wc-plugin-framework/composer.json +0 -8
  188. vendor/skyverge/wc-plugin-framework/composer.lock +0 -1643
changelog.txt CHANGED
@@ -1,53 +1,62 @@
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
- = 2.6.30 - 2022-11-09 =
4
- * Fix - Add backward compatibility for WC 6.1, 6.2, and 6.3 versions.
5
- * Fix - Sync product set when the term name changes.
6
-
7
- = 2.6.29 - 2022-11-08 =
8
- * Add - Facebook Product Set under the Marketing menu.
9
- * Add - HPOS Compatibility.
10
- * Add - Inbox note about Facebook menu moved under the Marketing menu.
11
- * Add - Set up Facebook task to the WooCommerce admin tasks.
12
- * Dev - Replaced methods from classes in the `Internal` namespace.
13
- * Fix - Ensure the enhanced product enhance catalog attributes value is unslashed before saving in the post_meta table.
14
- * Fix - Hosted Woo Updates.
15
- * Fix - Release/2.6.28.
16
- * Fix - duplicate InitiateCheckout when using checkout block.
17
- * Tweak - WC 7.1 compatibility.
18
- * Tweak - WP 6.1 compatibility.
19
- * Update - FB Product Set name changed to Facebook Product Set.
20
- * Update - On successful FBE install users will be redirected to Advertise tab of the plugin.
21
-
22
- = 2.6.28 - 2022-10-25 =
23
- * Fix - Ensure bundles are not treated as virtual products on product_sync.
24
- * Fix - Ensure google-product-category-fields-loads.js loads only on the product category screens.
25
- * Fix - Server side sending of pixel events blocks generating pages .
26
-
27
- = 2.6.27 - 2022-10-14 =
28
- * Fix - Revert "Switch to Jetpack autoloader. (#1996 PR refresh)".
29
-
30
- = 2.6.26 - 2022-10-13 =
31
- * Add - wc_facebook_should_sync_product filter.
32
- * Dev - Rename JobRegistry to JobManager.
33
- * Dev - Replace composer autoloader with Jetpack autoloader.
34
- * Fix - Fix content_name and content_category attributes set on ViewCategory pixel events.
35
- * Tweak - WC 7.0 compatibility.
36
-
37
- = 2.6.25 - 2022-10-04 =
38
- * Add - New filter (wc_facebook_product_group_default_variation) to allow customizing a product group's default variation.
39
- * Update - Remove Skyverge's sake as a dependency from the extension build process.
40
-
41
- = 2.6.24 - 2022-09-27 =
42
- * Fix - Adds helpful admin notices for correct user roles.
43
- * Fix - Track purchase event flag in session variable instead post meta table.
44
-
45
- = 2.6.23 - 2022-09-13 =
46
- * Add - Show warning when creating product set with excluded categories.
47
- * Fix - Messenger settings are no longer overridden after business config refresh.
48
- * Fix - PHP notice thrown by get_page_id() in facebook-for-woocommerce/includes/API/FBE/Installation/Read/Response.php.
49
- * Fix - When disabling Enable Messenger on the Messenger setting page, the setting does not persist after selecting Save Changes.
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  = 2.6.22 - 2022-09-06 =
52
  * Fix - Adding an excluded category doesn't remove that category synced products.
53
  * Fix - Ensure content_name and content_ids addToCart pixel event properties are correct for variable products when redirect to cart is enabled in WooCommerce.
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
+ = 3.0.0 - 2022-11-17 =
4
+ * Dev - Adding API Unit Tests.
5
+ * Dev - Adding unit test workflow.
6
+ * Dev - Adjusting php code styling.
7
+ * Dev - Refactoring multiple Facebook APIs into a single one.
8
+ * Dev - Removing SkyVerge dependency.
9
+ * Dev - Removing deprecations.
10
+ * Tweak - WC 5.4 compatibility.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ = 2.6.30 - 2022-11-09 =
13
+ * Fix - Add backward compatibility for WC 6.1, 6.2, and 6.3 versions.
14
+ * Fix - Sync product set when the term name changes.
15
+
16
+ = 2.6.29 - 2022-11-08 =
17
+ * Add - Facebook Product Set under the Marketing menu.
18
+ * Add - HPOS Compatibility.
19
+ * Add - Inbox note about Facebook menu moved under the Marketing menu.
20
+ * Add - Set up Facebook task to the WooCommerce admin tasks.
21
+ * Dev - Replaced methods from classes in the `Internal` namespace.
22
+ * Fix - Ensure the enhanced product enhance catalog attributes value is unslashed before saving in the post_meta table.
23
+ * Fix - Hosted Woo Updates.
24
+ * Fix - Release/2.6.28.
25
+ * Fix - duplicate InitiateCheckout when using checkout block.
26
+ * Tweak - WC 7.1 compatibility.
27
+ * Tweak - WP 6.1 compatibility.
28
+ * Update - FB Product Set name changed to Facebook Product Set.
29
+ * Update - On successful FBE install users will be redirected to Advertise tab of the plugin.
30
+
31
+ = 2.6.28 - 2022-10-25 =
32
+ * Fix - Ensure bundles are not treated as virtual products on product_sync.
33
+ * Fix - Ensure google-product-category-fields-loads.js loads only on the product category screens.
34
+ * Fix - Server side sending of pixel events blocks generating pages .
35
+
36
+ = 2.6.27 - 2022-10-14 =
37
+ * Fix - Revert "Switch to Jetpack autoloader. (#1996 PR refresh)".
38
+
39
+ = 2.6.26 - 2022-10-13 =
40
+ * Add - wc_facebook_should_sync_product filter.
41
+ * Dev - Rename JobRegistry to JobManager.
42
+ * Dev - Replace composer autoloader with Jetpack autoloader.
43
+ * Fix - Fix content_name and content_category attributes set on ViewCategory pixel events.
44
+ * Tweak - WC 7.0 compatibility.
45
+
46
+ = 2.6.25 - 2022-10-04 =
47
+ * Add - New filter (wc_facebook_product_group_default_variation) to allow customizing a product group's default variation.
48
+ * Update - Remove Skyverge's sake as a dependency from the extension build process.
49
+
50
+ = 2.6.24 - 2022-09-27 =
51
+ * Fix - Adds helpful admin notices for correct user roles.
52
+ * Fix - Track purchase event flag in session variable instead post meta table.
53
+
54
+ = 2.6.23 - 2022-09-13 =
55
+ * Add - Show warning when creating product set with excluded categories.
56
+ * Fix - Messenger settings are no longer overridden after business config refresh.
57
+ * Fix - PHP notice thrown by get_page_id() in facebook-for-woocommerce/includes/API/FBE/Installation/Read/Response.php.
58
+ * Fix - When disabling Enable Messenger on the Messenger setting page, the setting does not persist after selecting Save Changes.
59
+
60
  = 2.6.22 - 2022-09-06 =
61
  * Fix - Adding an excluded category doesn't remove that category synced products.
62
  * Fix - Ensure content_name and content_ids addToCart pixel event properties are correct for variable products when redirect to cart is enabled in WooCommerce.
class-wc-facebookcommerce.php CHANGED
@@ -9,1216 +9,793 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\API;
13
- use SkyVerge\WooCommerce\Facebook\Integrations\Bookings as BookingsIntegration;
14
- use SkyVerge\WooCommerce\Facebook\Lifecycle;
15
- use SkyVerge\WooCommerce\Facebook\Utilities\Background_Handle_Virtual_Products_Variations;
16
- use SkyVerge\WooCommerce\Facebook\Utilities\Background_Remove_Duplicate_Visibility_Meta;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
18
- use SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator as ProductSyncValidator;
19
- use SkyVerge\WooCommerce\Facebook\Utilities\Heartbeat;
20
- use Automattic\WooCommerce\Admin\Features\Features as WooAdminFeatures;
21
- use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
22
- use Automattic\WooCommerce\Admin\Notes\Note;
23
- use SkyVerge\WooCommerce\Facebook\Admin\Tasks\Setup;
24
- use SkyVerge\WooCommerce\Facebook\Admin\Notes\SettingsMoved;
25
-
26
- if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
27
-
28
- include_once 'includes/fbutils.php';
29
-
30
- class WC_Facebookcommerce extends Framework\SV_WC_Plugin {
31
-
32
-
33
- /** @var string the plugin version */
34
- const VERSION = WC_Facebook_Loader::PLUGIN_VERSION;
35
-
36
- /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
37
- const PLUGIN_VERSION = self::VERSION;
38
-
39
- /** @var string the plugin ID */
40
- const PLUGIN_ID = 'facebook_for_woocommerce';
41
-
42
- /** @var string the integration ID */
43
- const INTEGRATION_ID = 'facebookcommerce';
44
-
45
- /** @var string the product set categories meta name */
46
- const PRODUCT_SET_META = '_wc_facebook_product_cats';
47
-
48
- /** @var string the plugin user agent name to use for HTTP calls within User-Agent header */
49
- const PLUGIN_USER_AGENT_NAME = 'Facebook-for-WooCommerce';
50
-
51
- /** @var \WC_Facebookcommerce singleton instance */
52
- protected static $instance;
53
-
54
- /** @var SkyVerge\WooCommerce\Facebook\API instance */
55
- private $api;
56
-
57
- /** @var \WC_Facebookcommerce_Integration instance */
58
- private $integration;
59
-
60
- /** @var \SkyVerge\WooCommerce\Facebook\Admin admin handler instance */
61
- private $admin;
62
-
63
- /** @var \SkyVerge\WooCommerce\Facebook\Admin\Settings */
64
- private $admin_settings;
65
-
66
- /** @var \SkyVerge\WooCommerce\Facebook\AJAX Ajax handler instance */
67
- private $ajax;
68
-
69
- /** @var \SkyVerge\WooCommerce\Facebook\Products\Feed product feed handler */
70
- private $product_feed;
71
-
72
- /** @var Background_Handle_Virtual_Products_Variations instance */
73
- protected $background_handle_virtual_products_variations;
74
-
75
- /** @var Background_Remove_Duplicate_Visibility_Meta job handler instance */
76
- protected $background_remove_duplicate_visibility_meta;
77
-
78
- /** @var \SkyVerge\WooCommerce\Facebook\Products\Stock products stock handler */
79
- private $products_stock_handler;
80
-
81
- /** @var \SkyVerge\WooCommerce\Facebook\Products\Sync products sync handler */
82
- private $products_sync_handler;
83
-
84
- /** @var \SkyVerge\WooCommerce\Facebook\Products\Sync\Background background sync handler */
85
- private $sync_background_handler;
86
-
87
- /** @var \SkyVerge\WooCommerce\Facebook\ProductSets\Sync product sets sync handler */
88
- private $product_sets_sync_handler;
89
-
90
- /** @var \SkyVerge\WooCommerce\Facebook\Handlers\Connection connection handler */
91
- private $connection_handler;
92
 
93
- /** @var \SkyVerge\WooCommerce\Facebook\Handlers\WebHook webhook handler */
94
- private $webhook_handler;
95
-
96
- /** @var \SkyVerge\WooCommerce\Facebook\Commerce commerce handler */
97
- private $commerce_handler;
98
-
99
- /** @var \SkyVerge\WooCommerce\Facebook\Tracker */
100
- private $tracker;
101
-
102
- /** @var \SkyVerge\WooCommerce\Facebook\Jobs\JobManager */
103
- public $job_manager;
104
-
105
- /** @var Heartbeat */
106
- public $heartbeat;
107
-
108
- /**
109
- * Constructs the plugin.
110
- *
111
- * @since 1.0.0
112
- */
113
- public function __construct() {
114
-
115
- parent::__construct(
116
- self::PLUGIN_ID,
117
- self::VERSION,
118
- array(
119
- 'text_domain' => 'facebook-for-woocommerce',
120
- )
121
- );
122
-
123
- $this->init();
124
- }
125
-
126
-
127
- /**
128
- * Initializes the plugin.
129
- *
130
- * @internal
131
- */
132
- public function init() {
133
-
134
- add_action( 'init', array( $this, 'get_integration' ) );
135
- add_action( 'init', array( $this, 'register_custom_taxonomy' ) );
136
- add_action( 'add_meta_boxes_product', array( $this, 'remove_product_fb_product_set_metabox' ), 50 );
137
- add_filter( 'fb_product_set_row_actions', array( $this, 'product_set_links' ) );
138
- add_filter( 'manage_edit-fb_product_set_columns', array( $this, 'manage_fb_product_set_columns' ) );
139
-
140
- // Hook the setup task. The hook admin_init is not triggered when the WC fetches the tasks using the endpoint: wp-json/wc-admin/onboarding/tasks and hence hooking into init.
141
- add_action( 'init', array( $this, 'add_setup_task' ), 20 );
142
-
143
- // Product Set breadcrumb filters
144
- add_filter( 'woocommerce_navigation_is_connected_page', array( $this, 'is_current_page_conected_filter' ), 99, 2 );
145
- add_filter( 'woocommerce_navigation_get_breadcrumbs', array( $this, 'wc_page_breadcrumbs_filter' ), 99 );
146
-
147
- add_filter(
148
- 'wc_' . WC_Facebookcommerce::PLUGIN_ID . '_http_request_args',
149
- array( $this, 'force_user_agent_in_latin' )
150
- );
151
 
152
- if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
153
- require_once __DIR__ . '/vendor/autoload.php';
 
154
 
155
- include_once 'facebook-commerce.php';
 
156
 
157
- require_once $this->get_framework_path() . '/utilities/class-sv-wp-async-request.php';
158
- require_once $this->get_framework_path() . '/utilities/class-sv-wp-background-job-handler.php';
159
 
160
- require_once __DIR__ . '/includes/fbproductfeed.php';
161
- require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
162
- require_once __DIR__ . '/includes/Exceptions/ConnectWCAPIException.php';
163
 
164
- $this->heartbeat = new Heartbeat( WC()->queue() );
165
- $this->heartbeat->init();
166
 
167
- $this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
168
- $this->products_stock_handler = new \SkyVerge\WooCommerce\Facebook\Products\Stock();
169
- $this->products_sync_handler = new \SkyVerge\WooCommerce\Facebook\Products\Sync();
170
- $this->sync_background_handler = new \SkyVerge\WooCommerce\Facebook\Products\Sync\Background();
171
- $this->configuration_detection = new \SkyVerge\WooCommerce\Facebook\Feed\FeedConfigurationDetection();
172
- $this->product_sets_sync_handler = new \SkyVerge\WooCommerce\Facebook\ProductSets\Sync();
173
- $this->commerce_handler = new \SkyVerge\WooCommerce\Facebook\Commerce();
174
- $this->fb_categories = new \SkyVerge\WooCommerce\Facebook\Products\FBCategories();
175
 
176
- if ( wp_doing_ajax() ) {
177
- $this->ajax = new \SkyVerge\WooCommerce\Facebook\AJAX();
178
- }
179
 
180
- // Load integrations.
181
- require_once __DIR__ . '/includes/fbwpml.php';
182
- new WC_Facebook_WPML_Injector();
183
- new BookingsIntegration();
184
 
185
- if ( 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) ) {
 
186
 
187
- require_once __DIR__ . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php';
 
188
 
189
- $this->background_handle_virtual_products_variations = new Background_Handle_Virtual_Products_Variations();
 
190
 
191
- }
 
192
 
193
- if ( 'yes' !== get_option( 'wc_facebook_background_remove_duplicate_visibility_meta_complete', 'no' ) ) {
 
194
 
195
- require_once __DIR__ . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php';
 
196
 
197
- $this->background_remove_duplicate_visibility_meta = new Background_Remove_Duplicate_Visibility_Meta();
198
- }
199
 
200
- $this->connection_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\Connection( $this );
201
- $this->webhook_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\WebHook( $this );
202
 
203
- $this->tracker = new \SkyVerge\WooCommerce\Facebook\Utilities\Tracker();
 
204
 
205
- // Init jobs
206
- $this->job_manager = new \SkyVerge\WooCommerce\Facebook\Jobs\JobManager();
207
- add_action( 'init', [ $this->job_manager, 'init' ] );
208
 
209
- // load admin handlers, before admin_init
210
- if ( is_admin() ) {
211
 
212
- require_once __DIR__ . '/includes/Admin/Settings.php';
213
- require_once __DIR__ . '/includes/Admin/Abstract_Settings_Screen.php';
214
- require_once __DIR__ . '/includes/Admin/Settings_Screens/Connection.php';
215
- require_once __DIR__ . '/includes/Admin/Settings_Screens/Product_Sync.php';
216
- require_once __DIR__ . '/includes/Admin/Settings_Screens/Product_Sets.php';
217
- require_once __DIR__ . '/includes/Admin/Settings_Screens/Messenger.php';
218
- require_once __DIR__ . '/includes/Admin/Settings_Screens/Advertise.php';
219
- require_once __DIR__ . '/includes/Admin/Google_Product_Category_Field.php';
220
- require_once __DIR__ . '/includes/Admin/Enhanced_Catalog_Attribute_Fields.php';
221
 
222
- $this->admin_settings = new \SkyVerge\WooCommerce\Facebook\Admin\Settings();
223
- }
224
- }
225
- }
226
 
 
 
227
 
228
- /**
229
- * Initializes the admin handling.
230
- *
231
- * @internal
232
- *
233
- * @since 1.10.0
234
- */
235
- public function init_admin() {
236
 
237
- require_once __DIR__ . '/includes/Admin.php';
 
238
 
239
- $this->admin = new \SkyVerge\WooCommerce\Facebook\Admin();
240
- }
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
- /**
244
- * Gets deprecated and removed hooks.
245
- *
246
- * @since 2.1.0
247
- *
248
- * @return array
249
- */
250
- protected function get_deprecated_hooks() {
251
-
252
- return array(
253
- 'wc_facebook_page_access_token' => array(
254
- 'version' => '2.1.0',
255
- 'replacement' => false,
256
- ),
257
- );
258
- }
259
 
260
- /**
261
- * Adds the setup task to the Tasklists.
262
- *
263
- * @since 2.6.29
264
- */
265
- public function add_setup_task() {
266
- if ( class_exists( TaskLists::class ) ) { // This is added for backward compatibility.
267
- TaskLists::add_task(
268
- 'extended',
269
- new Setup(
270
- TaskLists::get_list( 'extended' )
271
- )
272
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
- }
275
 
276
- /**
277
- * Adds the plugin admin notices.
278
- *
279
- * @since 1.11.0
280
- */
281
- public function add_admin_notices() {
282
-
283
- parent::add_admin_notices();
284
-
285
- // inform users who are not connected to Facebook
286
- if ( ! $this->get_connection_handler()->is_connected() ) {
287
-
288
- // users who've never connected to FBE 2 but have previously connected to FBE 1
289
- if ( ! $this->get_connection_handler()->has_previously_connected_fbe_2() && $this->get_connection_handler()->has_previously_connected_fbe_1() ) {
290
-
291
- $message = sprintf(
292
- /* translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag */
293
- __( '%1$sHeads up!%2$s You\'re ready to migrate to a more secure, reliable Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!', 'facebook-for-woocommerce' ),
294
- '<strong>',
295
- '</strong>',
296
- '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">',
297
- '</a>'
298
- );
299
-
300
- $this->get_admin_notice_handler()->add_admin_notice(
301
- $message,
302
- self::PLUGIN_ID . '_migrate_to_v2_0',
303
- array(
304
- 'dismissible' => false,
305
- 'notice_class' => 'notice-info',
306
- )
307
- );
308
-
309
- // direct these users to the new plugin settings page
310
- if ( ! $this->is_plugin_settings() ) {
311
-
312
- $message = sprintf(
313
- /* translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link HTML tag */
314
- __( 'For your convenience, the Facebook for WooCommerce settings are now located under %1$sWooCommerce > Facebook%2$s.', 'facebook-for-woocommerce' ),
315
- '<a href="' . esc_url( facebook_for_woocommerce()->get_settings_url() ) . '">',
316
- '</a>'
317
- );
318
-
319
- $this->get_admin_notice_handler()->add_admin_notice(
320
- $message,
321
- self::PLUGIN_ID . '_relocated_settings',
322
- array(
323
- 'dismissible' => true,
324
- 'notice_class' => 'notice-info',
325
- )
326
- );
327
- }
328
- }
329
-
330
- // notices for those connected to FBE 2
331
- } else {
332
-
333
- // if upgraders had messenger enabled and one of the removed settings was customized, alert them to reconfigure
334
- if (
335
- $this->get_integration()->get_external_merchant_settings_id()
336
- && $this->get_integration()->is_messenger_enabled()
337
- && ( '#0084ff' !== $this->get_integration()->get_messenger_color_hex() || ! in_array( $this->get_integration()->get_messenger_greeting(), array( 'Hi! How can we help you?', "Hi! We're here to answer any questions you may have.", '' ), true ) )
338
- ) {
339
-
340
- $message = sprintf(
341
- /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
342
- __( '%1$sHeads up!%2$s If you\'ve customized your Facebook Messenger color or greeting settings, please update those settings again from the %3$sManage Connection%4$s area.', 'facebook-for-woocommerce' ),
343
- '<strong>',
344
- '</strong>',
345
- '<a href="' . esc_url( $this->get_connection_handler()->get_manage_url() ) . '" target="_blank">',
346
- '</a>'
347
- );
348
-
349
- $this->get_admin_notice_handler()->add_admin_notice(
350
- $message,
351
- 'update_messenger',
352
- array(
353
- 'always_show_on_settings' => false,
354
- 'notice_class' => 'notice-info',
355
- )
356
- );
357
- }
358
- }
359
 
360
- // if the connection is otherwise invalid, but there is an access token
361
- if ( get_transient( 'wc_facebook_connection_invalid' ) && $this->get_connection_handler()->is_connected() ) {
362
-
363
- $message = sprintf(
364
- /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
365
- __( '%1$sHeads up!%2$s Your connection to Facebook is no longer valid. Please %3$sclick here%4$s to securely reconnect your account and continue syncing products.', 'facebook-for-woocommerce' ),
366
- '<strong>',
367
- '</strong>',
368
- '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">',
369
- '</a>'
370
- );
371
-
372
- $this->get_admin_notice_handler()->add_admin_notice(
373
- $message,
374
- 'connection_invalid',
375
- array(
376
- 'notice_class' => 'notice-error',
377
- )
378
- );
379
  }
380
 
381
- if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
382
-
383
- if ( class_exists( WooAdminFeatures::class ) ) {
384
- $is_marketing_enabled = WooAdminFeatures::is_enabled( 'marketing' );
385
- } else {
386
- $is_marketing_enabled = is_callable( '\Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
387
- && \Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
388
- }
389
-
390
- if ( $is_marketing_enabled && class_exists( Note::class ) ) { // Checking for Note class is for backward compatibility.
391
- SettingsMoved::possibly_add_or_delete_note();
392
- }
393
  }
394
- }
395
 
396
- /**
397
- * Get the last event from the plugin lifecycle.
398
- *
399
- * @since 2.6.29
400
- * @return array
401
- */
402
- public function get_last_event_from_history() {
403
- $last_event = array();
404
- $history_events = $this->lifecycle_handler->get_event_history();
405
-
406
- if ( isset( $history_events[0] ) ) {
407
- $last_event = $history_events[0];
408
- }
409
- return $last_event;
410
- }
411
 
412
- public function add_wordpress_integration() {
413
- new WP_Facebook_Integration();
414
- }
415
 
416
- /**
417
- * Saves errors or messages to WooCommerce Log (woocommerce/logs/plugin-id-xxx.txt)
418
- *
419
- * @since 2.3.3
420
- * @param string $message error or message to save to log
421
- * @param string $log_id optional log id to segment the files by, defaults to plugin id
422
- */
423
- public function log( $message, $log_id = null ) {
424
- // Bail if site is connected and user has disabled logging.
425
- // If site is disconnected, force-enable logging so merchant can diagnose connection issues.
426
- if ( ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) && $this->get_connection_handler()->is_connected() ) {
427
- return;
428
  }
429
-
430
- parent::log( $message, $log_id );
431
  }
 
432
 
433
- /**
434
- * Logs an API request.
435
- *
436
- * @since 2.0.0
437
- *
438
- * @param array $request request data
439
- * @param array $response response data
440
- * @param null $log_id log ID
441
- */
442
- public function log_api_request( $request, $response, $log_id = null ) {
443
-
444
- // bail if logging isn't enabled
445
- if ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) {
446
- return;
447
- }
448
-
449
- // Maybe remove headers from the debug log.
450
- if( ! $this->get_integration()->are_headers_requested_for_debug() ) {
451
- unset( $request['headers'] );
452
- unset( $response['headers'] );
453
- }
454
 
455
- $this->log( $this->get_api_log_message( $request ), $log_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
 
457
- if ( ! empty( $response ) ) {
458
- $this->log( $this->get_api_log_message( $response ), $log_id );
459
- }
460
- }
461
 
462
- /**
463
- * Remove Product Set metabox from Product edit page
464
- *
465
- * @since 2.3.0
466
- */
467
- public function remove_product_fb_product_set_metabox() {
468
- remove_meta_box( 'fb_product_setdiv', 'product', 'side' );
469
- }
 
 
 
 
 
 
 
470
 
471
- /**
472
- * Register Facebook Product Set Taxonomy
473
- *
474
- * @since 2.3.0
475
- */
476
- public function register_custom_taxonomy() {
477
-
478
- $plural = esc_html__( 'Facebook Product Sets', 'facebook-for-woocommerce' );
479
- $singular = esc_html__( 'Facebook Product Set', 'facebook-for-woocommerce' );
480
-
481
- $args = array(
482
- 'labels' => array(
483
- 'name' => $plural,
484
- 'singular_name' => $singular,
485
- 'menu_name' => $plural,
486
- // translators: Edit item label
487
- 'edit_item' => sprintf( esc_html__( 'Edit %s', 'facebook-for-woocommerce' ), $singular ),
488
- // translators: Add new label
489
- 'add_new_item' => sprintf( esc_html__( 'Add new %s', 'facebook-for-woocommerce' ), $singular ),
490
- 'menu_name' => $plural,
491
- // translators: No items found text
492
- 'not_found' => sprintf( esc_html__( 'No %s found.', 'facebook-for-woocommerce' ), $plural ),
493
- // translators: Search label
494
- 'search_items' => sprintf( esc_html__( 'Search %s.', 'facebook-for-woocommerce' ), $plural ),
495
- // translators: Text label
496
- 'separate_items_with_commas' => sprintf( esc_html__( 'Separate %s with commas', 'facebook-for-woocommerce' ), $plural ),
497
- // translators: Text label
498
- 'choose_from_most_used' => sprintf( esc_html__( 'Choose from the most used %s', 'facebook-for-woocommerce' ), $plural ),
499
- ),
500
- 'hierarchical' => true,
501
- 'public' => true,
502
- 'show_in_nav_menus' => false,
503
- 'show_tagcloud' => false,
504
- 'show_in_menu' => false,
505
  );
506
-
507
- register_taxonomy( 'fb_product_set', array( 'product' ), $args );
508
- }
509
-
510
-
511
- /**
512
- * Filter Facebook Product Set Taxonomy table links
513
- *
514
- * @since 2.3.0
515
- *
516
- * @param array $actions Item Actions.
517
- *
518
- * @return array
519
- */
520
- public function product_set_links( $actions ) {
521
- unset( $actions['inline hide-if-no-js'] );
522
- unset( $actions['view'] );
523
- return $actions;
524
  }
 
525
 
 
 
 
 
 
 
 
 
 
526
 
527
- /**
528
- * Remove posts count column from Facebook Product Set custom taxonomy
529
- *
530
- * @since 2.3.0
531
- *
532
- * @param array $columns Taxonomy columns.
533
- *
534
- * @return array
535
- */
536
- public function manage_fb_product_set_columns( $columns ) {
537
- unset( $columns['posts'] );
538
- return $columns;
539
  }
 
 
540
 
 
 
 
541
 
542
- /**
543
- * Filter WC Breadcrumbs when the page is Facebook Product Sets
544
- *
545
- * @since 2.3.0
546
- *
547
- * @param array $breadcrumbs Page breadcrumbs.
548
- *
549
- * @return array
550
- */
551
- public function wc_page_breadcrumbs_filter( $breadcrumbs ) {
552
-
553
- if ( 'edit-fb_product_set' !== $this->get_current_page_id() ) {
554
- return $breadcrumbs;
555
- }
556
-
557
- $breadcrumbs = array(
558
- array( 'admin.php?page=wc-admin', 'WooCommerce' ),
559
- array( 'edit.php?post_type=product', 'Products' ),
560
- );
561
-
562
- $term_id = empty( $_GET['tag_ID'] ) ? '' : $_GET['tag_ID']; //phpcs:ignore WordPress.Security
563
-
564
- if ( ! empty( $term_id ) ) {
565
- $breadcrumbs[] = array( 'edit-tags.php?taxonomy=fb_product_set&post_type=product', 'Products Sets' );
566
- }
567
-
568
- $breadcrumbs[] = ( empty( $term_id ) ? 'Product Sets' : 'Edit Product Set' );
569
-
570
- return $breadcrumbs;
571
  }
572
 
 
 
573
 
574
- /**
575
- * Return that Facebook Product Set page is a WC Conected Page
576
- *
577
- * @since 2.3.0
578
- *
579
- * @param boolean $is_conected If it's connected or not.
580
- *
581
- * @return boolean
582
- */
583
- public function is_current_page_conected_filter( $is_conected ) {
584
-
585
- if ( 'edit-fb_product_set' === $this->get_current_page_id() ) {
586
- return true;
587
- }
588
-
589
- return $is_conected;
590
- }
591
-
592
- /**
593
- * Filter is responsible to always set latin user agent header value, because translated plugin names
594
- * may contain characters which Facebook does not accept and return 400 response for requests with such
595
- * header values.
596
- * Applying either sanitize_title() nor remove_accents() on header value will not work for all the languages
597
- * we support translations to e.g. Hebrew is going to convert into something %d7%90%d7%a8%d7%99%d7%92 which is
598
- * not acceptable neither.
599
- *
600
- * @param array $http_request_headers - http request headers
601
- * @return array
602
- */
603
- public function force_user_agent_in_latin( array $http_request_headers ) {
604
- if ( isset( $http_request_headers['user-agent'] ) ) {
605
- $http_request_headers['user-agent'] = sprintf(
606
- '%s/%s (WooCommerce/%s; WordPress/%s)',
607
- WC_Facebookcommerce::PLUGIN_USER_AGENT_NAME,
608
- WC_Facebookcommerce::PLUGIN_VERSION,
609
- defined( 'WC_VERSION' ) ? WC_VERSION : WC_Facebook_Loader::MINIMUM_WC_VERSION,
610
- $GLOBALS['wp_version']
611
- );
612
- }
613
- return $http_request_headers;
614
  }
615
 
616
-
617
- /** Getter methods ********************************************************************************************/
618
-
619
-
620
- /**
621
- * Gets the API instance.
622
- *
623
- * @since 2.0.0
624
- *
625
- * @param string $access_token access token to use for this API request
626
- * @return \SkyVerge\WooCommerce\Facebook\API
627
- * @throws Framework\SV_WC_API_Exception
628
- */
629
- public function get_api( $access_token = '' ) {
630
-
631
- // if none provided, use the general access token
632
- if ( ! $access_token ) {
633
- $access_token = $this->get_connection_handler()->get_access_token();
634
- }
635
-
636
- if ( ! is_object( $this->api ) ) {
637
-
638
- if ( ! $access_token ) {
639
- throw new Framework\SV_WC_API_Exception( __( 'Cannot create the API instance because the access token is missing.', 'facebook-for-woocommerce' ) );
640
- }
641
-
642
- if ( ! class_exists( API\Traits\Rate_Limited_API::class ) ) {
643
- require_once __DIR__ . '/includes/API/Traits/Rate_Limited_API.php';
644
- }
645
-
646
- if ( ! class_exists( API\Traits\Rate_Limited_Request::class ) ) {
647
- require_once __DIR__ . '/includes/API/Traits/Rate_Limited_Request.php';
648
- }
649
-
650
- if ( ! class_exists( API\Traits\Rate_Limited_Response::class ) ) {
651
- require_once __DIR__ . '/includes/API/Traits/Rate_Limited_Response.php';
652
- }
653
-
654
- if ( ! trait_exists( API\Traits\Paginated_Response::class, false ) ) {
655
- require_once __DIR__ . '/includes/API/Traits/Paginated_Response.php';
656
- }
657
-
658
- if ( ! trait_exists( API\Traits\Idempotent_Request::class, false ) ) {
659
- require_once __DIR__ . '/includes/API/Traits/Idempotent_Request.php';
660
- }
661
-
662
- if ( ! class_exists( API::class ) ) {
663
- require_once __DIR__ . '/includes/API.php';
664
- }
665
-
666
- if ( ! class_exists( API\Request::class ) ) {
667
- require_once __DIR__ . '/includes/API/Request.php';
668
- }
669
-
670
- if ( ! class_exists( API\Response::class ) ) {
671
- require_once __DIR__ . '/includes/API/Response.php';
672
- }
673
-
674
- if ( ! class_exists( API\Pixel\Events\Request::class ) ) {
675
- require_once __DIR__ . '/includes/API/Pixel/Events/Request.php';
676
- }
677
-
678
- if ( ! class_exists( API\Catalog\Request::class ) ) {
679
- require_once __DIR__ . '/includes/API/Catalog/Request.php';
680
- }
681
-
682
- if ( ! class_exists( API\Catalog\Response::class ) ) {
683
- require_once __DIR__ . '/includes/API/Catalog/Response.php';
684
- }
685
-
686
- if ( ! class_exists( API\Catalog\Send_Item_Updates\Request::class ) ) {
687
- require_once __DIR__ . '/includes/API/Catalog/Send_Item_Updates/Request.php';
688
- }
689
-
690
- if ( ! class_exists( API\Catalog\Send_Item_Updates\Response::class ) ) {
691
- require_once __DIR__ . '/includes/API/Catalog/Send_Item_Updates/Response.php';
692
- }
693
-
694
- if ( ! class_exists( API\Catalog\Product_Group\Products\Read\Request::class ) ) {
695
- require_once __DIR__ . '/includes/API/Catalog/Product_Group/Products/Read/Request.php';
696
- }
697
-
698
- if ( ! class_exists( API\Catalog\Product_Group\Products\Read\Response::class ) ) {
699
- require_once __DIR__ . '/includes/API/Catalog/Product_Group/Products/Read/Response.php';
700
- }
701
-
702
- if ( ! class_exists( API\Catalog\Product_Item\Response::class ) ) {
703
- require_once __DIR__ . '/includes/API/Catalog/Product_Item/Response.php';
704
- }
705
-
706
- if ( ! class_exists( API\Catalog\Product_Item\Find\Request::class ) ) {
707
- require_once __DIR__ . '/includes/API/Catalog/Product_Item/Find/Request.php';
708
- }
709
-
710
- if ( ! class_exists( API\User\Request::class ) ) {
711
- require_once __DIR__ . '/includes/API/User/Request.php';
712
- }
713
-
714
- if ( ! class_exists( API\User\Response::class ) ) {
715
- require_once __DIR__ . '/includes/API/User/Response.php';
716
- }
717
-
718
- if ( ! class_exists( API\User\Permissions\Delete\Request::class ) ) {
719
- require_once __DIR__ . '/includes/API/User/Permissions/Delete/Request.php';
720
- }
721
-
722
- if ( ! class_exists( API\FBE\Installation\Request::class ) ) {
723
- require_once __DIR__ . '/includes/API/FBE/Installation/Request.php';
724
- }
725
-
726
- if ( ! class_exists( API\FBE\Installation\Read\Request::class ) ) {
727
- require_once __DIR__ . '/includes/API/FBE/Installation/Read/Request.php';
728
- }
729
-
730
- if ( ! class_exists( API\FBE\Installation\Read\Response::class ) ) {
731
- require_once __DIR__ . '/includes/API/FBE/Installation/Read/Response.php';
732
- }
733
-
734
- if ( ! class_exists( API\FBE\Configuration\Request::class ) ) {
735
- require_once __DIR__ . '/includes/API/FBE/Configuration/Request.php';
736
- }
737
-
738
- if ( ! class_exists( API\FBE\Configuration\Messenger::class ) ) {
739
- require_once __DIR__ . '/includes/API/FBE/Configuration/Messenger.php';
740
- }
741
-
742
- if ( ! class_exists( API\FBE\Configuration\Read\Response::class ) ) {
743
- require_once __DIR__ . '/includes/API/FBE/Configuration/Read/Response.php';
744
- }
745
-
746
- if ( ! class_exists( API\FBE\Configuration\Update\Request::class ) ) {
747
- require_once __DIR__ . '/includes/API/FBE/Configuration/Update/Request.php';
748
- }
749
-
750
- if ( ! class_exists( API\Pages\Read\Request::class ) ) {
751
- require_once __DIR__ . '/includes/API/Pages/Read/Request.php';
752
- }
753
-
754
- if ( ! class_exists( API\Pages\Read\Response::class ) ) {
755
- require_once __DIR__ . '/includes/API/Pages/Read/Response.php';
756
- }
757
-
758
- if ( ! class_exists( API\Exceptions\Request_Limit_Reached::class ) ) {
759
- require_once __DIR__ . '/includes/API/Exceptions/Request_Limit_Reached.php';
760
- }
761
-
762
- if ( ! class_exists( API\Orders\Order::class ) ) {
763
- require_once __DIR__ . '/includes/API/Orders/Order.php';
764
- }
765
-
766
- if ( ! class_exists( API\Orders\Abstract_Request::class ) ) {
767
- require_once __DIR__ . '/includes/API/Orders/Abstract_Request.php';
768
- }
769
-
770
- if ( ! class_exists( API\Orders\Acknowledge\Request::class ) ) {
771
- require_once __DIR__ . '/includes/API/Orders/Acknowledge/Request.php';
772
- }
773
-
774
- if ( ! class_exists( API\Orders\Cancel\Request::class ) ) {
775
- require_once __DIR__ . '/includes/API/Orders/Cancel/Request.php';
776
- }
777
-
778
- if ( ! class_exists( API\Orders\Fulfillment\Request::class ) ) {
779
- require_once __DIR__ . '/includes/API/Orders/Fulfillment/Request.php';
780
- }
781
-
782
- if ( ! class_exists( API\Orders\Read\Request::class ) ) {
783
- require_once __DIR__ . '/includes/API/Orders/Read/Request.php';
784
- }
785
-
786
- if ( ! class_exists( API\Orders\Read\Response::class ) ) {
787
- require_once __DIR__ . '/includes/API/Orders/Read/Response.php';
788
- }
789
-
790
- if ( ! class_exists( API\Orders\Refund\Request::class ) ) {
791
- require_once __DIR__ . '/includes/API/Orders/Refund/Request.php';
792
- }
793
-
794
- if ( ! class_exists( API\Orders\Request::class ) ) {
795
- require_once __DIR__ . '/includes/API/Orders/Request.php';
796
- }
797
-
798
- if ( ! class_exists( API\Orders\Response::class ) ) {
799
- require_once __DIR__ . '/includes/API/Orders/Response.php';
800
- }
801
-
802
- $this->api = new SkyVerge\WooCommerce\Facebook\API( $access_token );
803
-
804
- } else {
805
-
806
- $this->api->set_access_token( $access_token );
807
- }
808
-
809
- return $this->api;
810
  }
811
 
 
812
 
813
- /**
814
- * Gets the admin handler instance.
815
- *
816
- * @since 1.10.0
817
- *
818
- * @return \SkyVerge\WooCommerce\Facebook\Admin|null
819
- */
820
- public function get_admin_handler() {
821
-
822
- return $this->admin;
823
  }
 
824
 
 
 
 
 
 
 
 
 
825
 
826
- /**
827
- * Gets the AJAX handler instance.
828
- *
829
- * @sinxe 1.10.0
830
- *
831
- * @return \SkyVerge\WooCommerce\Facebook\AJAX|null
832
- */
833
- public function get_ajax_handler() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
 
835
- return $this->ajax;
836
- }
837
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838
 
839
- /**
840
- * Gets the product feed handler.
841
- *
842
- * @since 1.11.0
843
- *
844
- * @return \SkyVerge\WooCommerce\Facebook\Products\Feed
845
- */
846
- public function get_product_feed_handler() {
847
 
848
- return $this->product_feed;
849
- }
 
 
 
 
 
 
 
 
 
 
 
850
 
851
- /**
852
- * Gets the category handler.
853
- *
854
- * @since 1.11.0
855
- *
856
- * @return \SkyVerge\WooCommerce\Facebook\Products\FBCategories
857
- */
858
- public function get_facebook_category_handler() {
859
- return $this->fb_categories;
860
- }
861
 
862
- /**
863
- * Gets the background handle virtual products and variations handler instance.
864
- *
865
- * @since 2.0.0
866
- *
867
- * @return Background_Handle_Virtual_Products_Variations
868
- */
869
- public function get_background_handle_virtual_products_variations_instance() {
 
 
870
 
871
- return $this->background_handle_virtual_products_variations;
 
872
  }
873
 
 
 
 
 
874
 
875
- /**
876
- * Gets the background remove duplicate visibility meta data handler instance.
877
- *
878
- * @since 2.0.3
879
- *
880
- * @return Background_Remove_Duplicate_Visibility_Meta
881
- */
882
- public function get_background_remove_duplicate_visibility_meta_instance() {
883
-
884
- return $this->background_remove_duplicate_visibility_meta;
885
  }
886
 
 
 
 
887
 
888
- /**
889
- * Gets the products stock handler.
890
- *
891
- * @since 2.0.5
892
- *
893
- * @return \SkyVerge\WooCommerce\Facebook\Products\Stock
894
- */
895
- public function get_products_stock_handler() {
896
 
897
- return $this->products_stock_handler;
 
 
 
 
 
 
 
 
 
 
 
898
  }
899
 
 
 
900
 
901
- /**
902
- * Gets the products sync handler.
903
- *
904
- * @since 2.0.0
905
- *
906
- * @return \SkyVerge\WooCommerce\Facebook\Products\Sync
907
- */
908
- public function get_products_sync_handler() {
909
-
910
- return $this->products_sync_handler;
 
 
 
 
 
 
 
 
 
 
911
  }
 
 
912
 
913
 
914
- /**
915
- * Gets the products sync background handler.
916
- *
917
- * @since 2.0.0
918
- *
919
- * @return \SkyVerge\WooCommerce\Facebook\Products\Sync\Background
920
- */
921
- public function get_products_sync_background_handler() {
922
-
923
- return $this->sync_background_handler;
924
- }
925
-
926
 
927
- /**
928
- * Gets the connection handler.
929
- *
930
- * @since 2.0.0
931
- *
932
- * @return \SkyVerge\WooCommerce\Facebook\Handlers\Connection
933
- */
934
- public function get_connection_handler() {
935
 
936
- return $this->connection_handler;
 
 
 
 
 
 
 
 
 
 
 
 
937
  }
938
-
939
-
940
- /**
941
- * Gets the integration instance.
942
- *
943
- * @since 1.10.0
944
- *
945
- * @return \WC_Facebookcommerce_Integration instance
946
- */
947
- public function get_integration() {
948
-
949
- if ( null === $this->integration ) {
950
- $this->integration = new WC_Facebookcommerce_Integration();
951
  }
952
-
953
- return $this->integration;
954
- }
955
-
956
-
957
- /**
958
- * Gets the commerce handler instance.
959
- *
960
- * @since 2.1.0
961
- *
962
- * @return \SkyVerge\WooCommerce\Facebook\Commerce commerce handler instance
963
- */
964
- public function get_commerce_handler() {
965
-
966
- return $this->commerce_handler;
967
  }
 
 
968
 
969
- /**
970
- * Gets tracker instance.
971
- *
972
- * @since 2.6.0
973
- *
974
- * @return \SkyVerge\WooCommerce\Facebook\Utilities\Tracker
975
- */
976
- public function get_tracker() {
 
 
977
 
978
- return $this->tracker;
979
- }
 
 
 
 
 
 
 
 
980
 
981
- /**
982
- * Gets the debug profiling logger instance.
983
- *
984
- * @return \SkyVerge\WooCommerce\Facebook\Debug\ProfilingLogger
985
- */
986
- public function get_profiling_logger() {
987
- static $instance = null;
988
- if ( null === $instance ) {
989
- $is_enabled = defined( 'FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED' ) && FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED;
990
- $instance = new \SkyVerge\WooCommerce\Facebook\Debug\ProfilingLogger( $is_enabled );
991
- }
992
 
993
- return $instance;
994
- }
 
 
 
 
 
 
 
 
995
 
996
- /**
997
- * Get the product sync validator class.
998
- *
999
- * @param WC_Product $product A product object to be validated.
1000
- *
1001
- * @return ProductSyncValidator
1002
- */
1003
- public function get_product_sync_validator( WC_Product $product ) {
1004
- return new ProductSyncValidator( $this->get_integration(), $product );
1005
- }
1006
 
1007
- /**
1008
- * Gets the settings page URL.
1009
- *
1010
- * @since 1.10.0
1011
- *
1012
- * @param null $plugin_id unused
1013
- * @return string
1014
- */
1015
- public function get_settings_url( $plugin_id = null ) {
1016
-
1017
- return admin_url( 'admin.php?page=wc-facebook' );
1018
- }
1019
-
1020
- /**
1021
- * Gets the advertise tab page URL.
1022
- *
1023
- * @since 2.6.29
1024
- *
1025
- * @return string
1026
- */
1027
- public function get_advertise_tab_url() {
1028
-
1029
- return admin_url( 'admin.php?page=wc-facebook&tab=advertise' );
1030
- }
1031
-
1032
-
1033
-
1034
- /**
1035
- * Gets the plugin's documentation URL.
1036
- *
1037
- * @since 1.10.0
1038
- *
1039
- * @return string
1040
- */
1041
- public function get_documentation_url() {
1042
-
1043
- return 'https://docs.woocommerce.com/document/facebook-for-woocommerce/';
1044
- }
1045
 
1046
 
1047
- /**
1048
- * Gets the plugin's support URL.
1049
- *
1050
- * @since 1.10.0
1051
- *
1052
- * @return string
1053
- */
1054
- public function get_support_url() {
 
 
1055
 
1056
- return 'https://wordpress.org/support/plugin/facebook-for-woocommerce/';
1057
- }
1058
 
 
 
 
 
 
 
 
 
 
 
1059
 
1060
- /**
1061
- * Gets the plugin's sales page URL.
1062
- *
1063
- * @since 1.10.0
1064
- *
1065
- * @return string
1066
- */
1067
- public function get_sales_page_url() {
1068
 
1069
- return 'https://woocommerce.com/products/facebook/';
 
 
 
 
 
 
 
 
 
1070
  }
1071
 
1072
-
1073
- /**
1074
- * Gets the plugin's reviews URL.
1075
- *
1076
- * @since 1.10.0
1077
- *
1078
- * @return string
1079
- */
1080
- public function get_reviews_url() {
1081
-
1082
- return 'https://wordpress.org/support/plugin/facebook-for-woocommerce/reviews/';
1083
- }
1084
 
1085
 
1086
- /**
1087
- * Gets the plugin name.
1088
- *
1089
- * @since 1.10.0
1090
- *
1091
- * @return string
1092
- */
1093
- public function get_plugin_name() {
 
 
1094
 
1095
- return __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' );
1096
- }
 
 
 
 
 
 
 
 
1097
 
1098
- /**
1099
- * Gets the url for the assets build directory.
1100
- *
1101
- * @since 2.3.4
1102
- *
1103
- * @return string
1104
- */
1105
- public function get_asset_build_dir_url() {
1106
- return $this->get_plugin_url() . '/assets/build';
 
1107
  }
1108
 
 
 
1109
 
1110
- /** Conditional methods ***************************************************************************************/
1111
-
1112
-
1113
- /**
1114
- * Determines if viewing the plugin settings in the admin.
1115
- *
1116
- * @since 1.10.0
1117
- *
1118
- * @return bool
1119
- */
1120
- public function is_plugin_settings() {
1121
 
1122
- return is_admin() && \SkyVerge\WooCommerce\Facebook\Admin\Settings::PAGE_ID === Framework\SV_WC_Helper::get_requested_value( 'page' );
1123
- }
 
 
 
 
 
 
 
 
1124
 
 
 
 
 
 
 
 
 
 
 
 
1125
 
1126
- /** Utility methods *******************************************************************************************/
 
 
 
 
 
 
 
 
 
1127
 
1128
 
1129
- /**
1130
- * Initializes the lifecycle handler.
1131
- *
1132
- * @since 1.10.0
1133
- */
1134
- protected function init_lifecycle_handler() {
 
 
 
 
1135
 
1136
- require_once __DIR__ . '/includes/Lifecycle.php';
1137
 
1138
- $this->lifecycle_handler = new Lifecycle( $this );
1139
- }
 
 
 
 
 
 
 
 
1140
 
1141
 
1142
- /**
1143
- * Gets the plugin singleton instance.
1144
- *
1145
- * @see \facebook_for_woocommerce()
1146
- *
1147
- * @since 1.10.0
1148
- *
1149
- * @return \WC_Facebookcommerce the plugin singleton instance
1150
- */
1151
- public static function instance() {
1152
 
1153
- if ( null === self::$instance ) {
1154
- self::$instance = new self();
1155
- }
1156
 
1157
- return self::$instance;
1158
- }
 
 
 
 
 
 
 
 
1159
 
 
 
 
 
 
 
 
 
 
 
1160
 
1161
- /**
1162
- * Gets the plugin file.
1163
- *
1164
- * @since 1.10.0
1165
- *
1166
- * @return string
1167
- */
1168
- protected function get_file() {
1169
 
1170
- return __FILE__;
1171
- }
1172
 
1173
 
1174
- /**
1175
- * Return current page ID
1176
- *
1177
- * @since 2.3.0
1178
- *
1179
- * @return string
1180
- */
1181
- protected function get_current_page_id() {
 
 
1182
 
1183
- $current_screen_id = '';
1184
- $current_screen = get_current_screen();
1185
- if ( ! empty( $current_screen ) ) {
1186
- $current_screen_id = $current_screen->id;
1187
- }
1188
- return $current_screen_id;
1189
- }
1190
 
 
1191
 
1192
- /** Deprecated methods ****************************************************************************************/
1193
 
 
 
 
 
 
 
 
 
1194
 
1195
- /**
1196
- * Adds the settings link on the plugin page.
1197
- *
1198
- * @internal
1199
- *
1200
- * @since 1.10.0
1201
- * @deprecated 1.10.0
1202
- */
1203
- public function add_settings_link() {
1204
 
1205
- wc_deprecated_function( __METHOD__, '1.10.0' );
 
 
 
 
 
 
 
 
 
 
 
1206
  }
1207
-
1208
  }
1209
 
1210
 
1211
  /**
1212
- * Gets the Facebook for WooCommerce plugin instance.
1213
  *
1214
  * @since 1.10.0
1215
  *
1216
- * @return \WC_Facebookcommerce instance of the plugin
1217
  */
1218
- function facebook_for_woocommerce() {
 
 
 
1219
 
1220
- return \WC_Facebookcommerce::instance();
 
 
 
 
 
 
 
 
 
 
 
 
 
1221
  }
 
 
1222
 
 
 
 
 
 
 
 
 
 
 
1223
 
1224
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ require_once __DIR__ . '/vendor/autoload.php';
13
+ require_once __DIR__ . '/includes/fbutils.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
16
+ use WooCommerce\Facebook\Admin\Tasks\Setup;
17
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
18
+ use WooCommerce\Facebook\Framework\Helper;
19
+ use WooCommerce\Facebook\Integrations\Bookings as BookingsIntegration;
20
+ use WooCommerce\Facebook\Lifecycle;
21
+ use WooCommerce\Facebook\ProductSync\ProductValidator as ProductSyncValidator;
22
+ use WooCommerce\Facebook\Utilities\Background_Handle_Virtual_Products_Variations;
23
+ use WooCommerce\Facebook\Utilities\Background_Remove_Duplicate_Visibility_Meta;
24
+ use WooCommerce\Facebook\Utilities\Heartbeat;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ class WC_Facebookcommerce extends WooCommerce\Facebook\Framework\Plugin {
27
+ /** @var string the plugin version */
28
+ const VERSION = WC_Facebook_Loader::PLUGIN_VERSION;
29
 
30
+ /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
31
+ const PLUGIN_VERSION = self::VERSION;
32
 
33
+ /** @var string the plugin ID */
34
+ const PLUGIN_ID = 'facebook_for_woocommerce';
35
 
36
+ /** @var string the integration ID */
37
+ const INTEGRATION_ID = 'facebookcommerce';
 
38
 
39
+ /** @var string the product set categories meta name */
40
+ const PRODUCT_SET_META = '_wc_facebook_product_cats';
41
 
42
+ /** @var string the plugin user agent name to use for HTTP calls within User-Agent header */
43
+ const PLUGIN_USER_AGENT_NAME = 'Facebook-for-WooCommerce';
 
 
 
 
 
 
44
 
45
+ /** @var WC_Facebookcommerce singleton instance */
46
+ protected static $instance;
 
47
 
48
+ /** @var WooCommerce\Facebook\API instance */
49
+ private $api;
 
 
50
 
51
+ /** @var \WC_Facebookcommerce_Integration instance */
52
+ private $integration;
53
 
54
+ /** @var WooCommerce\Facebook\Admin admin handler instance */
55
+ private $admin;
56
 
57
+ /** @var WooCommerce\Facebook\Admin\Settings */
58
+ private $admin_settings;
59
 
60
+ /** @var WooCommerce\Facebook\AJAX Ajax handler instance */
61
+ private $ajax;
62
 
63
+ /** @var WooCommerce\Facebook\Products\Feed product feed handler */
64
+ private $product_feed;
65
 
66
+ /** @var Background_Handle_Virtual_Products_Variations instance */
67
+ protected $background_handle_virtual_products_variations;
68
 
69
+ /** @var Background_Remove_Duplicate_Visibility_Meta job handler instance */
70
+ protected $background_remove_duplicate_visibility_meta;
71
 
72
+ /** @var WooCommerce\Facebook\Products\Stock products stock handler */
73
+ private $products_stock_handler;
74
 
75
+ /** @var WooCommerce\Facebook\Products\Sync products sync handler */
76
+ private $products_sync_handler;
77
 
78
+ /** @var WooCommerce\Facebook\Products\Sync\Background background sync handler */
79
+ private $sync_background_handler;
 
80
 
81
+ /** @var WooCommerce\Facebook\ProductSets\Sync product sets sync handler */
82
+ private $product_sets_sync_handler;
83
 
84
+ /** @var WooCommerce\Facebook\Handlers\Connection connection handler */
85
+ private $connection_handler;
 
 
 
 
 
 
 
86
 
87
+ /** @var WooCommerce\Facebook\Handlers\WebHook webhook handler */
88
+ private $webhook_handler;
 
 
89
 
90
+ /** @var WooCommerce\Facebook\Commerce commerce handler */
91
+ private $commerce_handler;
92
 
93
+ /** @var WooCommerce\Facebook\Utilities\Tracker */
94
+ private $tracker;
 
 
 
 
 
 
95
 
96
+ /** @var WooCommerce\Facebook\Jobs\JobManager */
97
+ public $job_manager;
98
 
99
+ /** @var WooCommerce\Facebook\Utilities\Heartbeat */
100
+ public $heartbeat;
101
 
102
+ /**
103
+ * Constructs the plugin.
104
+ *
105
+ * @since 1.0.0
106
+ */
107
+ public function __construct() {
108
+ parent::__construct(
109
+ self::PLUGIN_ID,
110
+ self::VERSION,
111
+ [ 'text_domain' => 'facebook-for-woocommerce' ]
112
+ );
113
+ $this->init();
114
+ $this->init_admin();
115
+ }
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ /**
119
+ * Initializes the plugin.
120
+ *
121
+ * @internal
122
+ */
123
+ public function init() {
124
+ add_action( 'init', array( $this, 'get_integration' ) );
125
+ add_action( 'init', array( $this, 'register_custom_taxonomy' ) );
126
+ add_action( 'add_meta_boxes_product', array( $this, 'remove_product_fb_product_set_metabox' ), 50 );
127
+ add_filter( 'fb_product_set_row_actions', array( $this, 'product_set_links' ) );
128
+ add_filter( 'manage_edit-fb_product_set_columns', array( $this, 'manage_fb_product_set_columns' ) );
129
+
130
+ // Hook the setup task. The hook admin_init is not triggered when the WC fetches the tasks using the endpoint: wp-json/wc-admin/onboarding/tasks and hence hooking into init.
131
+ add_action( 'init', array( $this, 'add_setup_task' ), 20 );
132
+
133
+ // Product Set breadcrumb filters
134
+ add_filter( 'woocommerce_navigation_is_connected_page', array( $this, 'is_current_page_conected_filter' ), 99, 2 );
135
+ add_filter( 'woocommerce_navigation_get_breadcrumbs', array( $this, 'wc_page_breadcrumbs_filter' ), 99 );
136
+
137
+ add_filter(
138
+ 'wc_' . WC_Facebookcommerce::PLUGIN_ID . '_http_request_args',
139
+ array( $this, 'force_user_agent_in_latin' )
140
+ );
141
+
142
+ if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
143
+ include_once 'facebook-commerce.php';
144
+
145
+ require_once __DIR__ . '/includes/fbproductfeed.php';
146
+ require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
147
+
148
+ $this->heartbeat = new Heartbeat( WC()->queue() );
149
+ $this->heartbeat->init();
150
+
151
+ $this->product_feed = new WooCommerce\Facebook\Products\Feed();
152
+ $this->products_stock_handler = new WooCommerce\Facebook\Products\Stock();
153
+ $this->products_sync_handler = new WooCommerce\Facebook\Products\Sync();
154
+ $this->sync_background_handler = new WooCommerce\Facebook\Products\Sync\Background();
155
+ $this->configuration_detection = new WooCommerce\Facebook\Feed\FeedConfigurationDetection();
156
+ $this->product_sets_sync_handler = new WooCommerce\Facebook\ProductSets\Sync();
157
+ $this->commerce_handler = new WooCommerce\Facebook\Commerce();
158
+ $this->fb_categories = new WooCommerce\Facebook\Products\FBCategories();
159
+
160
+ if ( wp_doing_ajax() ) {
161
+ $this->ajax = new WooCommerce\Facebook\AJAX();
162
  }
 
163
 
164
+ // Load integrations.
165
+ require_once __DIR__ . '/includes/fbwpml.php';
166
+ new WC_Facebook_WPML_Injector();
167
+ new BookingsIntegration();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ if ( 'yes' !== get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) ) {
170
+ $this->background_handle_virtual_products_variations = new Background_Handle_Virtual_Products_Variations();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  }
172
 
173
+ if ( 'yes' !== get_option( 'wc_facebook_background_remove_duplicate_visibility_meta_complete', 'no' ) ) {
174
+ $this->background_remove_duplicate_visibility_meta = new Background_Remove_Duplicate_Visibility_Meta();
 
 
 
 
 
 
 
 
 
 
175
  }
 
176
 
177
+ $this->connection_handler = new WooCommerce\Facebook\Handlers\Connection( $this );
178
+ $this->webhook_handler = new WooCommerce\Facebook\Handlers\WebHook( $this );
179
+ $this->tracker = new WooCommerce\Facebook\Utilities\Tracker();
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
+ // Init jobs
182
+ $this->job_manager = new WooCommerce\Facebook\Jobs\JobManager();
183
+ add_action( 'init', [ $this->job_manager, 'init' ] );
184
 
185
+ // load admin handlers, before admin_init
186
+ if ( is_admin() ) {
187
+ $this->admin_settings = new WooCommerce\Facebook\Admin\Settings();
 
 
 
 
 
 
 
 
 
188
  }
 
 
189
  }
190
+ }
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ /**
194
+ * Initializes the admin handling.
195
+ *
196
+ * @internal
197
+ *
198
+ * @since 1.10.0
199
+ */
200
+ public function init_admin() {
201
+ add_action(
202
+ 'admin_init',
203
+ function () {
204
+ $this->admin = new WooCommerce\Facebook\Admin();
205
+ },
206
+ 0
207
+ );
208
+ }
209
 
 
 
 
 
210
 
211
+ /**
212
+ * Gets deprecated and removed hooks.
213
+ *
214
+ * @since 2.1.0
215
+ *
216
+ * @return array
217
+ */
218
+ protected function get_deprecated_hooks() {
219
+ return array(
220
+ 'wc_facebook_page_access_token' => array(
221
+ 'version' => '2.1.0',
222
+ 'replacement' => false,
223
+ ),
224
+ );
225
+ }
226
 
227
+ /**
228
+ * Adds the setup task to the Tasklists.
229
+ *
230
+ * @since 2.6.29
231
+ */
232
+ public function add_setup_task() {
233
+ if ( class_exists( TaskLists::class ) ) { // This is added for backward compatibility.
234
+ TaskLists::add_task(
235
+ 'extended',
236
+ new Setup(
237
+ TaskLists::get_list( 'extended' )
238
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  }
241
+ }
242
 
243
+ /**
244
+ * Get the last event from the plugin lifecycle.
245
+ *
246
+ * @since 2.6.29
247
+ * @return array
248
+ */
249
+ public function get_last_event_from_history() {
250
+ $last_event = array();
251
+ $history_events = $this->lifecycle_handler->get_event_history();
252
 
253
+ if ( isset( $history_events[0] ) ) {
254
+ $last_event = $history_events[0];
 
 
 
 
 
 
 
 
 
 
255
  }
256
+ return $last_event;
257
+ }
258
 
259
+ public function add_wordpress_integration() {
260
+ new WP_Facebook_Integration();
261
+ }
262
 
263
+ /**
264
+ * Saves errors or messages to WooCommerce Log (woocommerce/logs/plugin-id-xxx.txt)
265
+ *
266
+ * @since 2.3.3
267
+ * @param string $message error or message to save to log
268
+ * @param string $log_id optional log id to segment the files by, defaults to plugin id
269
+ */
270
+ public function log( $message, $log_id = null ) {
271
+ // Bail if site is connected and user has disabled logging.
272
+ // If site is disconnected, force-enable logging so merchant can diagnose connection issues.
273
+ if ( ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) && $this->get_connection_handler()->is_connected() ) {
274
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
 
277
+ parent::log( $message, $log_id );
278
+ }
279
 
280
+ /**
281
+ * Logs an API request.
282
+ *
283
+ * @since 2.0.0
284
+ *
285
+ * @param array $request request data
286
+ * @param array $response response data
287
+ * @param null $log_id log ID
288
+ */
289
+ public function log_api_request( $request, $response, $log_id = null ) {
290
+ // bail if logging isn't enabled
291
+ if ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) {
292
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
 
295
+ // Maybe remove headers from the debug log.
296
+ if( ! $this->get_integration()->are_headers_requested_for_debug() ) {
297
+ unset( $request['headers'] );
298
+ unset( $response['headers'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
 
301
+ $this->log( $this->get_api_log_message( $request ), $log_id );
302
 
303
+ if ( ! empty( $response ) ) {
304
+ $this->log( $this->get_api_log_message( $response ), $log_id );
 
 
 
 
 
 
 
 
305
  }
306
+ }
307
 
308
+ /**
309
+ * Remove Product Set metabox from Product edit page
310
+ *
311
+ * @since 2.3.0
312
+ */
313
+ public function remove_product_fb_product_set_metabox() {
314
+ remove_meta_box( 'fb_product_setdiv', 'product', 'side' );
315
+ }
316
 
317
+ /**
318
+ * Register Facebook Product Set Taxonomy
319
+ *
320
+ * @since 2.3.0
321
+ */
322
+ public function register_custom_taxonomy() {
323
+ $plural = esc_html__( 'Facebook Product Sets', 'facebook-for-woocommerce' );
324
+ $singular = esc_html__( 'Facebook Product Set', 'facebook-for-woocommerce' );
325
+
326
+ $args = array(
327
+ 'labels' => array(
328
+ 'name' => $plural,
329
+ 'singular_name' => $singular,
330
+ 'menu_name' => $plural,
331
+ // translators: Edit item label
332
+ 'edit_item' => sprintf( esc_html__( 'Edit %s', 'facebook-for-woocommerce' ), $singular ),
333
+ // translators: Add new label
334
+ 'add_new_item' => sprintf( esc_html__( 'Add new %s', 'facebook-for-woocommerce' ), $singular ),
335
+ 'menu_name' => $plural,
336
+ // translators: No items found text
337
+ 'not_found' => sprintf( esc_html__( 'No %s found.', 'facebook-for-woocommerce' ), $plural ),
338
+ // translators: Search label
339
+ 'search_items' => sprintf( esc_html__( 'Search %s.', 'facebook-for-woocommerce' ), $plural ),
340
+ // translators: Text label
341
+ 'separate_items_with_commas' => sprintf( esc_html__( 'Separate %s with commas', 'facebook-for-woocommerce' ), $plural ),
342
+ // translators: Text label
343
+ 'choose_from_most_used' => sprintf( esc_html__( 'Choose from the most used %s', 'facebook-for-woocommerce' ), $plural ),
344
+ ),
345
+ 'hierarchical' => true,
346
+ 'public' => true,
347
+ 'show_in_nav_menus' => false,
348
+ 'show_tagcloud' => false,
349
+ 'show_in_menu' => false,
350
+ );
351
+
352
+ register_taxonomy( 'fb_product_set', array( 'product' ), $args );
353
+ }
354
 
 
 
355
 
356
+ /**
357
+ * Filter Facebook Product Set Taxonomy table links
358
+ *
359
+ * @since 2.3.0
360
+ *
361
+ * @param array $actions Item Actions.
362
+ *
363
+ * @return array
364
+ */
365
+ public function product_set_links( $actions ) {
366
+ unset( $actions['inline hide-if-no-js'] );
367
+ unset( $actions['view'] );
368
+ return $actions;
369
+ }
370
 
 
 
 
 
 
 
 
 
371
 
372
+ /**
373
+ * Remove posts count column from Facebook Product Set custom taxonomy
374
+ *
375
+ * @since 2.3.0
376
+ *
377
+ * @param array $columns Taxonomy columns.
378
+ *
379
+ * @return array
380
+ */
381
+ public function manage_fb_product_set_columns( $columns ) {
382
+ unset( $columns['posts'] );
383
+ return $columns;
384
+ }
385
 
 
 
 
 
 
 
 
 
 
 
386
 
387
+ /**
388
+ * Filter WC Breadcrumbs when the page is Facebook Product Sets
389
+ *
390
+ * @since 2.3.0
391
+ *
392
+ * @param array $breadcrumbs Page breadcrumbs.
393
+ *
394
+ * @return array
395
+ */
396
+ public function wc_page_breadcrumbs_filter( $breadcrumbs ) {
397
 
398
+ if ( 'edit-fb_product_set' !== $this->get_current_page_id() ) {
399
+ return $breadcrumbs;
400
  }
401
 
402
+ $breadcrumbs = array(
403
+ array( 'admin.php?page=wc-admin', 'WooCommerce' ),
404
+ array( 'edit.php?post_type=product', 'Products' ),
405
+ );
406
 
407
+ $term_id = empty( $_GET['tag_ID'] ) ? '' : $_GET['tag_ID']; //phpcs:ignore WordPress.Security
408
+ if ( ! empty( $term_id ) ) {
409
+ $breadcrumbs[] = array( 'edit-tags.php?taxonomy=fb_product_set&post_type=product', 'Products Sets' );
 
 
 
 
 
 
 
410
  }
411
 
412
+ $breadcrumbs[] = ( empty( $term_id ) ? 'Product Sets' : 'Edit Product Set' );
413
+ return $breadcrumbs;
414
+ }
415
 
 
 
 
 
 
 
 
 
416
 
417
+ /**
418
+ * Return that Facebook Product Set page is a WC Conected Page
419
+ *
420
+ * @since 2.3.0
421
+ *
422
+ * @param boolean $is_conected If it's connected or not.
423
+ *
424
+ * @return boolean
425
+ */
426
+ public function is_current_page_conected_filter( $is_conected ) {
427
+ if ( 'edit-fb_product_set' === $this->get_current_page_id() ) {
428
+ return true;
429
  }
430
 
431
+ return $is_conected;
432
+ }
433
 
434
+ /**
435
+ * Filter is responsible to always set latin user agent header value, because translated plugin names
436
+ * may contain characters which Facebook does not accept and return 400 response for requests with such
437
+ * header values.
438
+ * Applying either sanitize_title() nor remove_accents() on header value will not work for all the languages
439
+ * we support translations to e.g. Hebrew is going to convert into something %d7%90%d7%a8%d7%99%d7%92 which is
440
+ * not acceptable neither.
441
+ *
442
+ * @param array $http_request_headers - http request headers
443
+ * @return array
444
+ */
445
+ public function force_user_agent_in_latin( array $http_request_headers ) {
446
+ if ( isset( $http_request_headers['user-agent'] ) ) {
447
+ $http_request_headers['user-agent'] = sprintf(
448
+ '%s/%s (WooCommerce/%s; WordPress/%s)',
449
+ WC_Facebookcommerce::PLUGIN_USER_AGENT_NAME,
450
+ WC_Facebookcommerce::PLUGIN_VERSION,
451
+ defined( 'WC_VERSION' ) ? WC_VERSION : WC_Facebook_Loader::MINIMUM_WC_VERSION,
452
+ $GLOBALS['wp_version']
453
+ );
454
  }
455
+ return $http_request_headers;
456
+ }
457
 
458
 
459
+ /** Getter methods ********************************************************************************************/
 
 
 
 
 
 
 
 
 
 
 
460
 
 
 
 
 
 
 
 
 
461
 
462
+ /**
463
+ * Gets the API instance.
464
+ *
465
+ * @since 2.0.0
466
+ *
467
+ * @param string $access_token access token to use for this API request
468
+ * @return WooCommerce\Facebook\API
469
+ * @throws ApiException
470
+ */
471
+ public function get_api( string $access_token = '' ): WooCommerce\Facebook\API {
472
+ // if none provided, use the general access token
473
+ if ( ! $access_token ) {
474
+ $access_token = $this->get_connection_handler()->get_access_token();
475
  }
476
+ if ( ! is_object( $this->api ) ) {
477
+ if ( ! $access_token ) {
478
+ throw new ApiException( __( 'Cannot create the API instance because the access token is missing.', 'facebook-for-woocommerce' ) );
 
 
 
 
 
 
 
 
 
 
479
  }
480
+ $this->api = new WooCommerce\Facebook\API( $access_token );
481
+ } else {
482
+ $this->api->set_access_token( $access_token );
 
 
 
 
 
 
 
 
 
 
 
 
483
  }
484
+ return $this->api;
485
+ }
486
 
487
+ /**
488
+ * Gets the category handler.
489
+ *
490
+ * @since 1.11.0
491
+ *
492
+ * @return WooCommerce\Facebook\Products\FBCategories
493
+ */
494
+ public function get_facebook_category_handler() {
495
+ return $this->fb_categories;
496
+ }
497
 
498
+ /**
499
+ * Gets the background handle virtual products and variations handler instance.
500
+ *
501
+ * @since 2.0.0
502
+ *
503
+ * @return Background_Handle_Virtual_Products_Variations
504
+ */
505
+ public function get_background_handle_virtual_products_variations_instance() {
506
+ return $this->background_handle_virtual_products_variations;
507
+ }
508
 
 
 
 
 
 
 
 
 
 
 
 
509
 
510
+ /**
511
+ * Gets the background remove duplicate visibility meta data handler instance.
512
+ *
513
+ * @since 2.0.3
514
+ *
515
+ * @return Background_Remove_Duplicate_Visibility_Meta
516
+ */
517
+ public function get_background_remove_duplicate_visibility_meta_instance() {
518
+ return $this->background_remove_duplicate_visibility_meta;
519
+ }
520
 
 
 
 
 
 
 
 
 
 
 
521
 
522
+ /**
523
+ * Gets the products sync handler.
524
+ *
525
+ * @since 2.0.0
526
+ *
527
+ * @return WooCommerce\Facebook\Products\Sync
528
+ */
529
+ public function get_products_sync_handler() {
530
+ return $this->products_sync_handler;
531
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
 
534
+ /**
535
+ * Gets the products sync background handler.
536
+ *
537
+ * @since 2.0.0
538
+ *
539
+ * @return WooCommerce\Facebook\Products\Sync\Background
540
+ */
541
+ public function get_products_sync_background_handler() {
542
+ return $this->sync_background_handler;
543
+ }
544
 
 
 
545
 
546
+ /**
547
+ * Gets the connection handler.
548
+ *
549
+ * @since 2.0.0
550
+ *
551
+ * @return WooCommerce\Facebook\Handlers\Connection
552
+ */
553
+ public function get_connection_handler() {
554
+ return $this->connection_handler;
555
+ }
556
 
 
 
 
 
 
 
 
 
557
 
558
+ /**
559
+ * Gets the integration instance.
560
+ *
561
+ * @since 1.10.0
562
+ *
563
+ * @return WC_Facebookcommerce_Integration instance
564
+ */
565
+ public function get_integration() {
566
+ if ( null === $this->integration ) {
567
+ $this->integration = new WC_Facebookcommerce_Integration( $this );
568
  }
569
 
570
+ return $this->integration;
571
+ }
 
 
 
 
 
 
 
 
 
 
572
 
573
 
574
+ /**
575
+ * Gets the commerce handler instance.
576
+ *
577
+ * @since 2.1.0
578
+ *
579
+ * @return WooCommerce\Facebook\Commerce commerce handler instance
580
+ */
581
+ public function get_commerce_handler() {
582
+ return $this->commerce_handler;
583
+ }
584
 
585
+ /**
586
+ * Gets tracker instance.
587
+ *
588
+ * @since 2.6.0
589
+ *
590
+ * @return WooCommerce\Facebook\Utilities\Tracker
591
+ */
592
+ public function get_tracker() {
593
+ return $this->tracker;
594
+ }
595
 
596
+ /**
597
+ * Gets the debug profiling logger instance.
598
+ *
599
+ * @return WooCommerce\Facebook\Debug\ProfilingLogger
600
+ */
601
+ public function get_profiling_logger() {
602
+ static $instance = null;
603
+ if ( null === $instance ) {
604
+ $is_enabled = defined( 'FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED' ) && FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED;
605
+ $instance = new WooCommerce\Facebook\Debug\ProfilingLogger( $is_enabled );
606
  }
607
 
608
+ return $instance;
609
+ }
610
 
611
+ /**
612
+ * Get the product sync validator class.
613
+ *
614
+ * @param WC_Product $product A product object to be validated.
615
+ *
616
+ * @return ProductSyncValidator
617
+ */
618
+ public function get_product_sync_validator( WC_Product $product ) {
619
+ return new ProductSyncValidator( $this->get_integration(), $product );
620
+ }
 
621
 
622
+ /**
623
+ * Gets the advertise tab page URL.
624
+ *
625
+ * @since 2.6.29
626
+ *
627
+ * @return string
628
+ */
629
+ public function get_advertise_tab_url() {
630
+ return admin_url( 'admin.php?page=wc-facebook&tab=advertise' );
631
+ }
632
 
633
+ /**
634
+ * Gets the settings page URL.
635
+ *
636
+ * @since 1.10.0
637
+ *
638
+ * @param null $plugin_id unused
639
+ * @return string
640
+ */
641
+ public function get_settings_url( $plugin_id = null ) {
642
+ return admin_url( 'admin.php?page=wc-facebook' );
643
+ }
644
 
645
+ /**
646
+ * Gets the plugin's documentation URL.
647
+ *
648
+ * @since 1.10.0
649
+ *
650
+ * @return string
651
+ */
652
+ public function get_documentation_url() {
653
+ return 'https://docs.woocommerce.com/document/facebook-for-woocommerce/';
654
+ }
655
 
656
 
657
+ /**
658
+ * Gets the plugin's support URL.
659
+ *
660
+ * @since 1.10.0
661
+ *
662
+ * @return string
663
+ */
664
+ public function get_support_url() {
665
+ return 'https://wordpress.org/support/plugin/facebook-for-woocommerce/';
666
+ }
667
 
 
668
 
669
+ /**
670
+ * Gets the plugin's sales page URL.
671
+ *
672
+ * @since 1.10.0
673
+ *
674
+ * @return string
675
+ */
676
+ public function get_sales_page_url() {
677
+ return 'https://woocommerce.com/products/facebook/';
678
+ }
679
 
680
 
681
+ /**
682
+ * Gets the plugin's reviews URL.
683
+ *
684
+ * @since 1.10.0
685
+ *
686
+ * @return string
687
+ */
688
+ public function get_reviews_url() {
689
+ return 'https://wordpress.org/support/plugin/facebook-for-woocommerce/reviews/';
690
+ }
691
 
 
 
 
692
 
693
+ /**
694
+ * Gets the plugin name.
695
+ *
696
+ * @since 1.10.0
697
+ *
698
+ * @return string
699
+ */
700
+ public function get_plugin_name() {
701
+ return __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' );
702
+ }
703
 
704
+ /**
705
+ * Gets the url for the assets build directory.
706
+ *
707
+ * @since 2.3.4
708
+ *
709
+ * @return string
710
+ */
711
+ public function get_asset_build_dir_url() {
712
+ return $this->get_plugin_url() . '/assets/build';
713
+ }
714
 
 
 
 
 
 
 
 
 
715
 
716
+ /** Conditional methods ***************************************************************************************/
 
717
 
718
 
719
+ /**
720
+ * Determines if viewing the plugin settings in the admin.
721
+ *
722
+ * @since 1.10.0
723
+ *
724
+ * @return bool
725
+ */
726
+ public function is_plugin_settings() {
727
+ return is_admin() && WooCommerce\Facebook\Admin\Settings::PAGE_ID === Helper::get_requested_value( 'page' );
728
+ }
729
 
 
 
 
 
 
 
 
730
 
731
+ /** Utility methods *******************************************************************************************/
732
 
 
733
 
734
+ /**
735
+ * Initializes the lifecycle handler.
736
+ *
737
+ * @since 1.10.0
738
+ */
739
+ protected function init_lifecycle_handler() {
740
+ $this->lifecycle_handler = new Lifecycle( $this );
741
+ }
742
 
 
 
 
 
 
 
 
 
 
743
 
744
+ /**
745
+ * Gets the plugin singleton instance.
746
+ *
747
+ * @see \facebook_for_woocommerce()
748
+ *
749
+ * @since 1.10.0
750
+ *
751
+ * @return \WC_Facebookcommerce the plugin singleton instance
752
+ */
753
+ public static function instance() {
754
+ if ( null === self::$instance ) {
755
+ self::$instance = new self();
756
  }
757
+ return self::$instance;
758
  }
759
 
760
 
761
  /**
762
+ * Gets the plugin file.
763
  *
764
  * @since 1.10.0
765
  *
766
+ * @return string
767
  */
768
+ protected function get_file() {
769
+ return __FILE__;
770
+ }
771
+
772
 
773
+ /**
774
+ * Return current page ID
775
+ *
776
+ * @since 2.3.0
777
+ *
778
+ * @return string
779
+ */
780
+ protected function get_current_page_id() {
781
+ $current_screen_id = '';
782
+ $current_screen = get_current_screen();
783
+ if ( ! empty( $current_screen ) ) {
784
+ $current_screen_id = $current_screen->id;
785
+ }
786
+ return $current_screen_id;
787
  }
788
+ }
789
+
790
 
791
+ /**
792
+ * Gets the Facebook for WooCommerce plugin instance.
793
+ *
794
+ * @since 1.10.0
795
+ *
796
+ * @return \WC_Facebookcommerce instance of the plugin
797
+ */
798
+ function facebook_for_woocommerce() {
799
+ return \WC_Facebookcommerce::instance();
800
+ }
801
 
 
facebook-commerce-events-tracker.php CHANGED
@@ -9,8 +9,9 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\Events\Event;
13
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
14
 
15
  if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
16
 
@@ -24,13 +25,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
24
 
25
  class WC_Facebookcommerce_EventsTracker {
26
 
27
-
28
- /** @deprecated since 2.2.0 */
29
- const FB_PRIORITY_HIGH = 2;
30
- /** @deprecated since 2.2.0 */
31
- const FB_PRIORITY_LOW = 11;
32
-
33
-
34
  /** @var \WC_Facebookcommerce_Pixel instance */
35
  private $pixel;
36
 
@@ -146,22 +140,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
146
 
147
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
148
  add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );
149
-
150
  add_action( 'shutdown', array( $this, 'send_pending_events' ) );
151
-
152
- }
153
-
154
-
155
- /**
156
- * Adds filter hooks.
157
- *
158
- * @internal
159
- *
160
- * @deprecated since 2.2.0
161
- */
162
- public function apply_filters() {
163
-
164
- wc_deprecated_function( __METHOD__, '2.2.0' );
165
  }
166
 
167
 
@@ -470,7 +449,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
470
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
471
  'contents' => $contents,
472
  'search_string' => get_search_query(),
473
- 'value' => Framework\SV_WC_Helper::number_format( $total_value ),
474
  'currency' => get_woocommerce_currency(),
475
  ),
476
  'user_data' => $this->pixel->get_user_info(),
@@ -611,7 +590,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
611
  'user_data' => $this->pixel->get_user_info(),
612
  );
613
 
614
- $event = new SkyVerge\WooCommerce\Facebook\Events\Event( $event_data );
615
 
616
  $this->send_api_event( $event, false );
617
 
@@ -752,18 +731,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
752
  }
753
 
754
 
755
- /**
756
- * Sends a JSON response with the JavaScript code to track an AddToCart event.
757
- *
758
- * @internal
759
- * @deprecated since 1.10.2
760
- */
761
- public function inject_ajax_add_to_cart_event() {
762
-
763
- wc_deprecated_function( __METHOD__, '1.10.2' );
764
- }
765
-
766
-
767
  /**
768
  * Sets last product added to cart to session when adding to cart a product and redirection to cart is enabled.
769
  *
@@ -1097,23 +1064,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1097
  $this->tracked_events[] = $event;
1098
 
1099
  if ( $send_now ) {
1100
-
1101
  try {
1102
-
1103
  facebook_for_woocommerce()->get_api()->send_pixel_events( facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(), array( $event ) );
1104
-
1105
- } catch ( Framework\SV_WC_API_Exception $exception ) {
1106
-
1107
  facebook_for_woocommerce()->log( 'Could not send Pixel event: ' . $exception->getMessage() );
1108
  }
1109
-
1110
  } else {
1111
-
1112
  $this->pending_events[] = $event;
1113
-
1114
  }
1115
-
1116
-
1117
  }
1118
 
1119
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ use WooCommerce\Facebook\Events\Event;
13
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
14
+ use WooCommerce\Facebook\Framework\Helper;
15
 
16
  if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
17
 
25
 
26
  class WC_Facebookcommerce_EventsTracker {
27
 
 
 
 
 
 
 
 
28
  /** @var \WC_Facebookcommerce_Pixel instance */
29
  private $pixel;
30
 
140
 
141
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
142
  add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );
 
143
  add_action( 'shutdown', array( $this, 'send_pending_events' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  }
145
 
146
 
449
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
450
  'contents' => $contents,
451
  'search_string' => get_search_query(),
452
+ 'value' => Helper::number_format( $total_value ),
453
  'currency' => get_woocommerce_currency(),
454
  ),
455
  'user_data' => $this->pixel->get_user_info(),
590
  'user_data' => $this->pixel->get_user_info(),
591
  );
592
 
593
+ $event = new WooCommerce\Facebook\Events\Event( $event_data );
594
 
595
  $this->send_api_event( $event, false );
596
 
731
  }
732
 
733
 
 
 
 
 
 
 
 
 
 
 
 
 
734
  /**
735
  * Sets last product added to cart to session when adding to cart a product and redirection to cart is enabled.
736
  *
1064
  $this->tracked_events[] = $event;
1065
 
1066
  if ( $send_now ) {
 
1067
  try {
 
1068
  facebook_for_woocommerce()->get_api()->send_pixel_events( facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(), array( $event ) );
1069
+ } catch ( ApiException $exception ) {
 
 
1070
  facebook_for_woocommerce()->log( 'Could not send Pixel event: ' . $exception->getMessage() );
1071
  }
 
1072
  } else {
 
1073
  $this->pending_events[] = $event;
 
1074
  }
 
 
1075
  }
1076
 
1077
 
facebook-commerce-messenger-chat.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\Locale;
13
 
14
  if ( ! class_exists( 'WC_Facebookcommerce_MessengerChat' ) ) :
15
 
@@ -86,23 +86,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_MessengerChat' ) ) :
86
  endif;
87
  }
88
 
89
-
90
- /**
91
- * Gets the locales supported by Facebook Messenger.
92
- *
93
- * @since 1.10.0
94
- * @deprecated since 2.2.0
95
- *
96
- * @return array associative array of locale codes and names
97
- */
98
- public static function get_supported_locales() {
99
-
100
- wc_deprecated_function( __METHOD__, '2.2.0', '\\SkyVerge\\WooCommerce\\Facebook\\Locales::get_supported_locales_list()' );
101
-
102
- return Locale::get_supported_locales();
103
- }
104
-
105
-
106
  }
107
 
108
  endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ use WooCommerce\Facebook\Locale;
13
 
14
  if ( ! class_exists( 'WC_Facebookcommerce_MessengerChat' ) ) :
15
 
86
  endif;
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  endif;
facebook-commerce-pixel-event.php CHANGED
@@ -9,12 +9,9 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\Events\Event;
13
 
14
- if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
15
-
16
-
17
- class WC_Facebookcommerce_Pixel {
18
 
19
 
20
  const SETTINGS_KEY = 'facebook_config';
@@ -42,7 +39,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
42
  *
43
  * @var array Cache array.
44
  */
45
- public static $render_cache = array();
46
 
47
  /**
48
  * User information.
@@ -63,8 +60,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
63
  *
64
  * @param array $user_info User information array.
65
  */
66
- public function __construct( $user_info = array() ) {
67
-
68
  $this->user_info = $user_info;
69
  $this->last_event = '';
70
  }
@@ -162,7 +158,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
162
 
163
  <?php echo $this->get_pixel_init_code(); ?>
164
 
165
- fbq( 'track', 'PageView', <?php echo json_encode( self::build_params( array(), 'PageView' ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ); ?> );
166
 
167
  document.addEventListener( 'DOMContentLoaded', function() {
168
  jQuery && jQuery( function( $ ) {
@@ -228,24 +224,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
228
  }
229
 
230
 
231
- /**
232
- * Determines if the last event in the current thread matches a given event.
233
- *
234
- * TODO remove this deprecated method by March 2020 or version 2.0.0 {FN 2020-03-25}.
235
- *
236
- * @deprecated since 1.11.0
237
- *
238
- * @param string $event_name Name of the event.
239
- * @return bool
240
- */
241
- public function check_last_event( $event_name ) {
242
-
243
- wc_deprecated_function( __METHOD__, '1.11.0', __CLASS__ . '::has_last_event()' );
244
-
245
- return $this->is_last_event( $event_name );
246
- }
247
-
248
-
249
  /**
250
  * Gets the JavaScript code to track an event.
251
  *
@@ -495,7 +473,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
495
  * @param string $event The event name the params are for.
496
  * @return array
497
  */
498
- private static function build_params( $params = array(), $event = '' ) {
499
 
500
  $params = array_replace( Event::get_version_info(), $params );
501
 
@@ -691,20 +669,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
691
  }
692
 
693
 
694
- /**
695
- * Gets Facebook Pixel base code.
696
- *
697
- * @deprecated since 1.10.2
698
- *
699
- * @return string
700
- */
701
- public static function get_basecode() {
702
-
703
- wc_deprecated_function( __METHOD__, '1.10.2' );
704
-
705
- return '';
706
- }
707
-
708
  /**
709
  * Gets the logged in user info
710
  *
@@ -714,5 +678,3 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
714
  return $this->user_info;
715
  }
716
  }
717
-
718
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ use WooCommerce\Facebook\Events\Event;
13
 
14
+ class WC_Facebookcommerce_Pixel {
 
 
 
15
 
16
 
17
  const SETTINGS_KEY = 'facebook_config';
39
  *
40
  * @var array Cache array.
41
  */
42
+ public static $render_cache = [];
43
 
44
  /**
45
  * User information.
60
  *
61
  * @param array $user_info User information array.
62
  */
63
+ public function __construct( $user_info = [] ) {
 
64
  $this->user_info = $user_info;
65
  $this->last_event = '';
66
  }
158
 
159
  <?php echo $this->get_pixel_init_code(); ?>
160
 
161
+ fbq( 'track', 'PageView', <?php echo json_encode( self::build_params( [], 'PageView' ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ); ?> );
162
 
163
  document.addEventListener( 'DOMContentLoaded', function() {
164
  jQuery && jQuery( function( $ ) {
224
  }
225
 
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  /**
228
  * Gets the JavaScript code to track an event.
229
  *
473
  * @param string $event The event name the params are for.
474
  * @return array
475
  */
476
+ private static function build_params( $params = [], $event = '' ) {
477
 
478
  $params = array_replace( Event::get_version_info(), $params );
479
 
669
  }
670
 
671
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
  /**
673
  * Gets the logged in user info
674
  *
678
  return $this->user_info;
679
  }
680
  }
 
 
facebook-commerce.php CHANGED
@@ -9,16 +9,17 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\Admin;
13
- use SkyVerge\WooCommerce\Facebook\Events\AAMSettings;
14
- use SkyVerge\WooCommerce\Facebook\Handlers\Connection;
15
- use SkyVerge\WooCommerce\Facebook\Products;
16
- use SkyVerge\WooCommerce\Facebook\Products\Feed;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
18
-
19
- if ( ! defined( 'ABSPATH' ) ) {
20
- exit; // Exit if accessed directly
21
- }
 
22
 
23
  require_once 'facebook-config-warmer.php';
24
  require_once 'includes/fbproduct.php';
@@ -140,87 +141,57 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
140
  /** @var array the page name and url */
141
  private $page;
142
 
143
- /** @var WC_Facebookcommerce_Graph_API API handling class. */
144
- private $fbgraph;
145
-
146
  /** Legacy properties *********************************************************************************************/
147
 
148
 
149
  // TODO probably some of these meta keys need to be moved to Facebook\Products {FN 2020-01-13}.
150
- const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
151
- const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
152
- const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
153
 
154
  /** @var string the API flag to set a product as visible in the Facebook shop */
155
- const FB_SHOP_PRODUCT_VISIBLE = 'published';
156
 
157
  /** @var string the API flag to set a product as not visible in the Facebook shop */
158
- const FB_SHOP_PRODUCT_HIDDEN = 'hidden';
159
 
160
  /** @var string @deprecated */
161
- const FB_CART_URL = 'fb_cart_url';
162
 
163
- const FB_MESSAGE_DISPLAY_TIME = 180;
164
 
165
  // Number of days to query tip.
166
- const FB_TIP_QUERY = 1;
167
 
168
  // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}.
169
- const FB_VARIANT_IMAGE = 'fb_image';
170
 
171
- const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
172
 
173
- const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress';
174
- const FB_SYNC_REMAINING = 'fb_sync_remaining';
175
- const FB_SYNC_TIMEOUT = 30;
176
- const FB_PRIORITY_MID = 9;
177
 
 
 
 
 
 
178
  private $test_mode = false;
179
 
180
-
181
- public function init_pixel() {
182
- WC_Facebookcommerce_Pixel::initialize();
183
-
184
- /**
185
- * Migrate WC customer pixel_id from WC settings to WP options.
186
- * This is part of a larger effort to consolidate all the FB-specific
187
- * settings for all plugin integrations.
188
- */
189
- if ( is_admin() ) {
190
-
191
- $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
192
- $settings_pixel_id = $this->get_facebook_pixel_id();
193
-
194
- if (
195
- WC_Facebookcommerce_Utils::is_valid_id( $settings_pixel_id ) &&
196
- ( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) ||
197
- $pixel_id != $settings_pixel_id
198
- )
199
- ) {
200
- WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
201
- }
202
-
203
- /**
204
- * Migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
205
- * so that it works the same way the pixel ID does
206
- */
207
- $settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
208
- WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
209
-
210
- $settings_use_s2s = $this->is_use_s2s_enabled();
211
- WC_Facebookcommerce_Pixel::set_use_s2s( $settings_use_s2s );
212
-
213
- $settings_access_token = $this->get_access_token();
214
- WC_Facebookcommerce_Pixel::set_access_token( $settings_access_token );
215
- }
216
- }
217
 
218
  /**
219
  * Init and hook in the integration.
220
  *
 
221
  * @return void
222
  */
223
- public function __construct() {
 
 
224
  if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
225
  include_once 'facebook-commerce-events-tracker.php';
226
  }
@@ -253,25 +224,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
253
  }
254
 
255
  // For now, the values of use s2s and access token will be the ones returned from WC_Facebookcommerce_Pixel.
256
- $use_s2s = WC_Facebookcommerce_Pixel::get_use_s2s();
257
- $this->settings[ self::SETTING_USE_S2S ] = $use_s2s;
258
-
259
- $access_token = WC_Facebookcommerce_Pixel::get_access_token();
260
- $this->settings[ self::SETTING_ACCESS_TOKEN ] = $access_token;
261
-
262
- if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
263
- include_once 'includes/fbutils.php';
264
- }
265
 
266
  WC_Facebookcommerce_Utils::$ems = $this->get_external_merchant_settings_id();
267
 
268
- if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) {
269
- include_once 'includes/fbgraph.php';
270
- $this->fbgraph = new WC_Facebookcommerce_Graph_API( facebook_for_woocommerce()->get_connection_handler()->get_access_token() );
271
- }
272
-
273
- WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
274
-
275
  if ( is_admin() ) {
276
 
277
  $this->init_pixel();
@@ -295,75 +252,65 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
295
  if ( ! class_exists( 'WC_Facebookcommerce_Info_Banner' ) ) {
296
  include_once 'includes/fbinfobanner.php';
297
  }
298
- WC_Facebookcommerce_Info_Banner::get_instance(
299
- $this->get_external_merchant_settings_id(),
300
- $this->fbgraph,
301
- $should_query_tip
302
- );
303
  }
304
  }
305
 
306
- if ( ! class_exists( 'WC_Facebook_Integration_Test' ) ) {
307
- include_once 'includes/test/facebook-integration-test.php';
308
- }
309
- $integration_test = WC_Facebook_Integration_Test::get_instance( $this );
310
- $integration_test::$fbgraph = $this->fbgraph;
311
-
312
  if ( ! $this->get_pixel_install_time() && $this->get_facebook_pixel_id() ) {
313
  $this->update_pixel_install_time( time() );
314
  }
315
 
316
- add_action( 'admin_notices', array( $this, 'checks' ) );
317
 
318
- add_action( 'admin_enqueue_scripts', array( $this, 'load_assets' ) );
319
 
320
  add_action(
321
  'wp_ajax_ajax_sync_all_fb_products',
322
- array( $this, 'ajax_sync_all_fb_products' ),
323
  self::FB_PRIORITY_MID
324
  );
325
 
326
  add_action(
327
  'wp_ajax_ajax_check_feed_upload_status',
328
- array( $this, 'ajax_check_feed_upload_status' ),
329
  self::FB_PRIORITY_MID
330
  );
331
 
332
  add_action(
333
  'wp_ajax_ajax_reset_all_fb_products',
334
- array( $this, 'ajax_reset_all_fb_products' ),
335
  self::FB_PRIORITY_MID
336
  );
337
  add_action(
338
  'wp_ajax_ajax_display_test_result',
339
- array( $this, 'ajax_display_test_result' )
340
  );
341
 
342
  // Don't duplicate product FBID meta.
343
- add_filter( 'woocommerce_duplicate_product_exclude_meta', array( $this, 'fb_duplicate_product_reset_meta' ) );
344
 
345
  // Add product processing hooks if the plugin is configured only.
346
  if ( $this->is_configured() && $this->get_product_catalog_id() ) {
347
 
348
  // On_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information.
349
- add_action( 'woocommerce_process_product_meta', array( $this, 'on_product_save' ), 40 );
350
 
351
  add_action(
352
  'woocommerce_product_quick_edit_save',
353
- array( $this, 'on_quick_and_bulk_edit_save' )
354
  );
355
 
356
  add_action(
357
  'woocommerce_product_bulk_edit_save',
358
- array( $this, 'on_quick_and_bulk_edit_save' )
359
  );
360
 
361
- add_action( 'before_delete_post', array( $this, 'on_product_delete' ) );
362
 
363
  // Ensure product is deleted from FB when moved to trash.
364
- add_action( 'wp_trash_post', array( $this, 'on_product_delete' ) );
365
 
366
- add_action( 'add_meta_boxes', 'SkyVerge\WooCommerce\Facebook\Admin\Product_Sync_Meta_Box::register', 10, 1 );
367
 
368
  add_action(
369
  'wp_ajax_ajax_fb_toggle_visibility',
@@ -396,11 +343,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
396
  $this->remove_sticky_message();
397
  }
398
  }
399
-
400
  $this->load_background_sync_process();
401
  }
402
 
403
-
404
  if ( $this->get_facebook_pixel_id() ) {
405
  $aam_settings = $this->load_aam_settings_of_pixel();
406
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
@@ -423,11 +368,54 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
423
  3
424
  );
425
 
426
- add_action( 'untrashed_post', array( $this, 'fb_restore_untrashed_variable_product' ) );
427
 
428
  // Product Set hooks.
429
- add_action( 'fb_wc_product_set_sync', array( $this, 'create_or_update_product_set_item' ), 99, 2 );
430
- add_action( 'fb_wc_product_set_delete', array( $this, 'delete_product_set_item' ), 99 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  }
432
 
433
  /**
@@ -455,7 +443,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
455
  // installed pixel
456
  // because the admin could have changed the connection to Facebook
457
  // during the refresh interval.
458
- if ( $cached_aam_settings->get_pixel_id() == $installed_pixel ) {
459
  $aam_settings = $cached_aam_settings;
460
  }
461
  }
@@ -471,21 +459,30 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
471
  return $aam_settings;
472
  }
473
 
 
 
 
 
 
474
  public function load_background_sync_process() {
475
  // Attempt to load background processing (Woo 3.x.x only).
476
  include_once 'includes/fbbackground.php';
477
  if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
478
  if ( ! isset( $this->background_processor ) ) {
479
- $this->background_processor =
480
- new WC_Facebookcommerce_Background_Process( $this );
481
  }
482
  }
483
  add_action(
484
  'wp_ajax_ajax_fb_background_check_queue',
485
- array( $this, 'ajax_fb_background_check_queue' )
486
  );
487
  }
488
 
 
 
 
 
 
489
  public function ajax_fb_background_check_queue() {
490
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'background check queue', true );
491
  check_ajax_referer( 'wc_facebook_settings_jsx' );
@@ -493,152 +490,66 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
493
  if ( isset( $_POST['request_time'] ) ) {
494
  $request_time = esc_js( sanitize_text_field( wp_unslash( $_POST['request_time'] ) ) );
495
  }
496
-
497
- if ( facebook_for_woocommerce()->get_connection_handler()->get_access_token() ) {
498
-
499
  if ( isset( $this->background_processor ) ) {
500
  $is_processing = $this->background_processor->handle_cron_healthcheck();
501
  $remaining = $this->background_processor->get_item_count();
502
- $response = array(
503
  'connected' => true,
504
  'background' => true,
505
  'processing' => $is_processing,
506
  'remaining' => $remaining,
507
  'request_time' => $request_time,
508
- );
509
  } else {
510
- $response = array(
511
  'connected' => true,
512
  'background' => false,
513
- );
514
  }
515
  } else {
516
- $response = array(
517
  'connected' => false,
518
  'background' => false,
519
- );
520
  }
521
-
522
  printf( json_encode( $response ) );
523
  wp_die();
524
  }
525
 
526
 
527
- /**
528
- * Adds a new tab to the Product edit page.
529
- *
530
- * @internal
531
- * @deprecated since 1.10.0
532
- *
533
- * @param array $tabs Array of tabs.
534
- * @return array
535
- */
536
- public function fb_new_product_tab( $tabs ) {
537
-
538
- wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab()' );
539
-
540
- return $tabs;
541
- }
542
-
543
-
544
- /**
545
- * Adds content to the new Facebook tab on the Product edit page.
546
- *
547
- * @internal
548
- * @deprecated since 1.10.0
549
- */
550
- public function fb_new_product_tab_content() {
551
-
552
- wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab_content()' );
553
- }
554
-
555
-
556
- /**
557
- * Filters the product columns in the admin edit screen.
558
- *
559
- * @internal
560
- * @deprecated since 1.10.0
561
- *
562
- * @param array $existing_columns Array of columns and labels.
563
- * @return array
564
- */
565
- public function fb_product_columns( $existing_columns ) {
566
-
567
- wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_column()' );
568
-
569
- return $existing_columns;
570
- }
571
-
572
-
573
- /**
574
- * Outputs content for the FB Shop column in the edit screen.
575
- *
576
- * @internal
577
- * @deprecated since 1.10.0
578
- *
579
- * @param string $column Name of the column to display.
580
- */
581
- public function fb_render_product_columns( $column ) {
582
-
583
- wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_columns_content()' );
584
- }
585
-
586
- /**
587
- * Returns graph API client object.
588
- *
589
- * @since 2.6.0
590
- *
591
- * @return WC_Facebookcommerce_Graph_API
592
- */
593
- public function get_graph_api() {
594
- return $this->fbgraph;
595
- }
596
-
597
  /**
598
  * Gets a list of Product Item IDs indexed by the ID of the variation.
599
  *
600
  * @since 2.0.0
601
  *
602
- * @param string $product_group_id product group ID
 
603
  * @return array
604
  */
605
  public function get_variation_product_item_ids( $product, $product_group_id ) {
606
-
607
- $product_item_ids_by_variation_id = array();
608
- $missing_product_item_ids = array();
609
 
610
  // get the product item IDs from meta data and build a list of variations that don't have a product item ID stored
611
  foreach ( $product->get_children() as $variation_id ) {
612
-
613
  if ( $variation = wc_get_product( $variation_id ) ) {
614
-
615
  if ( $product_item_id = $variation->get_meta( self::FB_PRODUCT_ITEM_ID ) ) {
616
-
617
  $product_item_ids_by_variation_id[ $variation_id ] = $product_item_id;
618
-
619
  } else {
620
-
621
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation );
622
-
623
  $missing_product_item_ids[ $retailer_id ] = $variation;
624
-
625
  $product_item_ids_by_variation_id[ $variation_id ] = null;
626
  }
627
  }
628
  }
629
-
630
  // use the Graph API to try to find and store the product item IDs for variations that don't have a value yet
631
  if ( $missing_product_item_ids ) {
632
-
633
  $product_item_ids = $this->find_variation_product_item_ids( $product_group_id );
634
-
635
  foreach ( $missing_product_item_ids as $retailer_id => $variation ) {
636
-
637
  if ( isset( $product_item_ids[ $retailer_id ] ) ) {
638
-
639
  $variation->update_meta_data( self::FB_PRODUCT_ITEM_ID, $product_item_ids[ $retailer_id ] );
640
  $variation->save_meta_data();
641
-
642
  $product_item_ids_by_variation_id[ $variation->get_id() ] = $product_item_ids[ $retailer_id ];
643
  }
644
  }
@@ -650,34 +561,28 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
650
 
651
  /**
652
  * Uses the Graph API to return a list of Product Item IDs indexed by the variation's retailer ID.
653
- *
654
- * @since 2.0.0
 
 
 
 
655
  *
656
  * @param string $product_group_id product group ID
657
- * @return array
658
  */
659
- private function find_variation_product_item_ids( $product_group_id ) {
660
-
661
- $product_item_ids = array();
662
-
663
  try {
664
-
665
- $response = facebook_for_woocommerce()->get_api()->get_product_group_products( $product_group_id );
666
-
667
  do {
668
-
669
  $product_item_ids = array_merge( $product_item_ids, $response->get_ids() );
670
-
671
  // get up to two additional pages of results
672
- } while ( $response = facebook_for_woocommerce()->get_api()->next( $response, 2 ) );
673
-
674
- } catch ( Framework\SV_WC_API_Exception $e ) {
675
-
676
  $message = sprintf( 'There was an error trying to find the IDs for Product Items in the Product Group %s: %s', $product_group_id, $e->getMessage() );
677
-
678
  facebook_for_woocommerce()->log( $message );
679
  }
680
-
681
  return $product_item_ids;
682
  }
683
 
@@ -700,7 +605,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
700
  * @since 2.6.1
701
  */
702
  public function allow_full_batch_api_sync() {
703
-
704
  /**
705
  * Block the full batch API sync.
706
  *
@@ -737,95 +641,85 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
737
  */
738
  return apply_filters_deprecated(
739
  'facebook_for_woocommerce_allow_full_batch_api_sync',
740
- array(
741
  $default_allow_sync,
742
  $this->get_product_count(),
743
- ),
744
  '2.6.10',
745
  'facebook_for_woocommerce_block_full_batch_api_sync'
746
  );
747
  }
748
 
749
-
750
  /**
751
  * Load DIA specific JS Data
752
  */
753
  public function load_assets() {
754
-
755
- $ajax_data = array(
756
  'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
757
- );
758
  // load banner assets
759
  wp_enqueue_script(
760
  'wc_facebook_infobanner_jsx',
761
- facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/infobanner.js',
762
- array(),
763
  \WC_Facebookcommerce::PLUGIN_VERSION
764
  );
765
- wp_localize_script(
766
- 'wc_facebook_infobanner_jsx',
767
- 'wc_facebook_infobanner_jsx',
768
- $ajax_data
769
- );
770
-
771
  wp_enqueue_style(
772
  'wc_facebook_infobanner_css',
773
  plugins_url(
774
  '/assets/css/facebook-infobanner.css',
775
  __FILE__
776
  ),
777
- array(),
778
  \WC_Facebookcommerce::PLUGIN_VERSION
779
  );
780
 
781
- if ( ! facebook_for_woocommerce()->is_plugin_settings() ) {
782
  return;
783
  }
784
 
785
  ?>
786
- <script>
787
-
788
- window.facebookAdsToolboxConfig = {
789
- hasGzipSupport: '<?php echo extension_loaded( 'zlib' ) ? 'true' : 'false'; ?>',
790
- enabledPlugins: ['MESSENGER_CHAT','INSTAGRAM_SHOP', 'PAGE_SHOP'],
791
- enableSubscription: '<?php echo class_exists( 'WC_Subscriptions' ) ? 'true' : 'false'; ?>',
792
- popupOrigin: '<?php echo isset( $_GET['url'] ) ? esc_js( sanitize_text_field( wp_unslash( $_GET['url'] ) ) ) : 'https://www.facebook.com/'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>',
793
- feedWasDisabled: 'true',
794
- platform: 'WooCommerce',
795
- pixel: {
796
- pixelId: '<?php echo $this->get_facebook_pixel_id() ? esc_js( $this->get_facebook_pixel_id() ) : ''; ?>',
797
- advanced_matching_supported: true
798
- },
799
- diaSettingId: '<?php echo $this->get_external_merchant_settings_id() ? esc_js( $this->get_external_merchant_settings_id() ) : ''; ?>',
800
- store: {
801
- baseUrl: window.location.protocol + '//' + window.location.host,
802
- baseCurrency:'<?php echo esc_js( WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); ?>',
803
- timezoneId: '<?php echo esc_js( date( 'Z' ) ); ?>',
804
- storeName: '<?php echo esc_js( WC_Facebookcommerce_Utils::get_store_name() ); ?>',
805
- version: '<?php echo esc_js( WC()->version ); ?>',
806
- php_version: '<?php echo PHP_VERSION; ?>',
807
- plugin_version: '<?php echo esc_js( WC_Facebookcommerce_Utils::PLUGIN_VERSION ); ?>'
808
- },
809
- feed: {
810
- totalVisibleProducts: '<?php echo esc_js( $this->get_product_count() ); ?>',
811
- hasClientSideFeedUpload: '<?php echo esc_js( ! ! $this->get_feed_id() ); ?>',
812
- enabled: true,
813
- format: 'csv'
814
- },
815
- feedPrepared: {
816
- feedUrl: '<?php echo esc_url_raw( Feed::get_feed_data_url() ); ?>',
817
- feedPingUrl: '',
818
- feedMigrated: <?php echo $this->is_feed_migrated() ? 'true' : 'false'; ?>,
819
- samples: <?php echo $this->get_sample_product_feed(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
820
- },
821
- };
822
-
823
- </script>
824
-
825
  <?php
826
- $ajax_data = array(
827
  'nonce' => wp_create_nonce( 'wc_facebook_settings_jsx' ),
828
- );
829
  wp_localize_script(
830
  'wc_facebook_settings_jsx',
831
  'wc_facebook_settings_jsx',
@@ -837,12 +731,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
837
  '/assets/css/facebook.css',
838
  __FILE__
839
  ),
840
- array(),
841
  \WC_Facebookcommerce::PLUGIN_VERSION
842
  );
843
  }
844
 
845
-
846
  /**
847
  * Gets the IDs of products marked for deletion from Facebook when removed from Sync.
848
  *
@@ -853,16 +746,13 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
853
  * @return array
854
  */
855
  private function get_removed_from_sync_products_to_delete() {
856
-
857
- $posted_products = Framework\SV_WC_Helper::get_posted_value( WC_Facebook_Product::FB_REMOVE_FROM_SYNC );
858
  if ( empty( $posted_products ) ) {
859
- return array();
860
  }
861
-
862
  return array_map( 'absint', explode( ',', $posted_products ) );
863
  }
864
 
865
-
866
  /**
867
  * Checks the product type and calls the corresponding on publish method.
868
  *
@@ -872,83 +762,63 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
872
  *
873
  * @param int $wp_id post ID
874
  */
875
- public function on_product_save( $wp_id ) {
876
-
877
  $product = wc_get_product( $wp_id );
878
-
879
  if ( ! $product ) {
880
  return;
881
  }
882
-
883
- $sync_mode = isset( $_POST['wc_facebook_sync_mode'] ) ? $_POST['wc_facebook_sync_mode'] : Admin::SYNC_MODE_SYNC_DISABLED;
 
 
 
884
  $sync_enabled = Admin::SYNC_MODE_SYNC_DISABLED !== $sync_mode;
885
 
886
  if ( Admin::SYNC_MODE_SYNC_AND_SHOW === $sync_mode && $product->is_virtual() && 'bundle' !== $product->get_type() ) {
887
  // force to Sync and hide
888
  $sync_mode = Admin::SYNC_MODE_SYNC_AND_HIDE;
889
  }
890
-
891
  $products_to_delete_from_facebook = $this->get_removed_from_sync_products_to_delete();
892
-
893
  if ( $product->is_type( 'variable' ) ) {
894
-
895
  // check variations for deletion
896
  foreach ( $products_to_delete_from_facebook as $delete_product_id ) {
897
-
898
  $delete_product = wc_get_product( $delete_product_id );
899
-
900
  if ( empty( $delete_product ) ) {
901
  continue;
902
  }
903
-
904
  if ( Products::is_sync_enabled_for_product( $delete_product ) ) {
905
  continue;
906
  }
907
-
908
  $this->delete_fb_product( $delete_product );
909
  }
910
  } else {
911
-
912
  if ( $sync_enabled ) {
913
-
914
- Products::enable_sync_for_products( array( $product ) );
915
  Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
916
-
917
  $this->save_product_settings( $product );
918
-
919
  } else {
920
-
921
  // if previously enabled, add a notice on the next page load
922
  if ( Products::is_sync_enabled_for_product( $product ) ) {
923
  Admin::add_product_disabled_sync_notice();
924
  }
925
-
926
- Products::disable_sync_for_products( array( $product ) );
927
-
928
  if ( in_array( $wp_id, $products_to_delete_from_facebook, true ) ) {
929
-
930
  $this->delete_fb_product( $product );
931
  }
932
  }
933
  }
934
-
935
  if ( $sync_enabled ) {
936
-
937
  Admin\Products::save_commerce_fields( $product );
938
-
939
  switch ( $product->get_type() ) {
940
-
941
  case 'simple':
942
  case 'booking':
943
  case 'external':
944
  case 'composite':
945
  $this->on_simple_product_publish( $wp_id );
946
  break;
947
-
948
  case 'variable':
949
  $this->on_variable_product_publish( $wp_id );
950
  break;
951
-
952
  case 'subscription':
953
  case 'variable-subscription':
954
  case 'bundle':
@@ -958,7 +828,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
958
  }
959
  }
960
 
961
-
962
  /**
963
  * Saves the submitted Facebook settings for a product.
964
  *
@@ -966,8 +835,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
966
  *
967
  * @param \WC_Product $product the product object
968
  */
969
- private function save_product_settings( \WC_Product $product ) {
970
-
971
  $woo_product = new WC_Facebook_Product( $product->get_id() );
972
 
973
  // phpcs:disable WordPress.Security.NonceVerification.Missing
@@ -990,18 +858,16 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
990
  // phpcs:enable WordPress.Security.NonceVerification.Missing
991
  }
992
 
993
-
994
  /**
995
  * Deletes a product from Facebook.
996
  *
997
  * @param int $product_id product ID
998
  */
999
- public function on_product_delete( $product_id ) {
1000
-
1001
  $product = wc_get_product( $product_id );
1002
 
1003
  // bail if product does not exist
1004
- if ( ! $product instanceof \WC_Product ) {
1005
  return;
1006
  }
1007
 
@@ -1012,16 +878,15 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1012
  *
1013
  * @see ajax_delete_fb_product()
1014
  */
 
1015
  if ( ( ! wp_doing_ajax() || ! isset( $_POST['action'] ) || 'ajax_delete_fb_product' !== $_POST['action'] )
1016
  && ! Products::published_product_should_be_synced( $product ) && ! $product->is_type( 'variable' ) ) {
1017
-
1018
  return;
1019
  }
1020
 
1021
  $this->delete_fb_product( $product );
1022
  }
1023
 
1024
-
1025
  /**
1026
  * Deletes Facebook product.
1027
  *
@@ -1036,29 +901,20 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1036
  $product_id = $product->get_id();
1037
 
1038
  if ( $product->is_type( 'variation' ) ) {
1039
-
1040
  $retailer_id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
1041
-
1042
  // enqueue variation to be deleted in the background
1043
- facebook_for_woocommerce()->get_products_sync_handler()->delete_products( array( $retailer_id ) );
1044
-
1045
  } elseif ( $product->is_type( 'variable' ) ) {
1046
-
1047
- $retailer_ids = array();
1048
-
1049
  foreach ( $product->get_children() as $variation_id ) {
1050
-
1051
  $variation = wc_get_product( $variation_id );
1052
-
1053
  if ( $variation instanceof \WC_Product ) {
1054
  $retailer_ids[] = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation );
1055
  }
1056
  delete_post_meta( $variation_id, self::FB_PRODUCT_ITEM_ID );
1057
  }
1058
-
1059
  // enqueue variations to be deleted in the background
1060
- facebook_for_woocommerce()->get_products_sync_handler()->delete_products( $retailer_ids );
1061
-
1062
  $this->delete_product_group( $product_id );
1063
  } else {
1064
 
@@ -1069,10 +925,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1069
  // clear out both item and group IDs
1070
  delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
1071
  delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
1072
-
1073
  }
1074
 
1075
-
1076
  /**
1077
  * Updates Facebook Visibility upon trashing and restore.
1078
  *
@@ -1083,7 +937,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1083
  * @param \WP_post $post
1084
  */
1085
  public function fb_change_product_published_status( $new_status, $old_status, $post ) {
1086
-
1087
  if ( ! $post ) {
1088
  return;
1089
  }
@@ -1094,7 +947,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1094
 
1095
  $product = wc_get_product( $post->ID );
1096
 
1097
- // bail if we couldn't retrieve a valid product object or the product isn't enabled for sync
1098
  //
1099
  // Note that while moving a variable product to the trash, this method is called for each one of the
1100
  // variations before it gets called with the variable product. As a result, Products::product_should_be_synced()
@@ -1127,10 +980,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1127
  *
1128
  * @param int $post_id
1129
  */
1130
- public function fb_restore_untrashed_variable_product ( $post_id ) {
1131
  $product = wc_get_product( $post_id );
1132
 
1133
- if ( ! $product instanceof \WC_Product ) {
1134
  return;
1135
  }
1136
 
@@ -1161,11 +1014,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1161
  * @return bool
1162
  */
1163
  private function should_update_visibility_for_product_status_change( $new_status, $old_status ) {
1164
-
1165
  return ( $old_status === 'publish' && $new_status !== 'publish' ) || ( $old_status === 'trash' && $new_status === 'publish' ) || ( $old_status === 'future' && $new_status === 'publish' );
1166
  }
1167
 
1168
-
1169
  /**
1170
  * Generic function for use with any product publishing.
1171
  *
@@ -1175,7 +1026,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1175
  * @param int $product_id product ID
1176
  */
1177
  public function on_product_publish( $product_id ) {
1178
-
1179
  // bail if the plugin is not configured properly
1180
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
1181
  return;
@@ -1190,31 +1040,31 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1190
  }
1191
  }
1192
 
1193
-
1194
  /**
1195
  * If the user has opt-in to remove products that are out of stock,
1196
  * this function will delete the product from FB Page as well.
 
 
 
 
 
1197
  */
1198
- function delete_on_out_of_stock( $wp_id, $woo_product ) {
1199
-
1200
  if ( Products::product_should_be_deleted( $woo_product ) ) {
1201
  $product = wc_get_product( $wp_id );
1202
  $this->delete_fb_product( $product );
1203
  return true;
1204
  }
1205
-
1206
  return false;
1207
  }
1208
 
1209
-
1210
  /**
1211
  * Syncs product to Facebook when saving a variable product.
1212
  *
1213
  * @param int $wp_id product post ID
1214
  * @param WC_Facebook_Product|null $woo_product product object
1215
  */
1216
- function on_variable_product_publish( $wp_id, $woo_product = null ) {
1217
-
1218
  if ( ! $woo_product instanceof \WC_Facebook_Product ) {
1219
  $woo_product = new \WC_Facebook_Product( $wp_id );
1220
  }
@@ -1227,39 +1077,30 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1227
  return;
1228
  }
1229
 
1230
- // Check if product group has been published to FB. If not, it's new.
1231
  // If yes, loop through variants and see if product items are published.
1232
  $fb_product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $wp_id, $woo_product );
1233
-
1234
  if ( $fb_product_group_id ) {
1235
-
1236
  $woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
1237
-
1238
  $this->update_product_group( $woo_product );
1239
-
1240
  } else {
1241
-
1242
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product->woo_product );
1243
-
1244
  $this->create_product_group( $woo_product, $retailer_id, true );
1245
  }
1246
 
1247
- $variation_ids = array();
1248
 
1249
  // scheduled update for each variation that should be synced
1250
  foreach ( $woo_product->get_children() as $variation_id ) {
1251
-
1252
  $variation = wc_get_product( $variation_id );
1253
-
1254
- if ( $variation instanceof \WC_Product && $this->product_should_be_synced( $variation ) && ! $this->delete_on_out_of_stock( $variation_id, $variation ) ) {
1255
  $variation_ids[] = $variation_id;
1256
  }
1257
  }
1258
 
1259
- facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( $variation_ids );
1260
  }
1261
 
1262
-
1263
  /**
1264
  * Syncs product to Facebook when saving a simple product.
1265
  *
@@ -1268,8 +1109,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1268
  * @param WC_Facebook_Product|null $parent_product parent object
1269
  * @return int|mixed|void|null
1270
  */
1271
- function on_simple_product_publish( $wp_id, $woo_product = null, &$parent_product = null ) {
1272
-
1273
  if ( ! $woo_product instanceof \WC_Facebook_Product ) {
1274
  $woo_product = new \WC_Facebook_Product( $wp_id, $parent_product );
1275
  }
@@ -1287,173 +1127,108 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1287
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $wp_id, $woo_product );
1288
 
1289
  if ( $fb_product_item_id ) {
1290
-
1291
  $woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
1292
-
1293
  $this->update_product_item( $woo_product, $fb_product_item_id );
1294
-
1295
  return $fb_product_item_id;
1296
-
1297
  } else {
1298
-
1299
  // Check if this is a new product item for an existing product group
1300
  if ( $woo_product->get_parent_id() ) {
1301
-
1302
  $fb_product_group_id = $this->get_product_fbid(
1303
  self::FB_PRODUCT_GROUP_ID,
1304
  $woo_product->get_parent_id(),
1305
  $woo_product
1306
  );
1307
-
1308
  // New variant added
1309
  if ( $fb_product_group_id ) {
1310
-
1311
  return $this->create_product_simple( $woo_product, $fb_product_group_id );
1312
-
1313
  } else {
1314
-
1315
  WC_Facebookcommerce_Utils::fblog(
1316
- 'Wrong! simple_product_publish called without group ID for
1317
- a variable product!',
1318
- array(),
1319
  true
1320
  );
1321
  }
1322
  } else {
1323
-
1324
  return $this->create_product_simple( $woo_product ); // new product
1325
  }
1326
  }
1327
  }
1328
 
1329
-
1330
  /**
1331
  * Determines whether the product with the given ID should be synced.
1332
  *
1333
- * @deprecated use \SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator::validate instead
1334
- *
1335
  * @since 2.0.0
1336
  *
1337
- * @param \WC_Product|false $product product object
1338
  */
1339
- public function product_should_be_synced( $product ) {
1340
  try {
1341
- facebook_for_woocommerce()->get_product_sync_validator( $product )->validate();
1342
  return true;
1343
  } catch ( \Exception $e ) {
1344
  return false;
1345
  }
1346
  }
1347
 
1348
-
1349
  /**
1350
- * Create product group and product, store fb-specific info
1351
- **/
1352
- function create_product_simple( $woo_product, $fb_product_group_id = null ) {
 
 
 
 
1353
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
1354
 
1355
  if ( ! $fb_product_group_id ) {
1356
- $fb_product_group_id = $this->create_product_group(
1357
- $woo_product,
1358
- $retailer_id
1359
- );
1360
  }
1361
 
1362
  if ( $fb_product_group_id ) {
1363
- $fb_product_item_id = $this->create_product_item(
1364
- $woo_product,
1365
- $retailer_id,
1366
- $fb_product_group_id
1367
- );
1368
  return $fb_product_item_id;
1369
  }
 
1370
  }
1371
 
1372
- function create_product_group( $woo_product, $retailer_id, $variants = false ) {
1373
-
1374
- $product_group_data = array(
 
 
 
 
 
1375
  'retailer_id' => $retailer_id,
1376
- );
1377
-
1378
  if ( $variants ) {
1379
- $product_group_data['variants'] =
1380
- $woo_product->prepare_variants_for_group();
1381
  }
1382
 
1383
- $create_product_group_result = $this->check_api_result(
1384
- $this->fbgraph->create_product_group(
1385
- $this->get_product_catalog_id(),
1386
- $product_group_data
1387
- ),
1388
- $product_group_data,
1389
- $woo_product->get_id()
1390
  );
1391
 
1392
  // New variant added
1393
- if ( $create_product_group_result ) {
1394
- $decode_result = WC_Facebookcommerce_Utils::decode_json( $create_product_group_result['body'] );
1395
- $fb_product_group_id = $decode_result->id;
1396
-
1397
  update_post_meta(
1398
  $woo_product->get_id(),
1399
  self::FB_PRODUCT_GROUP_ID,
1400
  $fb_product_group_id
1401
  );
1402
-
1403
- /** TODO: restore when adopting FBE 2.0
1404
- $this->display_success_message(
1405
- 'Created product group <a href="https://facebook.com/' .
1406
- $fb_product_group_id . '" target="_blank">' .
1407
- $fb_product_group_id . '</a> on Facebook.'
1408
- );
1409
- */
1410
-
1411
  return $fb_product_group_id;
1412
  }
 
1413
  }
1414
 
1415
- function create_product_item( $woo_product, $retailer_id, $product_group_id ) {
1416
-
1417
- $product_data = $woo_product->prepare_product( $retailer_id );
1418
-
1419
- $product_result = $this->check_api_result(
1420
- $this->fbgraph->create_product_item(
1421
- $product_group_id,
1422
- $product_data
1423
- ),
1424
- $product_data,
1425
- $woo_product->get_id()
1426
- );
1427
-
1428
- if ( $product_result ) {
1429
- $decode_result = WC_Facebookcommerce_Utils::decode_json( $product_result['body'] );
1430
- $fb_product_item_id = $decode_result->id;
1431
-
1432
- update_post_meta(
1433
- $woo_product->get_id(),
1434
- self::FB_PRODUCT_ITEM_ID,
1435
- $fb_product_item_id
1436
- );
1437
-
1438
- /** TODO: restore when adopting FBE 2.0
1439
- $this->display_success_message(
1440
- 'Created product item <a href="https://facebook.com/' .
1441
- $fb_product_item_id . '" target="_blank">' .
1442
- $fb_product_item_id . '</a> on Facebook.'
1443
- );
1444
- */
1445
-
1446
- return $fb_product_item_id;
1447
- }
1448
- }
1449
-
1450
-
1451
  /**
1452
  * Update existing product group (variant data only)
1453
  *
1454
- * @param \WC_Facebook_Product $woo_product
1455
  **/
1456
- function update_product_group( $woo_product ) {
1457
  $fb_product_group_id = $this->get_product_fbid(
1458
  self::FB_PRODUCT_GROUP_ID,
1459
  $woo_product->get_id(),
@@ -1469,6 +1244,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1469
  if ( ! $variants ) {
1470
  WC_Facebookcommerce_Utils::log(
1471
  sprintf(
 
1472
  __(
1473
  'Nothing to update for product group for %1$s',
1474
  'facebook-for-woocommerce'
@@ -1479,10 +1255,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1479
  return;
1480
  }
1481
 
1482
-
1483
- $product_group_data = array(
1484
  'variants' => $variants,
1485
- );
1486
 
1487
  // Figure out the matching default variation.
1488
  $default_product_fbid = $this->get_product_group_default_variation( $woo_product, $fb_product_group_id );
@@ -1491,24 +1266,45 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1491
  $product_group_data['default_product_id'] = $default_product_fbid;
1492
  }
1493
 
1494
- $result = $this->check_api_result(
1495
- $this->fbgraph->update_product_group(
1496
- $fb_product_group_id,
1497
- $product_group_data
1498
- )
1499
- );
1500
-
1501
- /** TODO: restore when adopting FBE 2.0
1502
- if ( $result ) {
1503
  $this->display_success_message(
1504
  'Updated product group <a href="https://facebook.com/' .
1505
  $fb_product_group_id . '" target="_blank">' . $fb_product_group_id .
1506
  '</a> on Facebook.'
1507
  );
 
 
 
 
 
 
1508
  }
1509
- */
1510
  }
1511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1512
 
1513
  /**
1514
  * Determines if there is a matching variation for the default attributes.
@@ -1519,11 +1315,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1519
  *
1520
  * @since 2.1.2
1521
  *
1522
- * @param \WC_Facebook_Product $woo_product
 
1523
  * @return integer|null Facebook Catalog variation id.
1524
  */
1525
- private function get_product_group_default_variation( $woo_product, $fb_product_group_id ) {
1526
-
1527
  $default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' );
1528
  $default_variation = null;
1529
 
@@ -1546,24 +1342,24 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1546
  );
1547
 
1548
  // Check if currently processed variation exist in the catalog.
1549
- if ( ! in_array( $fb_retailer_id, $existing_catalog_variations_retailer_ids ) ) {
1550
  continue;
1551
  }
1552
 
1553
- $variation_attributes = $this->get_product_variation_attributes( $variation );
1554
- $variation_attributes_count = count( $variation_attributes );
1555
- $matching_attributes_count = count( array_intersect_assoc( $default_attributes, $variation_attributes ) );
1556
 
1557
  // Check how much current variation matches the selected default attributes.
1558
  if ( $matching_attributes_count === $variation_attributes_count ) {
1559
  // We found a perfect match;
1560
  $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1561
  break;
1562
- } else if ( $matching_attributes_count > $best_match_count ) {
 
1563
  // We found a better match.
1564
  $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1565
  }
1566
-
1567
  }
1568
  }
1569
 
@@ -1596,9 +1392,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1596
  * @param array $variation
1597
  * @return array
1598
  */
1599
- private function get_product_variation_attributes( $variation ) {
1600
-
1601
- $final_attributes = array();
1602
  $variation_attributes = $variation['attributes'];
1603
 
1604
  foreach ( $variation_attributes as $attribute_name => $attribute_value ) {
@@ -1608,11 +1403,14 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1608
  return $final_attributes;
1609
  }
1610
 
1611
-
1612
  /**
1613
- * Update existing product
1614
- **/
1615
- function update_product_item( $woo_product, $fb_product_item_id ) {
 
 
 
 
1616
  $product_data = $woo_product->prepare_product();
1617
 
1618
  // send an empty string to clear the additional_image_urls property if the product has no additional images
@@ -1620,21 +1418,18 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1620
  $product_data['additional_image_urls'] = '';
1621
  }
1622
 
1623
- $result = $this->check_api_result(
1624
- $this->fbgraph->update_product_item(
1625
- $fb_product_item_id,
1626
- $product_data
1627
- )
1628
- );
1629
-
1630
- /** TODO: restore when adopting FBE 2.0
1631
- if ( $result ) {
1632
  $this->display_success_message(
1633
  'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
1634
  '" target="_blank">' . $fb_product_item_id . '</a> on Facebook.'
1635
  );
 
 
 
 
 
1636
  }
1637
- */
1638
  }
1639
 
1640
 
@@ -1647,33 +1442,17 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1647
  * @param int $product_set_id Product Set Term Id.
1648
  **/
1649
  public function create_or_update_product_set_item( $product_set_data, $product_set_id ) {
1650
-
1651
  // check if exists in FB
1652
  $fb_product_set_id = get_term_meta( $product_set_id, self::FB_PRODUCT_SET_ID, true );
1653
 
1654
  // set data and execute API call
1655
- $method = empty( $fb_product_set_id ) ? 'create' : 'update';
1656
- $id = empty( $fb_product_set_id ) ? $this->get_product_catalog_id() : $fb_product_set_id;
1657
- $result = $this->check_api_result(
1658
- call_user_func_array(
1659
- array(
1660
- $this->fbgraph,
1661
- $method . '_product_set_item',
1662
- ),
1663
- array(
1664
- $id,
1665
- $product_set_data,
1666
- )
1667
- )
1668
- );
1669
 
1670
  // update product set to set Facebook Product Set ID
1671
  if ( $result && empty( $fb_product_set_id ) ) {
1672
-
1673
- // decode and get ID from result body
1674
- $decode_result = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
1675
- $fb_product_set_id = $decode_result->id;
1676
-
1677
  update_term_meta(
1678
  $product_set_id,
1679
  self::FB_PRODUCT_SET_ID,
@@ -1682,39 +1461,19 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1682
  }
1683
  }
1684
 
1685
-
1686
  /**
1687
  * Delete product set
1688
  *
1689
- * @since 2.3.0
1690
- *
1691
- * @param int $fb_product_set_id Facebook Product Set ID.
1692
- **/
1693
- public function delete_product_set_item( $fb_product_set_id ) {
1694
- $this->check_api_result( $this->fbgraph->delete_product_set_item( $fb_product_set_id ) );
1695
- }
1696
-
1697
- /**
1698
- * Saves settings via AJAX (to preserve window context for onboarding).
1699
- *
1700
- * @internal
1701
- *
1702
- * @deprecated 2.0.0
1703
  */
1704
- public function ajax_save_fb_settings() {
1705
-
1706
- wc_deprecated_function( __METHOD__, '2.0.0' );
1707
  }
1708
 
1709
- /**
1710
- * Delete all settings via AJAX
1711
- *
1712
- * @deprecated 2.0.0
1713
- */
1714
- function ajax_delete_fb_settings() {
1715
-
1716
- wc_deprecated_function( __METHOD__, '2.0.0' );
1717
- }
1718
 
1719
  /**
1720
  * Checks the feed upload status (FBE v1.0).
@@ -1722,32 +1481,27 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1722
  * @internal
1723
  */
1724
  public function ajax_check_feed_upload_status() {
1725
- $response = array(
1726
  'connected' => true,
1727
  'status' => 'complete',
1728
- );
1729
  printf( json_encode( $response ) );
1730
  wp_die();
1731
  }
1732
 
1733
-
1734
  /**
1735
  * Check Feed Upload Status (FBE v2.0)
1736
  * TODO: When migrating to FBE v2.0, remove above function and rename
1737
  * below function to ajax_check_feed_upload_status()
1738
  **/
1739
  public function ajax_check_feed_upload_status_v2() {
1740
-
1741
  \WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
1742
-
1743
  check_ajax_referer( 'wc_facebook_settings_jsx' );
1744
-
1745
  if ( $this->is_configured() ) {
1746
-
1747
- $response = array(
1748
  'connected' => true,
1749
  'status' => 'in progress',
1750
- );
1751
 
1752
  if ( ! empty( $this->get_upload_id() ) ) {
1753
 
@@ -1757,26 +1511,20 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1757
  include_once 'includes/fbproductfeed.php';
1758
  }
1759
 
1760
- $this->fbproductfeed = new \WC_Facebook_Product_Feed(
1761
- $this->get_product_catalog_id(),
1762
- $this->fbgraph
1763
- );
1764
  }
1765
 
1766
  $status = $this->fbproductfeed->is_upload_complete( $this->settings );
1767
 
1768
  $response['status'] = $status;
1769
-
1770
  } else {
1771
-
1772
- $response = array(
1773
  'connected' => true,
1774
  'status' => 'error',
1775
- );
1776
  }
1777
 
1778
  if ( 'complete' === $response['status'] ) {
1779
-
1780
  update_option(
1781
  $this->get_option_key(),
1782
  apply_filters(
@@ -1786,18 +1534,21 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1786
  );
1787
  }
1788
  } else {
1789
-
1790
- $response = array( 'connected' => false );
1791
  }
1792
-
1793
  printf( json_encode( $response ) );
1794
  wp_die();
1795
  }
1796
 
1797
  /**
1798
- * Display custom success message (sugar)
1799
- **/
1800
- function display_success_message( $msg ) {
 
 
 
 
 
1801
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1802
  set_transient(
1803
  'facebook_plugin_api_success',
@@ -1807,21 +1558,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1807
  }
1808
 
1809
  /**
1810
- * Display custom warning message (sugar)
1811
- **/
1812
- function display_warning_message( $msg ) {
1813
- $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1814
- set_transient(
1815
- 'facebook_plugin_api_warning',
1816
- $msg,
1817
- self::FB_MESSAGE_DISPLAY_TIME
1818
- );
1819
- }
1820
-
1821
- /**
1822
- * Display custom info message (sugar)
1823
- **/
1824
- function display_info_message( $msg ) {
1825
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1826
  set_transient(
1827
  'facebook_plugin_api_info',
@@ -1833,8 +1575,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1833
  /**
1834
  * Display custom "sticky" info message.
1835
  * Call remove_sticky_message or wait for time out.
1836
- **/
1837
- function display_sticky_message( $msg ) {
 
 
 
1838
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1839
  set_transient(
1840
  'facebook_plugin_api_sticky',
@@ -1844,123 +1589,44 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1844
  }
1845
 
1846
  /**
1847
- * Remove custom "sticky" info message
1848
- **/
1849
- function remove_sticky_message() {
 
 
1850
  delete_transient( 'facebook_plugin_api_sticky' );
1851
  }
1852
 
1853
- function remove_resync_message() {
 
 
 
 
 
1854
  $msg = get_transient( 'facebook_plugin_api_sticky' );
1855
  if ( $msg && strpos( $msg, 'Sync' ) !== false ) {
1856
  delete_transient( 'facebook_plugin_resync_sticky' );
1857
  }
1858
  }
1859
 
1860
-
1861
  /**
1862
  * Logs and stores custom error message (sugar).
1863
  *
1864
  * @param string $msg
 
1865
  */
1866
- function display_error_message( $msg ) {
1867
-
1868
  WC_Facebookcommerce_Utils::log( $msg );
1869
-
1870
  set_transient( 'facebook_plugin_api_error', $msg, self::FB_MESSAGE_DISPLAY_TIME );
1871
  }
1872
 
1873
-
1874
- /**
1875
- * Displays error message from API result (sugar).
1876
- *
1877
- * @param array $result
1878
- */
1879
- function display_error_message_from_result( $result ) {
1880
- $error = json_decode( $result['body'] )->error;
1881
- $msg = ( 'Fatal' === $error->message && ! empty( $error->error_user_title ) ) ? $error->error_user_title : $error->message;
1882
- $this->display_error_message( $msg );
1883
- }
1884
-
1885
-
1886
- /**
1887
- * Deals with FB API responses, displays error if FB API returns error.
1888
- *
1889
- * @param WP_Error|array $result API response
1890
- * @param array|null $logdata additional data for logging
1891
- * @param int|null $wpid post ID
1892
- * @return array|null|void result if response is 200, null otherwise
1893
- */
1894
- function check_api_result( $result, $logdata = null, $wpid = null ) {
1895
-
1896
- if ( is_wp_error( $result ) ) {
1897
-
1898
- WC_Facebookcommerce_Utils::log( $result->get_error_message() );
1899
-
1900
- $message = sprintf(
1901
- /* translators: Placeholders %1$s - original error message from Facebook API */
1902
- esc_html__( 'There was an issue connecting to the Facebook API: %1$s', 'facebook-for-woocommerce' ),
1903
- $result->get_error_message()
1904
- );
1905
-
1906
- $this->display_error_message( $message );
1907
-
1908
- return;
1909
- }
1910
-
1911
- if ( $result['response']['code'] != '200' ) {
1912
-
1913
- // Catch 10800 fb error code ("Duplicate retailer ID") and capture FBID
1914
- // if possible, otherwise let user know we found dupe SKUs
1915
- $body = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
1916
-
1917
- if ( $body && $body->error->code == '10800' ) {
1918
-
1919
- $error_data = $body->error->error_data; // error_data may contain FBIDs
1920
-
1921
- if ( $error_data && $wpid ) {
1922
-
1923
- $existing_id = $this->get_existing_fbid( $error_data, $wpid );
1924
-
1925
- if ( $existing_id ) {
1926
-
1927
- // Add "existing_id" ID to result
1928
- $body->id = $existing_id;
1929
- $result['body'] = json_encode( $body );
1930
- return $result;
1931
- }
1932
- }
1933
- } else {
1934
-
1935
- $this->display_error_message_from_result( $result );
1936
- }
1937
-
1938
- WC_Facebookcommerce_Utils::log( $result );
1939
-
1940
- $data = array(
1941
- 'result' => $result,
1942
- 'data' => $logdata,
1943
- );
1944
- WC_Facebookcommerce_Utils::fblog(
1945
- 'Non-200 error code from FB',
1946
- $data,
1947
- true
1948
- );
1949
-
1950
- return null;
1951
- }
1952
-
1953
- return $result;
1954
- }
1955
-
1956
-
1957
  /**
1958
  * Displays out of sync message if products are edited using WooCommerce Advanced Bulk Edit.
1959
  *
1960
- * @param $import_id
 
1961
  */
1962
- function ajax_woo_adv_bulk_edit_compat( $import_id ) {
1963
-
1964
  if ( ! WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'adv bulk edit', false ) ) {
1965
  return;
1966
  }
@@ -1973,20 +1639,32 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1973
  }
1974
  }
1975
 
1976
- function wp_all_import_compat( $import_id ) {
 
 
 
 
 
 
1977
  $import = new PMXI_Import_Record();
1978
  $import->getById( $import_id );
1979
- if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], array( 'product', 'product_variation' ) ) ) {
1980
  $this->display_out_of_sync_message( 'import' );
1981
  }
1982
  }
1983
 
1984
- function display_out_of_sync_message( $action_name ) {
 
 
 
 
 
 
1985
  $this->display_sticky_message(
1986
  sprintf(
1987
  'Products may be out of Sync with Facebook due to your recent ' . $action_name . '.' .
1988
  ' <a href="%s&fb_force_resync=true&remove_sticky=true">Re-Sync them with FB.</a>',
1989
- facebook_for_woocommerce()->get_settings_url()
1990
  )
1991
  );
1992
  }
@@ -1995,9 +1673,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1995
  * If we get a product group ID or product item ID back for a dupe retailer
1996
  * id error, update existing ID.
1997
  *
 
 
1998
  * @return null
1999
  **/
2000
- function get_existing_fbid( $error_data, $wpid ) {
2001
  if ( isset( $error_data->product_group_id ) ) {
2002
  update_post_meta(
2003
  $wpid,
@@ -2013,7 +1693,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2013
  );
2014
  return $error_data->product_item_id;
2015
  } else {
2016
- return;
2017
  }
2018
  }
2019
 
@@ -2021,8 +1701,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2021
  * Checks for API key and other API errors.
2022
  */
2023
  public function checks() {
2024
-
2025
  // TODO improve this by checking the settings page with Framework method and ensure error notices are displayed under the Integration sections {FN 2020-01-30}
 
2026
  if ( isset( $_GET['page'] ) && 'wc-facebook' === $_GET['page'] ) {
2027
  $this->display_errors();
2028
  }
@@ -2030,59 +1710,53 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2030
  $this->maybe_display_facebook_api_messages();
2031
  }
2032
 
2033
-
2034
  /**
2035
  * Gets a sample feed with up to 12 published products.
2036
  *
2037
  * @return string
2038
  */
2039
- function get_sample_product_feed() {
2040
-
2041
  ob_start();
2042
-
2043
  // get up to 12 published posts that are products
2044
- $args = array(
2045
  'post_type' => 'product',
2046
  'post_status' => 'publish',
2047
  'posts_per_page' => 12,
2048
  'fields' => 'ids',
2049
- );
2050
 
2051
  $post_ids = get_posts( $args );
2052
- $items = array();
2053
 
2054
  foreach ( $post_ids as $post_id ) {
2055
-
2056
  $woo_product = new WC_Facebook_Product( $post_id );
2057
  $product_data = $woo_product->prepare_product();
2058
 
2059
- $feed_item = array(
2060
  'title' => strip_tags( $product_data['name'] ),
2061
  'availability' => $woo_product->is_in_stock() ? 'in stock' :
2062
  'out of stock',
2063
  'description' => strip_tags( $product_data['description'] ),
2064
  'id' => $product_data['retailer_id'],
2065
  'image_link' => $product_data['image_url'],
2066
- 'brand' => Framework\SV_WC_Helper::str_truncate( wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() ), 100 ),
2067
  'link' => $product_data['url'],
2068
  'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
2069
- );
2070
-
2071
  array_push( $items, $feed_item );
2072
  }
2073
-
2074
  // https://codex.wordpress.org/Function_Reference/wp_reset_postdata
2075
  wp_reset_postdata();
2076
-
2077
  ob_end_clean();
2078
-
2079
- return json_encode( array( $items ) );
2080
  }
2081
 
2082
  /**
2083
  * Loop through array of WPIDs to remove metadata.
2084
- **/
2085
- function delete_post_meta_loop( $products ) {
 
 
2086
  foreach ( $products as $product_id ) {
2087
  delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
2088
  delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
@@ -2093,7 +1767,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2093
  /**
2094
  * Remove FBIDs from all products when resetting store.
2095
  **/
2096
- function reset_all_products() {
2097
  if ( ! is_admin() ) {
2098
  WC_Facebookcommerce_Utils::log(
2099
  'Not resetting any FBIDs from products,
@@ -2102,30 +1776,27 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2102
  return false;
2103
  }
2104
 
2105
- $test_instance = WC_Facebook_Integration_Test::get_instance( $this );
2106
- $this->test_mode = $test_instance::$test_mode;
2107
-
2108
  // Include draft products (omit 'post_status' => 'publish')
2109
  WC_Facebookcommerce_Utils::log( 'Removing FBIDs from all products' );
2110
 
2111
  $post_ids = get_posts(
2112
- array(
2113
  'post_type' => 'product',
2114
  'posts_per_page' => -1,
2115
  'fields' => 'ids',
2116
- )
2117
  );
2118
 
2119
- $children = array();
2120
  foreach ( $post_ids as $post_id ) {
2121
  $children = array_merge(
2122
  get_posts(
2123
- array(
2124
  'post_type' => 'product_variation',
2125
  'posts_per_page' => -1,
2126
  'post_parent' => $post_id,
2127
  'fields' => 'ids',
2128
- )
2129
  ),
2130
  $children
2131
  );
@@ -2139,10 +1810,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2139
 
2140
  /**
2141
  * Remove FBIDs from a single WC product
2142
- **/
2143
- function reset_single_product( $wp_id ) {
 
 
2144
  $woo_product = new WC_Facebook_Product( $wp_id );
2145
- $products = array( $woo_product->get_id() );
2146
  if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
2147
  $products = array_merge( $products, $woo_product->get_children() );
2148
  }
@@ -2152,7 +1825,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2152
  WC_Facebookcommerce_Utils::log( 'Deleted FB Metadata for product ' . $wp_id );
2153
  }
2154
 
2155
- function ajax_reset_all_fb_products() {
 
 
 
 
 
2156
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset products', true );
2157
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2158
  $this->reset_all_products();
@@ -2160,7 +1838,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2160
  wp_die();
2161
  }
2162
 
2163
- function ajax_reset_single_fb_product() {
 
 
 
 
 
2164
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset single product', true );
2165
  check_ajax_referer( 'wc_facebook_metabox_jsx' );
2166
  if ( ! isset( $_POST['wp_id'] ) ) {
@@ -2177,7 +1860,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2177
  wp_die();
2178
  }
2179
 
2180
- function ajax_delete_fb_product() {
 
 
 
 
 
2181
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'delete single product', true );
2182
  check_ajax_referer( 'wc_facebook_metabox_jsx' );
2183
  if ( ! isset( $_POST['wp_id'] ) ) {
@@ -2197,14 +1885,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2197
  * @internal
2198
  */
2199
  public function ajax_sync_all_fb_products() {
2200
-
2201
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
2202
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2203
 
2204
  $this->sync_facebook_products();
2205
  }
2206
 
2207
-
2208
  /**
2209
  * Syncs Facebook products using the GraphAPI.
2210
  *
@@ -2212,16 +1898,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2212
  * Ends the request sending a JSON response indicating success or failure.
2213
  *
2214
  * @since 1.10.2
2215
- *
2216
  */
2217
  private function sync_facebook_products() {
2218
-
2219
  try {
2220
  $this->sync_facebook_products_using_background_processor();
2221
  wp_send_json_success();
2222
-
2223
- } catch ( Framework\SV_WC_Plugin_Exception $e ) {
2224
-
2225
  // Access token has expired
2226
  if ( 190 === $e->getCode() ) {
2227
  $error_message = __( 'Your connection has expired.', 'facebook-for-woocommerce' ) . ' <strong>' . __( 'Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook.', 'facebook-for-woocommerce' ) . '</strong>';
@@ -2235,39 +1917,32 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2235
  $error_message
2236
  );
2237
 
2238
- wp_send_json_error( array( 'error' => $message ) );
2239
  }
2240
  }
2241
 
2242
-
2243
  /**
2244
  * Syncs Facebook products using the background processor.
2245
  *
2246
  * @since 1.10.2
2247
- *
2248
- * @throws Framework\SV_WC_Plugin_Exception
2249
  * @return bool
 
 
2250
  */
2251
  private function sync_facebook_products_using_background_processor() {
2252
-
2253
  if ( ! $this->is_product_sync_enabled() ) {
2254
-
2255
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
2256
-
2257
- throw new Framework\SV_WC_Plugin_Exception( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
2258
  }
2259
 
2260
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
2261
-
2262
  WC_Facebookcommerce_Utils::log( sprintf( 'Not syncing, the plugin is not configured or the Catalog ID is missing' ) );
2263
-
2264
- throw new Framework\SV_WC_Plugin_Exception( __( 'The plugin is not configured or the Catalog ID is missing.', 'facebook-for-woocommerce' ) );
2265
  }
2266
 
2267
  $this->remove_resync_message();
2268
 
2269
  $currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
2270
-
2271
  if ( isset( $this->background_processor ) ) {
2272
  if ( $this->background_processor->is_updating() ) {
2273
  $this->background_processor->handle_cron_healthcheck();
@@ -2276,38 +1951,29 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2276
  }
2277
 
2278
  if ( $currently_syncing ) {
2279
-
2280
  WC_Facebookcommerce_Utils::log( 'Not syncing again, sync already in progress' );
2281
  WC_Facebookcommerce_Utils::fblog(
2282
  'Tried to sync during an in-progress sync!',
2283
- array(),
2284
  true
2285
  );
2286
-
2287
- throw new Framework\SV_WC_Plugin_Exception( __( 'A product sync is in progress. Please wait until the sync finishes before starting a new one.', 'facebook-for-woocommerce' ) );
2288
  }
2289
 
2290
- if ( ! $this->fbgraph->is_product_catalog_valid( $this->get_product_catalog_id() ) ) {
2291
-
2292
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
2293
  WC_Facebookcommerce_Utils::fblog(
2294
  'Tried to sync with an invalid product catalog!',
2295
- array(),
2296
  true
2297
  );
2298
-
2299
- throw new Framework\SV_WC_Plugin_Exception( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
2300
  }
2301
 
2302
  // Get all published posts. First unsynced then already-synced.
2303
- $post_ids_new = WC_Facebookcommerce_Utils::get_wp_posts(
2304
- self::FB_PRODUCT_GROUP_ID,
2305
- 'NOT EXISTS'
2306
- );
2307
- $post_ids_old = WC_Facebookcommerce_Utils::get_wp_posts(
2308
- self::FB_PRODUCT_GROUP_ID,
2309
- 'EXISTS'
2310
- );
2311
 
2312
  $total_new = count( $post_ids_new );
2313
  $total_old = count( $post_ids_old );
@@ -2327,32 +1993,18 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2327
  'Starting background sync to Facebook: %d products...',
2328
  $total
2329
  );
2330
-
2331
- set_transient(
2332
- self::FB_SYNC_IN_PROGRESS,
2333
- true,
2334
- self::FB_SYNC_TIMEOUT
2335
- );
2336
-
2337
- set_transient(
2338
- self::FB_SYNC_REMAINING,
2339
- (int) $total
2340
- );
2341
-
2342
  $this->display_info_message( $starting_message );
2343
  WC_Facebookcommerce_Utils::log( $starting_message );
2344
-
2345
  foreach ( $post_ids as $post_id ) {
2346
- WC_Facebookcommerce_Utils::log( 'Pushing post to queue: ' . $post_id );
2347
- $this->background_processor->push_to_queue( $post_id );
2348
  }
2349
 
2350
  $this->background_processor->save()->dispatch();
2351
  // reset FB_SYNC_REMAINING to avoid race condition
2352
- set_transient(
2353
- self::FB_SYNC_REMAINING,
2354
- (int) $total
2355
- );
2356
  // handle_cron_healthcheck must be called
2357
  // https://github.com/A5hleyRich/wp-background-processing/issues/34
2358
  $this->background_processor->handle_cron_healthcheck();
@@ -2361,11 +2013,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2361
  $count = ( $total_old === $total ) ? 0 : $total_old;
2362
  foreach ( $post_ids as $post_id ) {
2363
  // Repeatedly overwrite sync total while in actual sync loop
2364
- set_transient(
2365
- self::FB_SYNC_IN_PROGRESS,
2366
- true,
2367
- self::FB_SYNC_TIMEOUT
2368
- );
2369
 
2370
  $this->display_sticky_message(
2371
  sprintf(
@@ -2402,73 +2050,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2402
  * @deprecated since 1.10.0
2403
  **/
2404
  public function ajax_fb_toggle_visibility() {
2405
-
2406
  wc_deprecated_function( __METHOD__, '1.10.0' );
2407
  }
2408
 
2409
-
2410
- /**
2411
- * Initializes the settings form fields.
2412
- *
2413
- * @since 1.0.0
2414
- * @deprecated 2.0.0
2415
- *
2416
- * @internal
2417
- */
2418
- public function init_form_fields() {
2419
-
2420
- wc_deprecated_function( __METHOD__, '2.0.0' );
2421
- }
2422
-
2423
-
2424
- /**
2425
- * Processes and saves options.
2426
- *
2427
- * TODO: remove this or move it to the new settings processing
2428
- *
2429
- * @internal
2430
- *
2431
- * @since 1.10.0
2432
- * @deprecated 2.0.0
2433
- */
2434
- public function process_admin_options() {
2435
-
2436
- wc_deprecated_function( __METHOD__, '2.0.0' );
2437
- }
2438
-
2439
-
2440
  /** Getter methods ************************************************************************************************/
2441
 
2442
-
2443
- /**
2444
- * Gets the page access token.
2445
- *
2446
- * TODO: remove this method by version 3.0.0 or by 2021-08-21 {WV 2020-08-21}
2447
- *
2448
- * @since 1.10.0
2449
- * @deprecated 2.1.0
2450
- *
2451
- * @return string
2452
- */
2453
- public function get_page_access_token() {
2454
-
2455
- wc_deprecated_function( __METHOD__, '2.1.0', Connection::class . '::get_page_access_token()' );
2456
-
2457
- $access_token = facebook_for_woocommerce()->get_connection_handler()->get_page_access_token();
2458
-
2459
- /**
2460
- * Filters the Facebook page access token.
2461
- *
2462
- * @since 1.10.0
2463
- * @deprecated 2.1.0
2464
- *
2465
- * @param string $page_access_token Facebook page access token
2466
- * @param \WC_Facebookcommerce_Integration $integration the integration instance
2467
- */
2468
- return (string) apply_filters( 'wc_facebook_page_access_token', ! $this->is_feed_migrated() ? $access_token : '', $this );
2469
- }
2470
-
2471
-
2472
  /**
2473
  * Gets the product catalog ID.
2474
  *
@@ -2477,11 +2063,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2477
  * @return string
2478
  */
2479
  public function get_product_catalog_id() {
2480
-
2481
  if ( ! is_string( $this->product_catalog_id ) ) {
2482
-
2483
- $value = get_option( self::OPTION_PRODUCT_CATALOG_ID, '' );
2484
-
2485
  $this->product_catalog_id = is_string( $value ) ? $value : '';
2486
  }
2487
 
@@ -2493,10 +2076,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2493
  * @param string $product_catalog_id Facebook product catalog ID
2494
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2495
  */
2496
- return (string) apply_filters( 'wc_facebook_product_catalog_id', $this->product_catalog_id, $this );
2497
  }
2498
 
2499
-
2500
  /**
2501
  * Gets the external merchant settings ID.
2502
  *
@@ -2505,11 +2087,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2505
  * @return string
2506
  */
2507
  public function get_external_merchant_settings_id() {
2508
-
2509
  if ( ! is_string( $this->external_merchant_settings_id ) ) {
2510
-
2511
- $value = get_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, '' );
2512
-
2513
  $this->external_merchant_settings_id = is_string( $value ) ? $value : '';
2514
  }
2515
 
@@ -2524,7 +2103,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2524
  return (string) apply_filters( 'wc_facebook_external_merchant_settings_id', $this->external_merchant_settings_id, $this );
2525
  }
2526
 
2527
-
2528
  /**
2529
  * Gets the feed ID.
2530
  *
@@ -2533,11 +2111,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2533
  * @return string
2534
  */
2535
  public function get_feed_id() {
2536
-
2537
  if ( ! is_string( $this->feed_id ) ) {
2538
-
2539
- $value = get_option( self::OPTION_FEED_ID, '' );
2540
-
2541
  $this->feed_id = is_string( $value ) ? $value : '';
2542
  }
2543
 
@@ -2552,7 +2127,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2552
  return (string) apply_filters( 'wc_facebook_feed_id', $this->feed_id, $this );
2553
  }
2554
 
2555
-
2556
  /***
2557
  * Gets the Facebook Upload ID.
2558
  *
@@ -2561,11 +2135,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2561
  * @return string
2562
  */
2563
  public function get_upload_id() {
2564
-
2565
  if ( ! is_string( $this->upload_id ) ) {
2566
-
2567
- $value = get_option( self::OPTION_UPLOAD_ID, '' );
2568
-
2569
  $this->upload_id = is_string( $value ) ? $value : '';
2570
  }
2571
 
@@ -2580,7 +2151,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2580
  return (string) apply_filters( 'wc_facebook_upload_id', $this->upload_id, $this );
2581
  }
2582
 
2583
-
2584
  /**
2585
  * Gets the Facebook pixel install time in UTC seconds.
2586
  *
@@ -2589,11 +2159,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2589
  * @return int
2590
  */
2591
  public function get_pixel_install_time() {
2592
-
2593
  if ( ! (int) $this->pixel_install_time ) {
2594
-
2595
- $value = (int) get_option( self::OPTION_PIXEL_INSTALL_TIME, 0 );
2596
-
2597
  $this->pixel_install_time = $value ?: null;
2598
  }
2599
 
@@ -2608,7 +2175,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2608
  return (int) apply_filters( 'wc_facebook_pixel_install_time', $this->pixel_install_time, $this );
2609
  }
2610
 
2611
-
2612
  /**
2613
  * Gets the configured JS SDK version.
2614
  *
@@ -2617,11 +2183,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2617
  * @return string
2618
  */
2619
  public function get_js_sdk_version() {
2620
-
2621
  if ( ! is_string( $this->js_sdk_version ) ) {
2622
-
2623
- $value = get_option( self::OPTION_JS_SDK_VERSION, '' );
2624
-
2625
  $this->js_sdk_version = is_string( $value ) ? $value : '';
2626
  }
2627
 
@@ -2636,7 +2199,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2636
  return (string) apply_filters( 'wc_facebook_js_sdk_version', $this->js_sdk_version, $this );
2637
  }
2638
 
2639
-
2640
  /**
2641
  * Gets the configured Facebook page ID.
2642
  *
@@ -2645,7 +2207,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2645
  * @return string
2646
  */
2647
  public function get_facebook_page_id() {
2648
-
2649
  /**
2650
  * Filters the configured Facebook page ID.
2651
  *
@@ -2657,7 +2218,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2657
  return (string) apply_filters( 'wc_facebook_page_id', get_option( self::SETTING_FACEBOOK_PAGE_ID, '' ), $this );
2658
  }
2659
 
2660
-
2661
  /**
2662
  * Gets the configured Facebook pixel ID.
2663
  *
@@ -2666,7 +2226,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2666
  * @return string
2667
  */
2668
  public function get_facebook_pixel_id() {
2669
-
2670
  /**
2671
  * Filters the configured Facebook pixel ID.
2672
  *
@@ -2678,25 +2237,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2678
  return (string) apply_filters( 'wc_facebook_pixel_id', get_option( self::SETTING_FACEBOOK_PIXEL_ID, '' ), $this );
2679
  }
2680
 
2681
- /**
2682
- * Gets the configured use s2s flag.
2683
- *
2684
- * @return bool
2685
- */
2686
- public function is_use_s2s_enabled() {
2687
- return WC_Facebookcommerce_Pixel::get_use_s2s();
2688
- }
2689
-
2690
- /**
2691
- * Gets the configured access token
2692
- *
2693
- * @return string
2694
- */
2695
- public function get_access_token() {
2696
- return WC_Facebookcommerce_Pixel::get_access_token();
2697
- }
2698
-
2699
-
2700
  /**
2701
  * Gets the IDs of the categories to be excluded from sync.
2702
  *
@@ -2705,7 +2245,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2705
  * @return int[]
2706
  */
2707
  public function get_excluded_product_category_ids() {
2708
-
2709
  /**
2710
  * Filters the configured excluded product category IDs.
2711
  *
@@ -2714,10 +2253,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2714
  * @param int[] $category_ids the configured excluded product category IDs
2715
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2716
  */
2717
- return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, array() ), $this );
2718
  }
2719
 
2720
-
2721
  /**
2722
  * Gets the IDs of the tags to be excluded from sync.
2723
  *
@@ -2726,7 +2264,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2726
  * @return int[]
2727
  */
2728
  public function get_excluded_product_tag_ids() {
2729
-
2730
  /**
2731
  * Filters the configured excluded product tag IDs.
2732
  *
@@ -2735,10 +2272,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2735
  * @param int[] $tag_ids the configured excluded product tag IDs
2736
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2737
  */
2738
- return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, array() ), $this );
2739
  }
2740
 
2741
-
2742
  /**
2743
  * Gets the configured product description mode.
2744
  *
@@ -2747,7 +2283,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2747
  * @return string
2748
  */
2749
  public function get_product_description_mode() {
2750
-
2751
  /**
2752
  * Filters the configured product description mode.
2753
  *
@@ -2758,10 +2293,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2758
  */
2759
  $mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
2760
 
2761
- $valid_modes = array(
2762
  self::PRODUCT_DESCRIPTION_MODE_STANDARD,
2763
  self::PRODUCT_DESCRIPTION_MODE_SHORT,
2764
- );
2765
 
2766
  if ( ! in_array( $mode, $valid_modes, true ) ) {
2767
  $mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
@@ -2770,23 +2305,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2770
  return $mode;
2771
  }
2772
 
2773
-
2774
- /**
2775
- * Gets the configured scheduled re-sync offset in seconds.
2776
- *
2777
- * Returns null if no offset is configured.
2778
- *
2779
- * @since 1.10.0
2780
- * @deprecated 2.0.0
2781
- *
2782
- * @return int|null
2783
- */
2784
- public function get_scheduled_resync_offset() {
2785
-
2786
- wc_deprecated_function( __METHOD__, '2.0.0' );
2787
- }
2788
-
2789
-
2790
  /**
2791
  * Gets the configured Facebook messenger locale.
2792
  *
@@ -2795,7 +2313,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2795
  * @return string
2796
  */
2797
  public function get_messenger_locale() {
2798
-
2799
  /**
2800
  * Filters the configured Facebook messenger locale.
2801
  *
@@ -2807,7 +2324,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2807
  return (string) apply_filters( 'wc_facebook_messenger_locale', get_option( self::SETTING_MESSENGER_LOCALE, 'en_US' ), $this );
2808
  }
2809
 
2810
-
2811
  /**
2812
  * Gets the configured Facebook messenger greeting.
2813
  *
@@ -2816,7 +2332,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2816
  * @return string
2817
  */
2818
  public function get_messenger_greeting() {
2819
-
2820
  /**
2821
  * Filters the configured Facebook messenger greeting.
2822
  *
@@ -2826,11 +2341,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2826
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2827
  */
2828
  $greeting = (string) apply_filters( 'wc_facebook_messenger_greeting', get_option( self::SETTING_MESSENGER_GREETING, __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' ) ), $this );
2829
-
2830
- return Framework\SV_WC_Helper::str_truncate( $greeting, $this->get_messenger_greeting_max_characters(), '' );
2831
  }
2832
 
2833
-
2834
  /**
2835
  * Gets the maximum number of characters allowed in the messenger greeting.
2836
  *
@@ -2839,7 +2352,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2839
  * @return int
2840
  */
2841
  public function get_messenger_greeting_max_characters() {
2842
-
2843
  $default = 80;
2844
 
2845
  /**
@@ -2855,7 +2367,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2855
  return $max < 1 ? $default : $max;
2856
  }
2857
 
2858
-
2859
  /**
2860
  * Gets the configured Facebook messenger color hex.
2861
  *
@@ -2866,7 +2377,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2866
  * @return string
2867
  */
2868
  public function get_messenger_color_hex() {
2869
-
2870
  /**
2871
  * Filters the configured Facebook messenger color hex.
2872
  *
@@ -2878,28 +2388,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2878
  return (string) apply_filters( 'wc_facebook_messenger_color_hex', get_option( self::SETTING_MESSENGER_COLOR_HEX, '#0084ff' ), $this );
2879
  }
2880
 
2881
-
2882
  /** Setter methods ************************************************************************************************/
2883
 
2884
 
2885
- /**
2886
- * Updates the Facebook page access token.
2887
- *
2888
- * TODO: remove this method by version 3.0.0 or by 2021-08-21 {WV 2020-08-21}
2889
- *
2890
- * @since 1.10.0
2891
- * @deprecated 2.1.0
2892
- *
2893
- * @param string $value page access token value
2894
- */
2895
- public function update_page_access_token( $value ) {
2896
-
2897
- wc_deprecated_function( __METHOD__, '2.1.0', Connection::class . '::update_page_access_token()' );
2898
-
2899
- facebook_for_woocommerce()->get_connection_handler()->update_page_access_token( $value );
2900
- }
2901
-
2902
-
2903
  /**
2904
  * Updates the Facebook product catalog ID.
2905
  *
@@ -2908,13 +2399,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2908
  * @param string $value product catalog ID value
2909
  */
2910
  public function update_product_catalog_id( $value ) {
2911
-
2912
  $this->product_catalog_id = $this->sanitize_facebook_credential( $value );
2913
 
2914
  update_option( self::OPTION_PRODUCT_CATALOG_ID, $this->product_catalog_id );
2915
  }
2916
 
2917
-
2918
  /**
2919
  * Updates the Facebook external merchant settings ID.
2920
  *
@@ -2923,13 +2412,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2923
  * @param string $value external merchant settings ID value
2924
  */
2925
  public function update_external_merchant_settings_id( $value ) {
2926
-
2927
  $this->external_merchant_settings_id = $this->sanitize_facebook_credential( $value );
2928
 
2929
  update_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, $this->external_merchant_settings_id );
2930
  }
2931
 
2932
-
2933
  /**
2934
  * Updates the Facebook feed ID.
2935
  *
@@ -2938,13 +2425,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2938
  * @param string $value feed ID value
2939
  */
2940
  public function update_feed_id( $value ) {
2941
-
2942
  $this->feed_id = $this->sanitize_facebook_credential( $value );
2943
 
2944
  update_option( self::OPTION_FEED_ID, $this->feed_id );
2945
  }
2946
 
2947
-
2948
  /**
2949
  * Updates the Facebook upload ID.
2950
  *
@@ -2953,13 +2438,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2953
  * @param string $value upload ID value
2954
  */
2955
  public function update_upload_id( $value ) {
2956
-
2957
  $this->upload_id = $this->sanitize_facebook_credential( $value );
2958
 
2959
  update_option( self::OPTION_UPLOAD_ID, $this->upload_id );
2960
  }
2961
 
2962
-
2963
  /**
2964
  * Updates the Facebook pixel install time.
2965
  *
@@ -2968,7 +2451,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2968
  * @param int $value pixel install time, in UTC seconds
2969
  */
2970
  public function update_pixel_install_time( $value ) {
2971
-
2972
  $value = (int) $value;
2973
 
2974
  $this->pixel_install_time = $value ?: null;
@@ -2976,7 +2458,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2976
  update_option( self::OPTION_PIXEL_INSTALL_TIME, $value ?: '' );
2977
  }
2978
 
2979
-
2980
  /**
2981
  * Updates the Facebook JS SDK version.
2982
  *
@@ -2985,9 +2466,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2985
  * @param string $value JS SDK version
2986
  */
2987
  public function update_js_sdk_version( $value ) {
2988
-
2989
  $this->js_sdk_version = $this->sanitize_facebook_credential( $value );
2990
-
2991
  update_option( self::OPTION_JS_SDK_VERSION, $this->js_sdk_version );
2992
  }
2993
 
@@ -3001,14 +2480,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3001
  * @return string
3002
  */
3003
  private function sanitize_facebook_credential( $value ) {
3004
-
3005
  return wc_clean( is_string( $value ) ? $value : '' );
3006
  }
3007
 
3008
-
3009
- /** Conditional methods *******************************************************************************************/
3010
-
3011
-
3012
  /**
3013
  * Determines whether Facebook for WooCommerce is configured.
3014
  *
@@ -3017,11 +2491,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3017
  * @return bool
3018
  */
3019
  public function is_configured() {
3020
-
3021
- return $this->get_facebook_page_id() && facebook_for_woocommerce()->get_connection_handler()->is_connected();
3022
  }
3023
 
3024
-
3025
  /**
3026
  * Determines whether advanced matching is enabled.
3027
  *
@@ -3030,7 +2502,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3030
  * @return bool
3031
  */
3032
  public function is_advanced_matching_enabled() {
3033
-
3034
  /**
3035
  * Filters whether advanced matching is enabled.
3036
  *
@@ -3042,7 +2513,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3042
  return (bool) apply_filters( 'wc_facebook_is_advanced_matching_enabled', true, $this );
3043
  }
3044
 
3045
-
3046
  /**
3047
  * Determines whether product sync is enabled.
3048
  *
@@ -3051,7 +2521,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3051
  * @return bool
3052
  */
3053
  public function is_product_sync_enabled() {
3054
-
3055
  /**
3056
  * Filters whether product sync is enabled.
3057
  *
@@ -3080,26 +2549,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3080
  * @return bool
3081
  */
3082
  public function is_legacy_feed_file_generation_enabled() {
3083
- return (bool) ( 'yes' === get_option( self::OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED, 'yes' ) );
3084
  }
3085
 
3086
-
3087
- /**
3088
- * Determines whether the scheduled re-sync is enabled.
3089
- *
3090
- * @since 1.10.0
3091
- * @deprecated 2.0.0
3092
- *
3093
- * @return bool
3094
- */
3095
- public function is_scheduled_resync_enabled() {
3096
-
3097
- wc_deprecated_function( __METHOD__, '2.0.0' );
3098
-
3099
- return false;
3100
- }
3101
-
3102
-
3103
  /**
3104
  * Determines whether the Facebook messenger is enabled.
3105
  *
@@ -3108,7 +2560,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3108
  * @return bool
3109
  */
3110
  public function is_messenger_enabled() {
3111
-
3112
  /**
3113
  * Filters whether the Facebook messenger is enabled.
3114
  *
@@ -3120,7 +2571,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3120
  return (bool) apply_filters( 'wc_facebook_is_messenger_enabled', 'yes' === get_option( self::SETTING_ENABLE_MESSENGER ), $this );
3121
  }
3122
 
3123
-
3124
  /**
3125
  * Determines whether debug mode is enabled.
3126
  *
@@ -3129,7 +2579,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3129
  * @return bool
3130
  */
3131
  public function is_debug_mode_enabled() {
3132
-
3133
  /**
3134
  * Filters whether debug mode is enabled.
3135
  *
@@ -3158,13 +2607,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3158
  * They will be disabled by default. Enabling them will require setting an option in the options table.
3159
  *
3160
  * @since 2.6.6
3161
- *
3162
  */
3163
  public function are_headers_requested_for_debug() {
3164
  return (bool) get_option( self::SETTING_REQUEST_HEADERS_IN_DEBUG_MODE, false );
3165
  }
3166
 
3167
-
3168
  /***
3169
  * Determines if the feed has been migrated from FBE 1 to FBE 1.5
3170
  *
@@ -3173,246 +2620,105 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3173
  * @return bool
3174
  */
3175
  public function is_feed_migrated() {
3176
-
3177
  if ( ! is_bool( $this->feed_migrated ) ) {
3178
-
3179
- $value = get_option( 'wc_facebook_feed_migrated', 'no' );
3180
-
3181
  $this->feed_migrated = wc_string_to_bool( $value );
3182
  }
3183
-
3184
  return $this->feed_migrated;
3185
  }
3186
 
3187
-
3188
  /**
3189
  * Gets message HTML.
3190
  *
 
 
3191
  * @return string
3192
  */
3193
- private function get_message_html( $message, $type = 'error' ) {
3194
  ob_start();
3195
-
3196
- ?>
3197
- <div class="notice is-dismissible notice-<?php echo esc_attr( $type ); ?>">
3198
- <p>
3199
- <?php
3200
- // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3201
- echo $message;
3202
- ?>
3203
- </p>
3204
- </div>
3205
- <?php
3206
-
3207
  return ob_get_clean();
3208
  }
3209
 
3210
-
3211
  /**
3212
  * Displays relevant messages to user from transients, clear once displayed.
3213
  */
3214
  public function maybe_display_facebook_api_messages() {
3215
-
3216
- if ( $error_msg = get_transient( 'facebook_plugin_api_error' ) ) {
3217
-
3218
  $message = '<strong>' . __( 'Facebook for WooCommerce error:', 'facebook-for-woocommerce' ) . '</strong></br>' . $error_msg;
3219
-
3220
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3221
  echo $this->get_message_html( $message );
3222
-
3223
  delete_transient( 'facebook_plugin_api_error' );
3224
-
3225
  WC_Facebookcommerce_Utils::fblog(
3226
  $error_msg,
3227
- array(),
3228
  true
3229
  );
3230
  }
3231
-
3232
  $warning_msg = get_transient( 'facebook_plugin_api_warning' );
3233
-
3234
  if ( $warning_msg ) {
3235
-
3236
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3237
  echo $this->get_message_html( $warning_msg, 'warning' );
3238
-
3239
  delete_transient( 'facebook_plugin_api_warning' );
3240
  }
3241
-
3242
  $success_msg = get_transient( 'facebook_plugin_api_success' );
3243
-
3244
  if ( $success_msg ) {
3245
-
3246
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3247
  echo $this->get_message_html( $success_msg, 'success' );
3248
-
3249
  delete_transient( 'facebook_plugin_api_success' );
3250
  }
3251
-
3252
  $info_msg = get_transient( 'facebook_plugin_api_info' );
3253
-
3254
  if ( $info_msg ) {
3255
-
3256
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3257
  echo $this->get_message_html( $info_msg, 'info' );
3258
-
3259
  delete_transient( 'facebook_plugin_api_info' );
3260
  }
3261
-
3262
  $sticky_msg = get_transient( 'facebook_plugin_api_sticky' );
3263
-
3264
  if ( $sticky_msg ) {
3265
-
3266
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3267
  echo $this->get_message_html( $sticky_msg, 'info' );
3268
-
3269
  // transient must be deleted elsewhere, or wait for timeout
3270
  }
3271
  }
3272
 
3273
-
3274
  /**
3275
- * Gets the array that holds the name and url of the configured Facebook page.
3276
- *
3277
- * @since 2.0.0
3278
- *
3279
- * @return array
3280
- */
3281
- private function get_page() {
3282
-
3283
- if ( ! is_array( $this->page ) && $this->is_configured() ) {
3284
-
3285
- try {
3286
-
3287
- $response = facebook_for_woocommerce()->get_api()->get_page( $this->get_facebook_page_id() );
3288
-
3289
- $this->page = array(
3290
- 'name' => $response->get_name(),
3291
- 'url' => $response->get_url(),
3292
- );
3293
-
3294
- } catch ( Framework\SV_WC_API_Exception $e ) {
3295
-
3296
- // we intentionally set $this->page to an empty array if an error occurs to avoid additional API requests
3297
- // it's unlikely that we will get a different result if the exception was caused by an expired token, incorrect page ID, or rate limiting error
3298
- $this->page = array();
3299
-
3300
- $message = sprintf( __( 'There was an error trying to retrieve information about the Facebook page: %s' ), $e->getMessage() );
3301
-
3302
- facebook_for_woocommerce()->log( $message );
3303
- }
3304
- }
3305
-
3306
- return is_array( $this->page ) ? $this->page : array();
3307
- }
3308
-
3309
-
3310
- /**
3311
- * Gets the name of the configured Facebook page.
3312
- *
3313
- * @return string
3314
  */
3315
- public function get_page_name() {
3316
-
3317
- $page = $this->get_page();
3318
 
3319
- return isset( $page['name'] ) ? $page['name'] : '';
 
 
 
 
 
 
 
3320
  }
3321
 
3322
-
3323
  /**
3324
- * Gets the Facebook page URL.
3325
- *
3326
- * @since 1.10.0
3327
  *
3328
- * @return string
3329
- */
3330
- public function get_page_url() {
3331
-
3332
- $page = $this->get_page();
3333
-
3334
- return isset( $page['url'] ) ? $page['url'] : '';
3335
- }
3336
-
3337
-
3338
- /**
3339
- * Gets Messenger or Instagram tooltip message.
3340
- *
3341
- * @return string
3342
- */
3343
- function get_nux_message_ifexist() {
3344
-
3345
- $nux_type_to_elemid_map = array(
3346
- 'messenger_chat' => 'connect_button',
3347
- 'instagram_shopping' => 'connect_button',
3348
- );
3349
-
3350
- $nux_type_to_message_map = array(
3351
- 'messenger_chat' => __( 'Get started with Messenger Customer Chat' ),
3352
- 'instagram_shopping' => __( 'Get started with Instagram Shopping' ),
3353
- );
3354
-
3355
- $message = '';
3356
-
3357
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
3358
- if ( isset( $_GET['nux'] ) ) {
3359
-
3360
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
3361
- $nux_type = sanitize_text_field( wp_unslash( $_GET['nux'] ) );
3362
-
3363
- ob_start();
3364
-
3365
- ?>
3366
-
3367
- <div class="nux-message" style="display: none;"
3368
- data-target="<?php echo esc_attr( $nux_type_to_elemid_map[ $nux_type ] ); ?>">
3369
- <div class="nux-message-text">
3370
- <?php echo esc_attr( $nux_type_to_message_map[ $nux_type ] ); ?>
3371
- </div>
3372
- <div class="nux-message-arrow"></div>
3373
- <i class="nux-message-close-btn">x</i>
3374
- </div>
3375
- <script>( function () { fbe_init_nux_messages(); } )();</script>
3376
-
3377
- <?php
3378
-
3379
- $message = ob_get_clean();
3380
- }
3381
-
3382
- return $message;
3383
- }
3384
-
3385
-
3386
- /**
3387
- * Admin Panel Options
3388
  */
3389
- function admin_options() {
3390
-
3391
- facebook_for_woocommerce()->get_message_handler()->show_messages();
3392
-
3393
- ?>
3394
-
3395
- <div id="integration-settings" <?php echo ! $this->is_configured() ? 'style="display: none"' : ''; ?>>
3396
- <table class="form-table"><?php $this->generate_settings_html( $this->get_form_fields() ); ?></table>
3397
- </div>
3398
-
3399
- <?php
3400
- }
3401
-
3402
-
3403
- function delete_product_item( $wp_id ) {
3404
  $fb_product_item_id = $this->get_product_fbid(
3405
  self::FB_PRODUCT_ITEM_ID,
3406
  $wp_id
3407
  );
3408
  if ( $fb_product_item_id ) {
3409
- $pi_result =
3410
- $this->fbgraph->delete_product_item( $fb_product_item_id );
3411
  WC_Facebookcommerce_Utils::log( $pi_result );
3412
  }
3413
  }
3414
 
3415
-
3416
  /**
3417
  * Uses the Graph API to delete the Product Group associated with the given product.
3418
  *
@@ -3420,100 +2726,81 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3420
  *
3421
  * @param int $product_id product ID
3422
  */
3423
- private function delete_product_group( $product_id ) {
3424
-
3425
  $product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $product_id );
3426
-
3427
  if ( $product_group_id ) {
3428
-
3429
  // TODO: replace with a call to API::delete_product_group() {WV 2020-05-26}
3430
- $pg_result = $this->fbgraph->delete_product_group( $product_group_id );
3431
-
3432
  \WC_Facebookcommerce_Utils::log( $pg_result );
3433
  }
3434
  }
3435
 
3436
-
3437
- function fb_duplicate_product_reset_meta( $to_delete ) {
3438
- array_push( $to_delete, self::FB_PRODUCT_ITEM_ID );
3439
- array_push( $to_delete, self::FB_PRODUCT_GROUP_ID );
 
 
 
 
 
3440
  return $to_delete;
3441
  }
3442
 
3443
-
3444
  /**
3445
  * Helper function to update FB visibility.
3446
  *
3447
- * @param int|\WC_Product $product_id product ID or product object
3448
- * @param string $visibility visibility
3449
  */
3450
- function update_fb_visibility( $product_id, $visibility ) {
3451
-
3452
  // bail if the plugin is not configured properly
3453
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
3454
  return;
3455
  }
3456
 
3457
- $product = $product_id instanceof \WC_Product ? $product_id : wc_get_product( $product_id );
3458
 
3459
  // bail if product isn't found
3460
- if ( ! $product instanceof \WC_Product ) {
3461
  return;
3462
  }
3463
 
3464
  $should_set_visible = $visibility === self::FB_SHOP_PRODUCT_VISIBLE;
3465
-
3466
  if ( $product->is_type( 'variation' ) ) {
3467
-
3468
  Products::set_product_visibility( $product, $should_set_visible );
3469
-
3470
- facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( array( $product->get_id() ) );
3471
-
3472
  } elseif ( $product->is_type( 'variable' ) ) {
3473
-
3474
  // parent product
3475
  Products::set_product_visibility( $product, $should_set_visible );
3476
-
3477
  // we should not add the parent product ID to the array of product IDs to be
3478
  // updated because product groups, which are used to represent the parent product
3479
  // for variable products, don't have the visibility property on Facebook
3480
- $product_ids = array();
3481
-
3482
  // set visibility for all children
3483
  foreach ( $product->get_children() as $index => $id ) {
3484
-
3485
  $product = wc_get_product( $id );
3486
-
3487
- if ( ! $product instanceof \WC_Product ) {
3488
  continue;
3489
  }
3490
-
3491
  Products::set_product_visibility( $product, $should_set_visible );
3492
-
3493
  $product_ids[] = $product->get_id();
3494
  }
3495
-
3496
  // sync product with all variations
3497
- facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( $product_ids );
3498
-
3499
  } else {
3500
-
3501
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $product_id );
3502
-
3503
  if ( ! $fb_product_item_id ) {
3504
- \WC_Facebookcommerce_Utils::fblog( $fb_product_item_id . " doesn't exist but underwent a visibility transform.", array(), true );
3505
  return;
3506
  }
3507
-
3508
- $set_visibility = $this->fbgraph->update_product_item( $fb_product_item_id, array( 'visibility' => $visibility ) );
3509
-
3510
- if ( $this->check_api_result( $set_visibility ) ) {
3511
  Products::set_product_visibility( $product, $should_set_visible );
3512
  }
3513
  }
3514
  }
3515
 
3516
-
3517
  /**
3518
  * Sync product upon quick or bulk edit save action.
3519
  *
@@ -3522,7 +2809,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3522
  * @param \WC_Product $product product object
3523
  */
3524
  public function on_quick_and_bulk_edit_save( $product ) {
3525
-
3526
  // bail if not a product or product is not enabled for sync
3527
  if ( ! $product instanceof \WC_Product || ! Products::published_product_should_be_synced( $product ) ) {
3528
  return;
@@ -3541,118 +2827,70 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3541
  }
3542
  }
3543
 
3544
-
3545
  /**
3546
  * Gets Facebook product ID from meta or from Facebook API.
3547
  *
3548
  * @param string $fbid_type ID type (group or item)
3549
  * @param int $wp_id post ID
3550
  * @param WC_Facebook_Product|null $woo_product product
3551
- * @return mixed|void|null
3552
  */
3553
- public function get_product_fbid( $fbid_type, $wp_id, $woo_product = null ) {
3554
-
3555
- $fb_id = WC_Facebookcommerce_Utils::get_fbid_post_meta(
3556
- $wp_id,
3557
- $fbid_type
3558
- );
3559
-
3560
  if ( $fb_id ) {
3561
  return $fb_id;
3562
  }
3563
-
3564
  if ( ! $woo_product ) {
3565
  $woo_product = new WC_Facebook_Product( $wp_id );
3566
  }
3567
-
3568
  $products = WC_Facebookcommerce_Utils::get_product_array( $woo_product );
3569
-
3570
  // if the product with ID equal to $wp_id is variable, $woo_product will be the first child
3571
  $woo_product = new WC_Facebook_Product( current( $products ) );
3572
 
3573
  $fb_retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
3574
 
3575
- $product_fbid_result = $this->fbgraph->get_facebook_id(
3576
- $this->get_product_catalog_id(),
3577
- $fb_retailer_id
3578
- );
3579
-
3580
- if ( is_wp_error( $product_fbid_result ) ) {
3581
 
3582
- WC_Facebookcommerce_Utils::log( $product_fbid_result->get_error_message() );
 
 
 
 
3583
 
 
 
 
 
 
3584
  $this->display_error_message(
3585
  sprintf(
3586
  /* translators: Placeholders %1$s - original error message from Facebook API */
3587
  esc_html__( 'There was an issue connecting to the Facebook API: %s', 'facebook-for-woocommerce' ),
3588
- $product_fbid_result->get_error_message()
3589
  )
3590
  );
3591
-
3592
- return;
3593
  }
3594
 
3595
- if ( $product_fbid_result && isset( $product_fbid_result['body'] ) ) {
3596
-
3597
- $body = WC_Facebookcommerce_Utils::decode_json( $product_fbid_result['body'] );
3598
-
3599
- if ( ! empty( $body->id ) ) {
3600
-
3601
- if ( $fbid_type == self::FB_PRODUCT_GROUP_ID ) {
3602
- $fb_id = $body->product_group->id;
3603
- } else {
3604
- $fb_id = $body->id;
3605
- }
3606
-
3607
- update_post_meta(
3608
- $wp_id,
3609
- $fbid_type,
3610
- $fb_id
3611
- );
3612
-
3613
- return $fb_id;
3614
- }
3615
- }
3616
-
3617
- return;
3618
- }
3619
-
3620
-
3621
- private function set_default_variant( $product_group_id, $product_item_id ) {
3622
- $result = $this->check_api_result(
3623
- $this->fbgraph->set_default_variant(
3624
- $product_group_id,
3625
- array( 'default_product_id' => $product_item_id )
3626
- )
3627
- );
3628
- if ( ! $result ) {
3629
- WC_Facebookcommerce_Utils::fblog(
3630
- 'Fail to set default product item',
3631
- array(),
3632
- true
3633
- );
3634
- }
3635
- }
3636
-
3637
- private function fb_wp_die() {
3638
- if ( ! $this->test_mode ) {
3639
- wp_die();
3640
- }
3641
  }
3642
 
3643
  /**
3644
  * Display test result.
3645
  **/
3646
- function ajax_display_test_result() {
3647
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'test result', true );
3648
  check_ajax_referer( 'wc_facebook_settings_jsx' );
3649
- $response = array(
3650
  'pass' => 'true',
3651
- );
3652
  $test_pass = get_option( 'fb_test_pass', null );
3653
  if ( ! isset( $test_pass ) ) {
3654
  $response['pass'] = 'in progress';
3655
- } elseif ( $test_pass == 0 ) {
3656
  $response['pass'] = 'false';
3657
  $response['debug_info'] = get_transient( 'facebook_plugin_test_fail' );
3658
  $response['stack_trace'] =
@@ -3667,23 +2905,4 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3667
  wp_die();
3668
  }
3669
 
3670
- /** Deprecated methods ********************************************************************************************/
3671
-
3672
-
3673
- /**
3674
- * Enables product sync delay notice when a post is moved to the trash.
3675
- *
3676
- * @internal
3677
- *
3678
- * @since 1.11.0
3679
- * @deprecated 2.0.0
3680
- *
3681
- * @param int $post_id the post ID
3682
- */
3683
- public function on_product_trash( $post_id ) {
3684
-
3685
- wc_deprecated_function( __METHOD__, '2.0.0' );
3686
- }
3687
-
3688
-
3689
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ use WooCommerce\Facebook\Admin;
13
+ use WooCommerce\Facebook\API;
14
+ use WooCommerce\Facebook\Events\AAMSettings;
15
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
16
+ use WooCommerce\Facebook\Framework\Helper;
17
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
18
+ use WooCommerce\Facebook\Handlers\Connection;
19
+ use WooCommerce\Facebook\Products;
20
+ use WooCommerce\Facebook\Products\Feed;
21
+
22
+ defined( 'ABSPATH' ) || exit;
23
 
24
  require_once 'facebook-config-warmer.php';
25
  require_once 'includes/fbproduct.php';
141
  /** @var array the page name and url */
142
  private $page;
143
 
 
 
 
144
  /** Legacy properties *********************************************************************************************/
145
 
146
 
147
  // TODO probably some of these meta keys need to be moved to Facebook\Products {FN 2020-01-13}.
148
+ public const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
149
+ public const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
150
+ public const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
151
 
152
  /** @var string the API flag to set a product as visible in the Facebook shop */
153
+ public const FB_SHOP_PRODUCT_VISIBLE = 'published';
154
 
155
  /** @var string the API flag to set a product as not visible in the Facebook shop */
156
+ public const FB_SHOP_PRODUCT_HIDDEN = 'hidden';
157
 
158
  /** @var string @deprecated */
159
+ public const FB_CART_URL = 'fb_cart_url';
160
 
161
+ public const FB_MESSAGE_DISPLAY_TIME = 180;
162
 
163
  // Number of days to query tip.
164
+ public const FB_TIP_QUERY = 1;
165
 
166
  // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}.
167
+ public const FB_VARIANT_IMAGE = 'fb_image';
168
 
169
+ public const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
170
 
171
+ public const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress';
172
+ public const FB_SYNC_REMAINING = 'fb_sync_remaining';
173
+ public const FB_SYNC_TIMEOUT = 30;
174
+ public const FB_PRIORITY_MID = 9;
175
 
176
+ /**
177
+ * Facebook exception test mode switch.
178
+ *
179
+ * @var bool
180
+ */
181
  private $test_mode = false;
182
 
183
+ /** @var WC_Facebookcommerce */
184
+ private $facebook_for_woocommerce;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  /**
187
  * Init and hook in the integration.
188
  *
189
+ * @param WC_Facebookcommerce $facebook_for_woocommerce
190
  * @return void
191
  */
192
+ public function __construct( WC_Facebookcommerce $facebook_for_woocommerce ) {
193
+ $this->facebook_for_woocommerce = $facebook_for_woocommerce;
194
+
195
  if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
196
  include_once 'facebook-commerce-events-tracker.php';
197
  }
224
  }
225
 
226
  // For now, the values of use s2s and access token will be the ones returned from WC_Facebookcommerce_Pixel.
227
+ $this->settings[ self::SETTING_USE_S2S ] = WC_Facebookcommerce_Pixel::get_use_s2s();
228
+ $this->settings[ self::SETTING_ACCESS_TOKEN ] = WC_Facebookcommerce_Pixel::get_access_token();
 
 
 
 
 
 
 
229
 
230
  WC_Facebookcommerce_Utils::$ems = $this->get_external_merchant_settings_id();
231
 
 
 
 
 
 
 
 
232
  if ( is_admin() ) {
233
 
234
  $this->init_pixel();
252
  if ( ! class_exists( 'WC_Facebookcommerce_Info_Banner' ) ) {
253
  include_once 'includes/fbinfobanner.php';
254
  }
255
+ WC_Facebookcommerce_Info_Banner::get_instance( $this->get_external_merchant_settings_id(), $should_query_tip );
 
 
 
 
256
  }
257
  }
258
 
 
 
 
 
 
 
259
  if ( ! $this->get_pixel_install_time() && $this->get_facebook_pixel_id() ) {
260
  $this->update_pixel_install_time( time() );
261
  }
262
 
263
+ add_action( 'admin_notices', [ $this, 'checks' ] );
264
 
265
+ add_action( 'admin_enqueue_scripts', [ $this, 'load_assets' ] );
266
 
267
  add_action(
268
  'wp_ajax_ajax_sync_all_fb_products',
269
+ [ $this, 'ajax_sync_all_fb_products' ],
270
  self::FB_PRIORITY_MID
271
  );
272
 
273
  add_action(
274
  'wp_ajax_ajax_check_feed_upload_status',
275
+ [ $this, 'ajax_check_feed_upload_status' ],
276
  self::FB_PRIORITY_MID
277
  );
278
 
279
  add_action(
280
  'wp_ajax_ajax_reset_all_fb_products',
281
+ [ $this, 'ajax_reset_all_fb_products' ],
282
  self::FB_PRIORITY_MID
283
  );
284
  add_action(
285
  'wp_ajax_ajax_display_test_result',
286
+ [ $this, 'ajax_display_test_result' ]
287
  );
288
 
289
  // Don't duplicate product FBID meta.
290
+ add_filter( 'woocommerce_duplicate_product_exclude_meta', [ $this, 'fb_duplicate_product_reset_meta' ] );
291
 
292
  // Add product processing hooks if the plugin is configured only.
293
  if ( $this->is_configured() && $this->get_product_catalog_id() ) {
294
 
295
  // On_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information.
296
+ add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 40 );
297
 
298
  add_action(
299
  'woocommerce_product_quick_edit_save',
300
+ [ $this, 'on_quick_and_bulk_edit_save' ]
301
  );
302
 
303
  add_action(
304
  'woocommerce_product_bulk_edit_save',
305
+ [ $this, 'on_quick_and_bulk_edit_save' ]
306
  );
307
 
308
+ add_action( 'before_delete_post', [ $this, 'on_product_delete' ] );
309
 
310
  // Ensure product is deleted from FB when moved to trash.
311
+ add_action( 'wp_trash_post', [ $this, 'on_product_delete' ] );
312
 
313
+ add_action( 'add_meta_boxes', 'WooCommerce\Facebook\Admin\Product_Sync_Meta_Box::register', 10, 1 );
314
 
315
  add_action(
316
  'wp_ajax_ajax_fb_toggle_visibility',
343
  $this->remove_sticky_message();
344
  }
345
  }
 
346
  $this->load_background_sync_process();
347
  }
348
 
 
349
  if ( $this->get_facebook_pixel_id() ) {
350
  $aam_settings = $this->load_aam_settings_of_pixel();
351
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
368
  3
369
  );
370
 
371
+ add_action( 'untrashed_post', [ $this, 'fb_restore_untrashed_variable_product' ] );
372
 
373
  // Product Set hooks.
374
+ add_action( 'fb_wc_product_set_sync', [ $this, 'create_or_update_product_set_item' ], 99, 2 );
375
+ add_action( 'fb_wc_product_set_delete', [ $this, 'delete_product_set_item' ], 99 );
376
+ }
377
+
378
+ /**
379
+ * Initialises Facebook Pixel and its settings.
380
+ *
381
+ * @return bool
382
+ */
383
+ public function init_pixel() {
384
+ /* Not sure this one is needed. Config warmer is never written. */
385
+ WC_Facebookcommerce_Pixel::initialize();
386
+
387
+ /**
388
+ * Migrate WC customer pixel_id from WC settings to WP options.
389
+ * This is part of a larger effort to consolidate all the FB-specific
390
+ * settings for all plugin integrations.
391
+ */
392
+ if ( is_admin() ) {
393
+ $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
394
+ $settings_pixel_id = $this->get_facebook_pixel_id();
395
+ if (
396
+ WC_Facebookcommerce_Utils::is_valid_id( $settings_pixel_id ) &&
397
+ ( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) ||
398
+ $pixel_id != $settings_pixel_id
399
+ )
400
+ ) {
401
+ WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
402
+ }
403
+ /**
404
+ * Migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
405
+ * so that it works the same way the pixel ID does
406
+ */
407
+ $settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
408
+ WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
409
+
410
+ $settings_use_s2s = WC_Facebookcommerce_Pixel::get_use_s2s();
411
+ WC_Facebookcommerce_Pixel::set_use_s2s( $settings_use_s2s );
412
+
413
+ $settings_access_token = WC_Facebookcommerce_Pixel::get_access_token();
414
+ WC_Facebookcommerce_Pixel::set_access_token( $settings_access_token );
415
+
416
+ return true;
417
+ }
418
+ return false;
419
  }
420
 
421
  /**
443
  // installed pixel
444
  // because the admin could have changed the connection to Facebook
445
  // during the refresh interval.
446
+ if ( $cached_aam_settings->get_pixel_id() === $installed_pixel ) {
447
  $aam_settings = $cached_aam_settings;
448
  }
449
  }
459
  return $aam_settings;
460
  }
461
 
462
+ /**
463
+ * Init background process.
464
+ *
465
+ * @return void
466
+ */
467
  public function load_background_sync_process() {
468
  // Attempt to load background processing (Woo 3.x.x only).
469
  include_once 'includes/fbbackground.php';
470
  if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
471
  if ( ! isset( $this->background_processor ) ) {
472
+ $this->background_processor = new WC_Facebookcommerce_Background_Process( $this );
 
473
  }
474
  }
475
  add_action(
476
  'wp_ajax_ajax_fb_background_check_queue',
477
+ [ $this, 'ajax_fb_background_check_queue' ]
478
  );
479
  }
480
 
481
+ /**
482
+ * Ajax background check handler.
483
+ *
484
+ * @return void
485
+ */
486
  public function ajax_fb_background_check_queue() {
487
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'background check queue', true );
488
  check_ajax_referer( 'wc_facebook_settings_jsx' );
490
  if ( isset( $_POST['request_time'] ) ) {
491
  $request_time = esc_js( sanitize_text_field( wp_unslash( $_POST['request_time'] ) ) );
492
  }
493
+ if ( $this->facebook_for_woocommerce->get_connection_handler()->get_access_token() ) {
 
 
494
  if ( isset( $this->background_processor ) ) {
495
  $is_processing = $this->background_processor->handle_cron_healthcheck();
496
  $remaining = $this->background_processor->get_item_count();
497
+ $response = [
498
  'connected' => true,
499
  'background' => true,
500
  'processing' => $is_processing,
501
  'remaining' => $remaining,
502
  'request_time' => $request_time,
503
+ ];
504
  } else {
505
+ $response = [
506
  'connected' => true,
507
  'background' => false,
508
+ ];
509
  }
510
  } else {
511
+ $response = [
512
  'connected' => false,
513
  'background' => false,
514
+ ];
515
  }
 
516
  printf( json_encode( $response ) );
517
  wp_die();
518
  }
519
 
520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  /**
522
  * Gets a list of Product Item IDs indexed by the ID of the variation.
523
  *
524
  * @since 2.0.0
525
  *
526
+ * @param WC_Facebook_Product|WC_Product $product product
527
+ * @param string $product_group_id product group ID
528
  * @return array
529
  */
530
  public function get_variation_product_item_ids( $product, $product_group_id ) {
531
+ $product_item_ids_by_variation_id = [];
532
+ $missing_product_item_ids = [];
 
533
 
534
  // get the product item IDs from meta data and build a list of variations that don't have a product item ID stored
535
  foreach ( $product->get_children() as $variation_id ) {
 
536
  if ( $variation = wc_get_product( $variation_id ) ) {
 
537
  if ( $product_item_id = $variation->get_meta( self::FB_PRODUCT_ITEM_ID ) ) {
 
538
  $product_item_ids_by_variation_id[ $variation_id ] = $product_item_id;
 
539
  } else {
 
540
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation );
 
541
  $missing_product_item_ids[ $retailer_id ] = $variation;
 
542
  $product_item_ids_by_variation_id[ $variation_id ] = null;
543
  }
544
  }
545
  }
 
546
  // use the Graph API to try to find and store the product item IDs for variations that don't have a value yet
547
  if ( $missing_product_item_ids ) {
 
548
  $product_item_ids = $this->find_variation_product_item_ids( $product_group_id );
 
549
  foreach ( $missing_product_item_ids as $retailer_id => $variation ) {
 
550
  if ( isset( $product_item_ids[ $retailer_id ] ) ) {
 
551
  $variation->update_meta_data( self::FB_PRODUCT_ITEM_ID, $product_item_ids[ $retailer_id ] );
552
  $variation->save_meta_data();
 
553
  $product_item_ids_by_variation_id[ $variation->get_id() ] = $product_item_ids[ $retailer_id ];
554
  }
555
  }
561
 
562
  /**
563
  * Uses the Graph API to return a list of Product Item IDs indexed by the variation's retailer ID.
564
+ * Returns a map of pairs
565
+ * e.g.
566
+ * (
567
+ * `woo-vneck-tee-blue_28` -> `7344216055651160`,
568
+ * `woo-vneck-tee-red_26` -> `5102436146508829`
569
+ * )
570
  *
571
  * @param string $product_group_id product group ID
572
+ * @return array a map of ( `retailer id` -> `id` ) pairs.
573
  */
574
+ private function find_variation_product_item_ids( string $product_group_id ): array {
575
+ $product_item_ids = [];
 
 
576
  try {
577
+ $response = $this->facebook_for_woocommerce->get_api()->get_product_group_products( $product_group_id );
 
 
578
  do {
 
579
  $product_item_ids = array_merge( $product_item_ids, $response->get_ids() );
 
580
  // get up to two additional pages of results
581
+ } while ( $response = $this->facebook_for_woocommerce->get_api()->next( $response, 2 ) );
582
+ } catch ( ApiException $e ) {
 
 
583
  $message = sprintf( 'There was an error trying to find the IDs for Product Items in the Product Group %s: %s', $product_group_id, $e->getMessage() );
 
584
  facebook_for_woocommerce()->log( $message );
585
  }
 
586
  return $product_item_ids;
587
  }
588
 
605
  * @since 2.6.1
606
  */
607
  public function allow_full_batch_api_sync() {
 
608
  /**
609
  * Block the full batch API sync.
610
  *
641
  */
642
  return apply_filters_deprecated(
643
  'facebook_for_woocommerce_allow_full_batch_api_sync',
644
+ [
645
  $default_allow_sync,
646
  $this->get_product_count(),
647
+ ],
648
  '2.6.10',
649
  'facebook_for_woocommerce_block_full_batch_api_sync'
650
  );
651
  }
652
 
 
653
  /**
654
  * Load DIA specific JS Data
655
  */
656
  public function load_assets() {
657
+ $ajax_data = [
 
658
  'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
659
+ ];
660
  // load banner assets
661
  wp_enqueue_script(
662
  'wc_facebook_infobanner_jsx',
663
+ $this->facebook_for_woocommerce->get_asset_build_dir_url() . '/admin/infobanner.js',
664
+ [],
665
  \WC_Facebookcommerce::PLUGIN_VERSION
666
  );
667
+ wp_localize_script( 'wc_facebook_infobanner_jsx', 'wc_facebook_infobanner_jsx', $ajax_data );
 
 
 
 
 
668
  wp_enqueue_style(
669
  'wc_facebook_infobanner_css',
670
  plugins_url(
671
  '/assets/css/facebook-infobanner.css',
672
  __FILE__
673
  ),
674
+ [],
675
  \WC_Facebookcommerce::PLUGIN_VERSION
676
  );
677
 
678
+ if ( ! $this->facebook_for_woocommerce->is_plugin_settings() ) {
679
  return;
680
  }
681
 
682
  ?>
683
+ <script>
684
+ window.facebookAdsToolboxConfig = {
685
+ hasGzipSupport: '<?php echo extension_loaded( 'zlib' ) ? 'true' : 'false'; ?>',
686
+ enabledPlugins: ['MESSENGER_CHAT','INSTAGRAM_SHOP', 'PAGE_SHOP'],
687
+ enableSubscription: '<?php echo class_exists( 'WC_Subscriptions' ) ? 'true' : 'false'; ?>',
688
+ popupOrigin: '<?php echo isset( $_GET['url'] ) ? esc_js( sanitize_text_field( wp_unslash( $_GET['url'] ) ) ) : 'https://www.facebook.com/'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>',
689
+ feedWasDisabled: 'true',
690
+ platform: 'WooCommerce',
691
+ pixel: {
692
+ pixelId: '<?php echo $this->get_facebook_pixel_id() ? esc_js( $this->get_facebook_pixel_id() ) : ''; ?>',
693
+ advanced_matching_supported: true
694
+ },
695
+ diaSettingId: '<?php echo $this->get_external_merchant_settings_id() ? esc_js( $this->get_external_merchant_settings_id() ) : ''; ?>',
696
+ store: {
697
+ baseUrl: window.location.protocol + '//' + window.location.host,
698
+ baseCurrency:'<?php echo esc_js( WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); ?>',
699
+ timezoneId: '<?php echo esc_js( date( 'Z' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date ?>',
700
+ storeName: '<?php echo esc_js( WC_Facebookcommerce_Utils::get_store_name() ); ?>',
701
+ version: '<?php echo esc_js( WC()->version ); ?>',
702
+ php_version: '<?php echo PHP_VERSION; ?>',
703
+ plugin_version: '<?php echo esc_js( WC_Facebookcommerce_Utils::PLUGIN_VERSION ); ?>'
704
+ },
705
+ feed: {
706
+ totalVisibleProducts: '<?php echo esc_js( $this->get_product_count() ); ?>',
707
+ hasClientSideFeedUpload: '<?php echo esc_js( ! ! $this->get_feed_id() ); ?>',
708
+ enabled: true,
709
+ format: 'csv'
710
+ },
711
+ feedPrepared: {
712
+ feedUrl: '<?php echo esc_url_raw( Feed::get_feed_data_url() ); ?>',
713
+ feedPingUrl: '',
714
+ feedMigrated: <?php echo $this->is_feed_migrated() ? 'true' : 'false'; ?>,
715
+ samples: <?php echo $this->get_sample_product_feed(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
716
+ },
717
+ };
718
+ </script>
 
 
 
719
  <?php
720
+ $ajax_data = [
721
  'nonce' => wp_create_nonce( 'wc_facebook_settings_jsx' ),
722
+ ];
723
  wp_localize_script(
724
  'wc_facebook_settings_jsx',
725
  'wc_facebook_settings_jsx',
731
  '/assets/css/facebook.css',
732
  __FILE__
733
  ),
734
+ [],
735
  \WC_Facebookcommerce::PLUGIN_VERSION
736
  );
737
  }
738
 
 
739
  /**
740
  * Gets the IDs of products marked for deletion from Facebook when removed from Sync.
741
  *
746
  * @return array
747
  */
748
  private function get_removed_from_sync_products_to_delete() {
749
+ $posted_products = Helper::get_posted_value( WC_Facebook_Product::FB_REMOVE_FROM_SYNC );
 
750
  if ( empty( $posted_products ) ) {
751
+ return [];
752
  }
 
753
  return array_map( 'absint', explode( ',', $posted_products ) );
754
  }
755
 
 
756
  /**
757
  * Checks the product type and calls the corresponding on publish method.
758
  *
762
  *
763
  * @param int $wp_id post ID
764
  */
765
+ public function on_product_save( int $wp_id ) {
 
766
  $product = wc_get_product( $wp_id );
 
767
  if ( ! $product ) {
768
  return;
769
  }
770
+ // phpcs:disable WordPress.Security.NonceVerification.Missing
771
+ $sync_mode = isset( $_POST['wc_facebook_sync_mode'] )
772
+ ? sanitize_text_field( wp_unslash( $_POST['wc_facebook_sync_mode'] ) )
773
+ : Admin::SYNC_MODE_SYNC_DISABLED;
774
+ // phpcs:enable WordPress.Security.NonceVerification.Missing
775
  $sync_enabled = Admin::SYNC_MODE_SYNC_DISABLED !== $sync_mode;
776
 
777
  if ( Admin::SYNC_MODE_SYNC_AND_SHOW === $sync_mode && $product->is_virtual() && 'bundle' !== $product->get_type() ) {
778
  // force to Sync and hide
779
  $sync_mode = Admin::SYNC_MODE_SYNC_AND_HIDE;
780
  }
 
781
  $products_to_delete_from_facebook = $this->get_removed_from_sync_products_to_delete();
 
782
  if ( $product->is_type( 'variable' ) ) {
 
783
  // check variations for deletion
784
  foreach ( $products_to_delete_from_facebook as $delete_product_id ) {
 
785
  $delete_product = wc_get_product( $delete_product_id );
 
786
  if ( empty( $delete_product ) ) {
787
  continue;
788
  }
 
789
  if ( Products::is_sync_enabled_for_product( $delete_product ) ) {
790
  continue;
791
  }
 
792
  $this->delete_fb_product( $delete_product );
793
  }
794
  } else {
 
795
  if ( $sync_enabled ) {
796
+ Products::enable_sync_for_products( [ $product ] );
 
797
  Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
 
798
  $this->save_product_settings( $product );
 
799
  } else {
 
800
  // if previously enabled, add a notice on the next page load
801
  if ( Products::is_sync_enabled_for_product( $product ) ) {
802
  Admin::add_product_disabled_sync_notice();
803
  }
804
+ Products::disable_sync_for_products( [ $product ] );
 
 
805
  if ( in_array( $wp_id, $products_to_delete_from_facebook, true ) ) {
 
806
  $this->delete_fb_product( $product );
807
  }
808
  }
809
  }
 
810
  if ( $sync_enabled ) {
 
811
  Admin\Products::save_commerce_fields( $product );
 
812
  switch ( $product->get_type() ) {
 
813
  case 'simple':
814
  case 'booking':
815
  case 'external':
816
  case 'composite':
817
  $this->on_simple_product_publish( $wp_id );
818
  break;
 
819
  case 'variable':
820
  $this->on_variable_product_publish( $wp_id );
821
  break;
 
822
  case 'subscription':
823
  case 'variable-subscription':
824
  case 'bundle':
828
  }
829
  }
830
 
 
831
  /**
832
  * Saves the submitted Facebook settings for a product.
833
  *
835
  *
836
  * @param \WC_Product $product the product object
837
  */
838
+ private function save_product_settings( WC_Product $product ) {
 
839
  $woo_product = new WC_Facebook_Product( $product->get_id() );
840
 
841
  // phpcs:disable WordPress.Security.NonceVerification.Missing
858
  // phpcs:enable WordPress.Security.NonceVerification.Missing
859
  }
860
 
 
861
  /**
862
  * Deletes a product from Facebook.
863
  *
864
  * @param int $product_id product ID
865
  */
866
+ public function on_product_delete( int $product_id ) {
 
867
  $product = wc_get_product( $product_id );
868
 
869
  // bail if product does not exist
870
+ if ( ! $product instanceof WC_Product ) {
871
  return;
872
  }
873
 
878
  *
879
  * @see ajax_delete_fb_product()
880
  */
881
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
882
  if ( ( ! wp_doing_ajax() || ! isset( $_POST['action'] ) || 'ajax_delete_fb_product' !== $_POST['action'] )
883
  && ! Products::published_product_should_be_synced( $product ) && ! $product->is_type( 'variable' ) ) {
 
884
  return;
885
  }
886
 
887
  $this->delete_fb_product( $product );
888
  }
889
 
 
890
  /**
891
  * Deletes Facebook product.
892
  *
901
  $product_id = $product->get_id();
902
 
903
  if ( $product->is_type( 'variation' ) ) {
 
904
  $retailer_id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
 
905
  // enqueue variation to be deleted in the background
906
+ $this->facebook_for_woocommerce->get_products_sync_handler()->delete_products( [ $retailer_id ] );
 
907
  } elseif ( $product->is_type( 'variable' ) ) {
908
+ $retailer_ids = [];
 
 
909
  foreach ( $product->get_children() as $variation_id ) {
 
910
  $variation = wc_get_product( $variation_id );
 
911
  if ( $variation instanceof \WC_Product ) {
912
  $retailer_ids[] = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $variation );
913
  }
914
  delete_post_meta( $variation_id, self::FB_PRODUCT_ITEM_ID );
915
  }
 
916
  // enqueue variations to be deleted in the background
917
+ $this->facebook_for_woocommerce->get_products_sync_handler()->delete_products( $retailer_ids );
 
918
  $this->delete_product_group( $product_id );
919
  } else {
920
 
925
  // clear out both item and group IDs
926
  delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
927
  delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
 
928
  }
929
 
 
930
  /**
931
  * Updates Facebook Visibility upon trashing and restore.
932
  *
937
  * @param \WP_post $post
938
  */
939
  public function fb_change_product_published_status( $new_status, $old_status, $post ) {
 
940
  if ( ! $post ) {
941
  return;
942
  }
947
 
948
  $product = wc_get_product( $post->ID );
949
 
950
+ // Bail if we couldn't retrieve a valid product object or the product isn't enabled for sync
951
  //
952
  // Note that while moving a variable product to the trash, this method is called for each one of the
953
  // variations before it gets called with the variable product. As a result, Products::product_should_be_synced()
980
  *
981
  * @param int $post_id
982
  */
983
+ public function fb_restore_untrashed_variable_product( $post_id ) {
984
  $product = wc_get_product( $post_id );
985
 
986
+ if ( ! $product instanceof \WC_Product ) {
987
  return;
988
  }
989
 
1014
  * @return bool
1015
  */
1016
  private function should_update_visibility_for_product_status_change( $new_status, $old_status ) {
 
1017
  return ( $old_status === 'publish' && $new_status !== 'publish' ) || ( $old_status === 'trash' && $new_status === 'publish' ) || ( $old_status === 'future' && $new_status === 'publish' );
1018
  }
1019
 
 
1020
  /**
1021
  * Generic function for use with any product publishing.
1022
  *
1026
  * @param int $product_id product ID
1027
  */
1028
  public function on_product_publish( $product_id ) {
 
1029
  // bail if the plugin is not configured properly
1030
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
1031
  return;
1040
  }
1041
  }
1042
 
 
1043
  /**
1044
  * If the user has opt-in to remove products that are out of stock,
1045
  * this function will delete the product from FB Page as well.
1046
+ *
1047
+ * @param int $wp_id
1048
+ * @param WC_Product $woo_product
1049
+ *
1050
+ * @return bool
1051
  */
1052
+ public function delete_on_out_of_stock( int $wp_id, WC_Product $woo_product ): bool {
 
1053
  if ( Products::product_should_be_deleted( $woo_product ) ) {
1054
  $product = wc_get_product( $wp_id );
1055
  $this->delete_fb_product( $product );
1056
  return true;
1057
  }
 
1058
  return false;
1059
  }
1060
 
 
1061
  /**
1062
  * Syncs product to Facebook when saving a variable product.
1063
  *
1064
  * @param int $wp_id product post ID
1065
  * @param WC_Facebook_Product|null $woo_product product object
1066
  */
1067
+ public function on_variable_product_publish( $wp_id, $woo_product = null ) {
 
1068
  if ( ! $woo_product instanceof \WC_Facebook_Product ) {
1069
  $woo_product = new \WC_Facebook_Product( $wp_id );
1070
  }
1077
  return;
1078
  }
1079
 
1080
+ // Check if product group has been published to FB. If not, it's new.
1081
  // If yes, loop through variants and see if product items are published.
1082
  $fb_product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $wp_id, $woo_product );
 
1083
  if ( $fb_product_group_id ) {
 
1084
  $woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
 
1085
  $this->update_product_group( $woo_product );
 
1086
  } else {
 
1087
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product->woo_product );
 
1088
  $this->create_product_group( $woo_product, $retailer_id, true );
1089
  }
1090
 
1091
+ $variation_ids = [];
1092
 
1093
  // scheduled update for each variation that should be synced
1094
  foreach ( $woo_product->get_children() as $variation_id ) {
 
1095
  $variation = wc_get_product( $variation_id );
1096
+ if ( $variation instanceof WC_Product && $this->product_should_be_synced( $variation ) && ! $this->delete_on_out_of_stock( $variation_id, $variation ) ) {
 
1097
  $variation_ids[] = $variation_id;
1098
  }
1099
  }
1100
 
1101
+ $this->facebook_for_woocommerce->get_products_sync_handler()->create_or_update_products( $variation_ids );
1102
  }
1103
 
 
1104
  /**
1105
  * Syncs product to Facebook when saving a simple product.
1106
  *
1109
  * @param WC_Facebook_Product|null $parent_product parent object
1110
  * @return int|mixed|void|null
1111
  */
1112
+ public function on_simple_product_publish( $wp_id, $woo_product = null, &$parent_product = null ) {
 
1113
  if ( ! $woo_product instanceof \WC_Facebook_Product ) {
1114
  $woo_product = new \WC_Facebook_Product( $wp_id, $parent_product );
1115
  }
1127
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $wp_id, $woo_product );
1128
 
1129
  if ( $fb_product_item_id ) {
 
1130
  $woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
 
1131
  $this->update_product_item( $woo_product, $fb_product_item_id );
 
1132
  return $fb_product_item_id;
 
1133
  } else {
 
1134
  // Check if this is a new product item for an existing product group
1135
  if ( $woo_product->get_parent_id() ) {
 
1136
  $fb_product_group_id = $this->get_product_fbid(
1137
  self::FB_PRODUCT_GROUP_ID,
1138
  $woo_product->get_parent_id(),
1139
  $woo_product
1140
  );
 
1141
  // New variant added
1142
  if ( $fb_product_group_id ) {
 
1143
  return $this->create_product_simple( $woo_product, $fb_product_group_id );
 
1144
  } else {
 
1145
  WC_Facebookcommerce_Utils::fblog(
1146
+ 'Wrong! simple_product_publish called without group ID for a variable product!',
1147
+ [],
 
1148
  true
1149
  );
1150
  }
1151
  } else {
 
1152
  return $this->create_product_simple( $woo_product ); // new product
1153
  }
1154
  }
1155
  }
1156
 
 
1157
  /**
1158
  * Determines whether the product with the given ID should be synced.
1159
  *
 
 
1160
  * @since 2.0.0
1161
  *
1162
+ * @param WC_Product $product product object
1163
  */
1164
+ public function product_should_be_synced( WC_Product $product ): bool {
1165
  try {
1166
+ $this->facebook_for_woocommerce->get_product_sync_validator( $product )->validate();
1167
  return true;
1168
  } catch ( \Exception $e ) {
1169
  return false;
1170
  }
1171
  }
1172
 
 
1173
  /**
1174
+ * Create product group and product, store fb-specific info.
1175
+ *
1176
+ * @param WC_Facebook_Product $woo_product
1177
+ * @param string|null $fb_product_group_id
1178
+ * @return string facebook product item id
1179
+ */
1180
+ public function create_product_simple( WC_Facebook_Product $woo_product, string $fb_product_group_id = null ): string {
1181
  $retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
1182
 
1183
  if ( ! $fb_product_group_id ) {
1184
+ $fb_product_group_id = $this->create_product_group( $woo_product, $retailer_id );
 
 
 
1185
  }
1186
 
1187
  if ( $fb_product_group_id ) {
1188
+ $fb_product_item_id = $this->create_product_item( $woo_product, $retailer_id, $fb_product_group_id );
 
 
 
 
1189
  return $fb_product_item_id;
1190
  }
1191
+ return '';
1192
  }
1193
 
1194
+ /**
1195
+ * @param WC_Facebook_Product $woo_product
1196
+ * @param string $retailer_id
1197
+ * @param bool $variants
1198
+ * @return ?string
1199
+ */
1200
+ public function create_product_group( WC_Facebook_Product $woo_product, string $retailer_id, bool $variants = false ): ?string {
1201
+ $product_group_data = [
1202
  'retailer_id' => $retailer_id,
1203
+ ];
 
1204
  if ( $variants ) {
1205
+ $product_group_data['variants'] = $woo_product->prepare_variants_for_group();
 
1206
  }
1207
 
1208
+ $create_product_group_result = $this->facebook_for_woocommerce->get_api()->create_product_group(
1209
+ $this->get_product_catalog_id(),
1210
+ $product_group_data
 
 
 
 
1211
  );
1212
 
1213
  // New variant added
1214
+ if ( $create_product_group_result->id ) {
1215
+ $fb_product_group_id = $create_product_group_result->id;
 
 
1216
  update_post_meta(
1217
  $woo_product->get_id(),
1218
  self::FB_PRODUCT_GROUP_ID,
1219
  $fb_product_group_id
1220
  );
 
 
 
 
 
 
 
 
 
1221
  return $fb_product_group_id;
1222
  }
1223
+ return null;
1224
  }
1225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1226
  /**
1227
  * Update existing product group (variant data only)
1228
  *
1229
+ * @param WC_Facebook_Product $woo_product
1230
  **/
1231
+ public function update_product_group( WC_Facebook_Product $woo_product ) {
1232
  $fb_product_group_id = $this->get_product_fbid(
1233
  self::FB_PRODUCT_GROUP_ID,
1234
  $woo_product->get_id(),
1244
  if ( ! $variants ) {
1245
  WC_Facebookcommerce_Utils::log(
1246
  sprintf(
1247
+ /* translators: %1$s is referring to facebook product group id. */
1248
  __(
1249
  'Nothing to update for product group for %1$s',
1250
  'facebook-for-woocommerce'
1255
  return;
1256
  }
1257
 
1258
+ $product_group_data = [
 
1259
  'variants' => $variants,
1260
+ ];
1261
 
1262
  // Figure out the matching default variation.
1263
  $default_product_fbid = $this->get_product_group_default_variation( $woo_product, $fb_product_group_id );
1266
  $product_group_data['default_product_id'] = $default_product_fbid;
1267
  }
1268
 
1269
+ $response = $this->facebook_for_woocommerce->get_api()->update_product_group( $fb_product_group_id, $product_group_data );
1270
+ if ( $response->success ) {
 
 
 
 
 
 
 
1271
  $this->display_success_message(
1272
  'Updated product group <a href="https://facebook.com/' .
1273
  $fb_product_group_id . '" target="_blank">' . $fb_product_group_id .
1274
  '</a> on Facebook.'
1275
  );
1276
+ } else {
1277
+ $this->display_error_message(
1278
+ 'Updating product group <a href="https://facebook.com/' .
1279
+ $fb_product_group_id . '" target="_blank">' . $fb_product_group_id .
1280
+ '</a> on Facebook has failed.'
1281
+ );
1282
  }
 
1283
  }
1284
 
1285
+ function create_product_item( $woo_product, $retailer_id, $product_group_id ): string {
1286
+ $product_data = $woo_product->prepare_product( $retailer_id );
1287
+ $product_result = $this->facebook_for_woocommerce->get_api()->create_product_item( $product_group_id, $product_data );
1288
+
1289
+ if ( $product_result->id ) {
1290
+ $fb_product_item_id = $product_result->id;
1291
+
1292
+ update_post_meta(
1293
+ $woo_product->get_id(),
1294
+ self::FB_PRODUCT_ITEM_ID,
1295
+ $fb_product_item_id
1296
+ );
1297
+
1298
+ $this->display_success_message(
1299
+ 'Created product item <a href="https://facebook.com/' .
1300
+ $fb_product_item_id . '" target="_blank">' .
1301
+ $fb_product_item_id . '</a> on Facebook.'
1302
+ );
1303
+
1304
+ return $fb_product_item_id;
1305
+ }
1306
+ return '';
1307
+ }
1308
 
1309
  /**
1310
  * Determines if there is a matching variation for the default attributes.
1315
  *
1316
  * @since 2.1.2
1317
  *
1318
+ * @param WC_Facebook_Product $woo_product
1319
+ * @param string $fb_product_group_id
1320
  * @return integer|null Facebook Catalog variation id.
1321
  */
1322
+ private function get_product_group_default_variation( WC_Facebook_Product $woo_product, string $fb_product_group_id ) {
 
1323
  $default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' );
1324
  $default_variation = null;
1325
 
1342
  );
1343
 
1344
  // Check if currently processed variation exist in the catalog.
1345
+ if (!in_array($fb_retailer_id, $existing_catalog_variations_retailer_ids)) {
1346
  continue;
1347
  }
1348
 
1349
+ $variation_attributes = $this->get_product_variation_attributes($variation);
1350
+ $variation_attributes_count = count($variation_attributes);
1351
+ $matching_attributes_count = count(array_intersect_assoc($default_attributes, $variation_attributes));
1352
 
1353
  // Check how much current variation matches the selected default attributes.
1354
  if ( $matching_attributes_count === $variation_attributes_count ) {
1355
  // We found a perfect match;
1356
  $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1357
  break;
1358
+ }
1359
+ if ( $matching_attributes_count > $best_match_count ) {
1360
  // We found a better match.
1361
  $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1362
  }
 
1363
  }
1364
  }
1365
 
1392
  * @param array $variation
1393
  * @return array
1394
  */
1395
+ private function get_product_variation_attributes( array $variation ): array {
1396
+ $final_attributes = [];
 
1397
  $variation_attributes = $variation['attributes'];
1398
 
1399
  foreach ( $variation_attributes as $attribute_name => $attribute_value ) {
1403
  return $final_attributes;
1404
  }
1405
 
 
1406
  /**
1407
+ * Update existing product.
1408
+ *
1409
+ * @param WC_Facebook_Product $woo_product
1410
+ * @param string $fb_product_item_id
1411
+ * @return void
1412
+ */
1413
+ public function update_product_item( WC_Facebook_Product $woo_product, string $fb_product_item_id ): void {
1414
  $product_data = $woo_product->prepare_product();
1415
 
1416
  // send an empty string to clear the additional_image_urls property if the product has no additional images
1418
  $product_data['additional_image_urls'] = '';
1419
  }
1420
 
1421
+ $result = $this->facebook_for_woocommerce->get_api()->update_product_item( $fb_product_item_id, $product_data );
1422
+ if ( $result->success ) {
 
 
 
 
 
 
 
1423
  $this->display_success_message(
1424
  'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
1425
  '" target="_blank">' . $fb_product_item_id . '</a> on Facebook.'
1426
  );
1427
+ } else {
1428
+ $this->display_error_message(
1429
+ 'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
1430
+ '" target="_blank">' . $fb_product_item_id . '</a> on Facebook has failed.'
1431
+ );
1432
  }
 
1433
  }
1434
 
1435
 
1442
  * @param int $product_set_id Product Set Term Id.
1443
  **/
1444
  public function create_or_update_product_set_item( $product_set_data, $product_set_id ) {
 
1445
  // check if exists in FB
1446
  $fb_product_set_id = get_term_meta( $product_set_id, self::FB_PRODUCT_SET_ID, true );
1447
 
1448
  // set data and execute API call
1449
+ $result = empty( $fb_product_set_id )
1450
+ ? $this->facebook_for_woocommerce->get_api()->create_product_set_item( $this->get_product_catalog_id(), $product_set_data )
1451
+ : $this->facebook_for_woocommerce->get_api()->update_product_set_item( $fb_product_set_id, $product_set_data );
 
 
 
 
 
 
 
 
 
 
 
1452
 
1453
  // update product set to set Facebook Product Set ID
1454
  if ( $result && empty( $fb_product_set_id ) ) {
1455
+ $fb_product_set_id = $result->id;
 
 
 
 
1456
  update_term_meta(
1457
  $product_set_id,
1458
  self::FB_PRODUCT_SET_ID,
1461
  }
1462
  }
1463
 
 
1464
  /**
1465
  * Delete product set
1466
  *
1467
+ * @param string $fb_product_set_id Facebook Product Set ID.
1468
+ * @return void
1469
+ * @throws ApiException
1470
+ * @throws \WooCommerce\Facebook\API\Exceptions\Request_Limit_Reached
 
 
 
 
 
 
 
 
 
 
1471
  */
1472
+ public function delete_product_set_item( string $fb_product_set_id ) {
1473
+ $allow_live_deletion = apply_filters( 'wc_facebook_commerce_allow_live_product_set_deletion', true, $fb_product_set_id );
1474
+ $this->facebook_for_woocommerce->get_api()->delete_product_set_item( $fb_product_set_id, $allow_live_deletion );
1475
  }
1476
 
 
 
 
 
 
 
 
 
 
1477
 
1478
  /**
1479
  * Checks the feed upload status (FBE v1.0).
1481
  * @internal
1482
  */
1483
  public function ajax_check_feed_upload_status() {
1484
+ $response = [
1485
  'connected' => true,
1486
  'status' => 'complete',
1487
+ ];
1488
  printf( json_encode( $response ) );
1489
  wp_die();
1490
  }
1491
 
 
1492
  /**
1493
  * Check Feed Upload Status (FBE v2.0)
1494
  * TODO: When migrating to FBE v2.0, remove above function and rename
1495
  * below function to ajax_check_feed_upload_status()
1496
  **/
1497
  public function ajax_check_feed_upload_status_v2() {
 
1498
  \WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
 
1499
  check_ajax_referer( 'wc_facebook_settings_jsx' );
 
1500
  if ( $this->is_configured() ) {
1501
+ $response = [
 
1502
  'connected' => true,
1503
  'status' => 'in progress',
1504
+ ];
1505
 
1506
  if ( ! empty( $this->get_upload_id() ) ) {
1507
 
1511
  include_once 'includes/fbproductfeed.php';
1512
  }
1513
 
1514
+ $this->fbproductfeed = new \WC_Facebook_Product_Feed( $this->get_product_catalog_id() );
 
 
 
1515
  }
1516
 
1517
  $status = $this->fbproductfeed->is_upload_complete( $this->settings );
1518
 
1519
  $response['status'] = $status;
 
1520
  } else {
1521
+ $response = [
 
1522
  'connected' => true,
1523
  'status' => 'error',
1524
+ ];
1525
  }
1526
 
1527
  if ( 'complete' === $response['status'] ) {
 
1528
  update_option(
1529
  $this->get_option_key(),
1530
  apply_filters(
1534
  );
1535
  }
1536
  } else {
1537
+ $response = [ 'connected' => false ];
 
1538
  }
 
1539
  printf( json_encode( $response ) );
1540
  wp_die();
1541
  }
1542
 
1543
  /**
1544
+ * Display custom success message (sugar).
1545
+ *
1546
+ * @deprecated 2.1.0
1547
+ *
1548
+ * @param string $msg
1549
+ * @return void
1550
+ */
1551
+ public function display_success_message( string $msg ): void {
1552
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1553
  set_transient(
1554
  'facebook_plugin_api_success',
1558
  }
1559
 
1560
  /**
1561
+ * Display custom info message (sugar).
1562
+ *
1563
+ * @param string $msg
1564
+ * @return void
1565
+ */
1566
+ public function display_info_message( string $msg ): void {
 
 
 
 
 
 
 
 
 
1567
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1568
  set_transient(
1569
  'facebook_plugin_api_info',
1575
  /**
1576
  * Display custom "sticky" info message.
1577
  * Call remove_sticky_message or wait for time out.
1578
+ *
1579
+ * @param string $msg
1580
+ * @return void
1581
+ */
1582
+ public function display_sticky_message( string $msg ): void {
1583
  $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1584
  set_transient(
1585
  'facebook_plugin_api_sticky',
1589
  }
1590
 
1591
  /**
1592
+ * Remove custom "sticky" info message.
1593
+ *
1594
+ * @return void
1595
+ */
1596
+ public function remove_sticky_message() {
1597
  delete_transient( 'facebook_plugin_api_sticky' );
1598
  }
1599
 
1600
+ /**
1601
+ * Remove 'resync' message.
1602
+ *
1603
+ * @return void
1604
+ */
1605
+ public function remove_resync_message() {
1606
  $msg = get_transient( 'facebook_plugin_api_sticky' );
1607
  if ( $msg && strpos( $msg, 'Sync' ) !== false ) {
1608
  delete_transient( 'facebook_plugin_resync_sticky' );
1609
  }
1610
  }
1611
 
 
1612
  /**
1613
  * Logs and stores custom error message (sugar).
1614
  *
1615
  * @param string $msg
1616
+ * @return void
1617
  */
1618
+ public function display_error_message( string $msg ): void {
 
1619
  WC_Facebookcommerce_Utils::log( $msg );
 
1620
  set_transient( 'facebook_plugin_api_error', $msg, self::FB_MESSAGE_DISPLAY_TIME );
1621
  }
1622
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1623
  /**
1624
  * Displays out of sync message if products are edited using WooCommerce Advanced Bulk Edit.
1625
  *
1626
+ * @param string $import_id
1627
+ * @return void
1628
  */
1629
+ public function ajax_woo_adv_bulk_edit_compat( string $import_id ): void {
 
1630
  if ( ! WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'adv bulk edit', false ) ) {
1631
  return;
1632
  }
1639
  }
1640
  }
1641
 
1642
+ /**
1643
+ * Display import message.
1644
+ *
1645
+ * @param string $import_id
1646
+ * @return void
1647
+ */
1648
+ public function wp_all_import_compat( string $import_id ): void {
1649
  $import = new PMXI_Import_Record();
1650
  $import->getById( $import_id );
1651
+ if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], [ 'product', 'product_variation' ], true ) ) {
1652
  $this->display_out_of_sync_message( 'import' );
1653
  }
1654
  }
1655
 
1656
+ /**
1657
+ * Displays out of sync message.
1658
+ *
1659
+ * @param string $action_name
1660
+ * @return void
1661
+ */
1662
+ public function display_out_of_sync_message( string $action_name ): void {
1663
  $this->display_sticky_message(
1664
  sprintf(
1665
  'Products may be out of Sync with Facebook due to your recent ' . $action_name . '.' .
1666
  ' <a href="%s&fb_force_resync=true&remove_sticky=true">Re-Sync them with FB.</a>',
1667
+ $this->facebook_for_woocommerce->get_settings_url()
1668
  )
1669
  );
1670
  }
1673
  * If we get a product group ID or product item ID back for a dupe retailer
1674
  * id error, update existing ID.
1675
  *
1676
+ * @param stdClass $error_data
1677
+ * @param int $wpid
1678
  * @return null
1679
  **/
1680
+ public function get_existing_fbid( stdClass $error_data, int $wpid ) {
1681
  if ( isset( $error_data->product_group_id ) ) {
1682
  update_post_meta(
1683
  $wpid,
1693
  );
1694
  return $error_data->product_item_id;
1695
  } else {
1696
+ return null;
1697
  }
1698
  }
1699
 
1701
  * Checks for API key and other API errors.
1702
  */
1703
  public function checks() {
 
1704
  // TODO improve this by checking the settings page with Framework method and ensure error notices are displayed under the Integration sections {FN 2020-01-30}
1705
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1706
  if ( isset( $_GET['page'] ) && 'wc-facebook' === $_GET['page'] ) {
1707
  $this->display_errors();
1708
  }
1710
  $this->maybe_display_facebook_api_messages();
1711
  }
1712
 
 
1713
  /**
1714
  * Gets a sample feed with up to 12 published products.
1715
  *
1716
  * @return string
1717
  */
1718
+ public function get_sample_product_feed() {
 
1719
  ob_start();
 
1720
  // get up to 12 published posts that are products
1721
+ $args = [
1722
  'post_type' => 'product',
1723
  'post_status' => 'publish',
1724
  'posts_per_page' => 12,
1725
  'fields' => 'ids',
1726
+ ];
1727
 
1728
  $post_ids = get_posts( $args );
1729
+ $items = [];
1730
 
1731
  foreach ( $post_ids as $post_id ) {
 
1732
  $woo_product = new WC_Facebook_Product( $post_id );
1733
  $product_data = $woo_product->prepare_product();
1734
 
1735
+ $feed_item = [
1736
  'title' => strip_tags( $product_data['name'] ),
1737
  'availability' => $woo_product->is_in_stock() ? 'in stock' :
1738
  'out of stock',
1739
  'description' => strip_tags( $product_data['description'] ),
1740
  'id' => $product_data['retailer_id'],
1741
  'image_link' => $product_data['image_url'],
1742
+ 'brand' => Helper::str_truncate( wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() ), 100 ),
1743
  'link' => $product_data['url'],
1744
  'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
1745
+ ];
 
1746
  array_push( $items, $feed_item );
1747
  }
 
1748
  // https://codex.wordpress.org/Function_Reference/wp_reset_postdata
1749
  wp_reset_postdata();
 
1750
  ob_end_clean();
1751
+ return json_encode( [ $items ] );
 
1752
  }
1753
 
1754
  /**
1755
  * Loop through array of WPIDs to remove metadata.
1756
+ *
1757
+ * @param array $products
1758
+ */
1759
+ public function delete_post_meta_loop( array $products ) {
1760
  foreach ( $products as $product_id ) {
1761
  delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
1762
  delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
1767
  /**
1768
  * Remove FBIDs from all products when resetting store.
1769
  **/
1770
+ public function reset_all_products() {
1771
  if ( ! is_admin() ) {
1772
  WC_Facebookcommerce_Utils::log(
1773
  'Not resetting any FBIDs from products,
1776
  return false;
1777
  }
1778
 
 
 
 
1779
  // Include draft products (omit 'post_status' => 'publish')
1780
  WC_Facebookcommerce_Utils::log( 'Removing FBIDs from all products' );
1781
 
1782
  $post_ids = get_posts(
1783
+ [
1784
  'post_type' => 'product',
1785
  'posts_per_page' => -1,
1786
  'fields' => 'ids',
1787
+ ]
1788
  );
1789
 
1790
+ $children = [];
1791
  foreach ( $post_ids as $post_id ) {
1792
  $children = array_merge(
1793
  get_posts(
1794
+ [
1795
  'post_type' => 'product_variation',
1796
  'posts_per_page' => -1,
1797
  'post_parent' => $post_id,
1798
  'fields' => 'ids',
1799
+ ]
1800
  ),
1801
  $children
1802
  );
1810
 
1811
  /**
1812
  * Remove FBIDs from a single WC product
1813
+ *
1814
+ * @param int $wp_id
1815
+ */
1816
+ public function reset_single_product( int $wp_id ) {
1817
  $woo_product = new WC_Facebook_Product( $wp_id );
1818
+ $products = [ $woo_product->get_id() ];
1819
  if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
1820
  $products = array_merge( $products, $woo_product->get_children() );
1821
  }
1825
  WC_Facebookcommerce_Utils::log( 'Deleted FB Metadata for product ' . $wp_id );
1826
  }
1827
 
1828
+ /**
1829
+ * Ajax reset all Facebook products.
1830
+ *
1831
+ * @return void
1832
+ */
1833
+ public function ajax_reset_all_fb_products() {
1834
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset products', true );
1835
  check_ajax_referer( 'wc_facebook_settings_jsx' );
1836
  $this->reset_all_products();
1838
  wp_die();
1839
  }
1840
 
1841
+ /**
1842
+ * Ajax reset single Facebook product.
1843
+ *
1844
+ * @return void
1845
+ */
1846
+ public function ajax_reset_single_fb_product() {
1847
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset single product', true );
1848
  check_ajax_referer( 'wc_facebook_metabox_jsx' );
1849
  if ( ! isset( $_POST['wp_id'] ) ) {
1860
  wp_die();
1861
  }
1862
 
1863
+ /**
1864
+ * Ajax delete Facebook product.
1865
+ *
1866
+ * @return void
1867
+ */
1868
+ public function ajax_delete_fb_product() {
1869
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'delete single product', true );
1870
  check_ajax_referer( 'wc_facebook_metabox_jsx' );
1871
  if ( ! isset( $_POST['wp_id'] ) ) {
1885
  * @internal
1886
  */
1887
  public function ajax_sync_all_fb_products() {
 
1888
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
1889
  check_ajax_referer( 'wc_facebook_settings_jsx' );
1890
 
1891
  $this->sync_facebook_products();
1892
  }
1893
 
 
1894
  /**
1895
  * Syncs Facebook products using the GraphAPI.
1896
  *
1898
  * Ends the request sending a JSON response indicating success or failure.
1899
  *
1900
  * @since 1.10.2
 
1901
  */
1902
  private function sync_facebook_products() {
 
1903
  try {
1904
  $this->sync_facebook_products_using_background_processor();
1905
  wp_send_json_success();
1906
+ } catch ( PluginException $e ) {
 
 
1907
  // Access token has expired
1908
  if ( 190 === $e->getCode() ) {
1909
  $error_message = __( 'Your connection has expired.', 'facebook-for-woocommerce' ) . ' <strong>' . __( 'Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook.', 'facebook-for-woocommerce' ) . '</strong>';
1917
  $error_message
1918
  );
1919
 
1920
+ wp_send_json_error( [ 'error' => $message ] );
1921
  }
1922
  }
1923
 
 
1924
  /**
1925
  * Syncs Facebook products using the background processor.
1926
  *
1927
  * @since 1.10.2
 
 
1928
  * @return bool
1929
+ * @throws ApiException Some comment.
1930
+ * @throws PluginException If product sync disabled.
1931
  */
1932
  private function sync_facebook_products_using_background_processor() {
 
1933
  if ( ! $this->is_product_sync_enabled() ) {
 
1934
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
1935
+ throw new PluginException( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
 
1936
  }
1937
 
1938
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
 
1939
  WC_Facebookcommerce_Utils::log( sprintf( 'Not syncing, the plugin is not configured or the Catalog ID is missing' ) );
1940
+ throw new PluginException( __( 'The plugin is not configured or the Catalog ID is missing.', 'facebook-for-woocommerce' ) );
 
1941
  }
1942
 
1943
  $this->remove_resync_message();
1944
 
1945
  $currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
 
1946
  if ( isset( $this->background_processor ) ) {
1947
  if ( $this->background_processor->is_updating() ) {
1948
  $this->background_processor->handle_cron_healthcheck();
1951
  }
1952
 
1953
  if ( $currently_syncing ) {
 
1954
  WC_Facebookcommerce_Utils::log( 'Not syncing again, sync already in progress' );
1955
  WC_Facebookcommerce_Utils::fblog(
1956
  'Tried to sync during an in-progress sync!',
1957
+ [],
1958
  true
1959
  );
1960
+ throw new PluginException( __( 'A product sync is in progress. Please wait until the sync finishes before starting a new one.', 'facebook-for-woocommerce' ) );
 
1961
  }
1962
 
1963
+ $catalog = $this->facebook_for_woocommerce->get_api()->get_catalog( $this->get_product_catalog_id() );
1964
+ if ( $catalog->id ) {
1965
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
1966
  WC_Facebookcommerce_Utils::fblog(
1967
  'Tried to sync with an invalid product catalog!',
1968
+ [],
1969
  true
1970
  );
1971
+ throw new PluginException( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
 
1972
  }
1973
 
1974
  // Get all published posts. First unsynced then already-synced.
1975
+ $post_ids_new = WC_Facebookcommerce_Utils::get_wp_posts( self::FB_PRODUCT_GROUP_ID, 'NOT EXISTS' );
1976
+ $post_ids_old = WC_Facebookcommerce_Utils::get_wp_posts( self::FB_PRODUCT_GROUP_ID, 'EXISTS' );
 
 
 
 
 
 
1977
 
1978
  $total_new = count( $post_ids_new );
1979
  $total_old = count( $post_ids_old );
1993
  'Starting background sync to Facebook: %d products...',
1994
  $total
1995
  );
1996
+ set_transient( self::FB_SYNC_IN_PROGRESS, true, self::FB_SYNC_TIMEOUT );
1997
+ set_transient( self::FB_SYNC_REMAINING, (int) $total );
 
 
 
 
 
 
 
 
 
 
1998
  $this->display_info_message( $starting_message );
1999
  WC_Facebookcommerce_Utils::log( $starting_message );
 
2000
  foreach ( $post_ids as $post_id ) {
2001
+ WC_Facebookcommerce_Utils::log( 'Pushing post to queue: ' . $post_id );
2002
+ $this->background_processor->push_to_queue( $post_id );
2003
  }
2004
 
2005
  $this->background_processor->save()->dispatch();
2006
  // reset FB_SYNC_REMAINING to avoid race condition
2007
+ set_transient( self::FB_SYNC_REMAINING, (int) $total );
 
 
 
2008
  // handle_cron_healthcheck must be called
2009
  // https://github.com/A5hleyRich/wp-background-processing/issues/34
2010
  $this->background_processor->handle_cron_healthcheck();
2013
  $count = ( $total_old === $total ) ? 0 : $total_old;
2014
  foreach ( $post_ids as $post_id ) {
2015
  // Repeatedly overwrite sync total while in actual sync loop
2016
+ set_transient( self::FB_SYNC_IN_PROGRESS, true, self::FB_SYNC_TIMEOUT );
 
 
 
 
2017
 
2018
  $this->display_sticky_message(
2019
  sprintf(
2050
  * @deprecated since 1.10.0
2051
  **/
2052
  public function ajax_fb_toggle_visibility() {
 
2053
  wc_deprecated_function( __METHOD__, '1.10.0' );
2054
  }
2055
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2056
  /** Getter methods ************************************************************************************************/
2057
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2058
  /**
2059
  * Gets the product catalog ID.
2060
  *
2063
  * @return string
2064
  */
2065
  public function get_product_catalog_id() {
 
2066
  if ( ! is_string( $this->product_catalog_id ) ) {
2067
+ $value = get_option( self::OPTION_PRODUCT_CATALOG_ID, '' );
 
 
2068
  $this->product_catalog_id = is_string( $value ) ? $value : '';
2069
  }
2070
 
2076
  * @param string $product_catalog_id Facebook product catalog ID
2077
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2078
  */
2079
+ return apply_filters( 'wc_facebook_product_catalog_id', $this->product_catalog_id, $this );
2080
  }
2081
 
 
2082
  /**
2083
  * Gets the external merchant settings ID.
2084
  *
2087
  * @return string
2088
  */
2089
  public function get_external_merchant_settings_id() {
 
2090
  if ( ! is_string( $this->external_merchant_settings_id ) ) {
2091
+ $value = get_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, '' );
 
 
2092
  $this->external_merchant_settings_id = is_string( $value ) ? $value : '';
2093
  }
2094
 
2103
  return (string) apply_filters( 'wc_facebook_external_merchant_settings_id', $this->external_merchant_settings_id, $this );
2104
  }
2105
 
 
2106
  /**
2107
  * Gets the feed ID.
2108
  *
2111
  * @return string
2112
  */
2113
  public function get_feed_id() {
 
2114
  if ( ! is_string( $this->feed_id ) ) {
2115
+ $value = get_option( self::OPTION_FEED_ID, '' );
 
 
2116
  $this->feed_id = is_string( $value ) ? $value : '';
2117
  }
2118
 
2127
  return (string) apply_filters( 'wc_facebook_feed_id', $this->feed_id, $this );
2128
  }
2129
 
 
2130
  /***
2131
  * Gets the Facebook Upload ID.
2132
  *
2135
  * @return string
2136
  */
2137
  public function get_upload_id() {
 
2138
  if ( ! is_string( $this->upload_id ) ) {
2139
+ $value = get_option( self::OPTION_UPLOAD_ID, '' );
 
 
2140
  $this->upload_id = is_string( $value ) ? $value : '';
2141
  }
2142
 
2151
  return (string) apply_filters( 'wc_facebook_upload_id', $this->upload_id, $this );
2152
  }
2153
 
 
2154
  /**
2155
  * Gets the Facebook pixel install time in UTC seconds.
2156
  *
2159
  * @return int
2160
  */
2161
  public function get_pixel_install_time() {
 
2162
  if ( ! (int) $this->pixel_install_time ) {
2163
+ $value = (int) get_option( self::OPTION_PIXEL_INSTALL_TIME, 0 );
 
 
2164
  $this->pixel_install_time = $value ?: null;
2165
  }
2166
 
2175
  return (int) apply_filters( 'wc_facebook_pixel_install_time', $this->pixel_install_time, $this );
2176
  }
2177
 
 
2178
  /**
2179
  * Gets the configured JS SDK version.
2180
  *
2183
  * @return string
2184
  */
2185
  public function get_js_sdk_version() {
 
2186
  if ( ! is_string( $this->js_sdk_version ) ) {
2187
+ $value = get_option( self::OPTION_JS_SDK_VERSION, '' );
 
 
2188
  $this->js_sdk_version = is_string( $value ) ? $value : '';
2189
  }
2190
 
2199
  return (string) apply_filters( 'wc_facebook_js_sdk_version', $this->js_sdk_version, $this );
2200
  }
2201
 
 
2202
  /**
2203
  * Gets the configured Facebook page ID.
2204
  *
2207
  * @return string
2208
  */
2209
  public function get_facebook_page_id() {
 
2210
  /**
2211
  * Filters the configured Facebook page ID.
2212
  *
2218
  return (string) apply_filters( 'wc_facebook_page_id', get_option( self::SETTING_FACEBOOK_PAGE_ID, '' ), $this );
2219
  }
2220
 
 
2221
  /**
2222
  * Gets the configured Facebook pixel ID.
2223
  *
2226
  * @return string
2227
  */
2228
  public function get_facebook_pixel_id() {
 
2229
  /**
2230
  * Filters the configured Facebook pixel ID.
2231
  *
2237
  return (string) apply_filters( 'wc_facebook_pixel_id', get_option( self::SETTING_FACEBOOK_PIXEL_ID, '' ), $this );
2238
  }
2239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2240
  /**
2241
  * Gets the IDs of the categories to be excluded from sync.
2242
  *
2245
  * @return int[]
2246
  */
2247
  public function get_excluded_product_category_ids() {
 
2248
  /**
2249
  * Filters the configured excluded product category IDs.
2250
  *
2253
  * @param int[] $category_ids the configured excluded product category IDs
2254
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2255
  */
2256
+ return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, [] ), $this );
2257
  }
2258
 
 
2259
  /**
2260
  * Gets the IDs of the tags to be excluded from sync.
2261
  *
2264
  * @return int[]
2265
  */
2266
  public function get_excluded_product_tag_ids() {
 
2267
  /**
2268
  * Filters the configured excluded product tag IDs.
2269
  *
2272
  * @param int[] $tag_ids the configured excluded product tag IDs
2273
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2274
  */
2275
+ return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, [] ), $this );
2276
  }
2277
 
 
2278
  /**
2279
  * Gets the configured product description mode.
2280
  *
2283
  * @return string
2284
  */
2285
  public function get_product_description_mode() {
 
2286
  /**
2287
  * Filters the configured product description mode.
2288
  *
2293
  */
2294
  $mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
2295
 
2296
+ $valid_modes = [
2297
  self::PRODUCT_DESCRIPTION_MODE_STANDARD,
2298
  self::PRODUCT_DESCRIPTION_MODE_SHORT,
2299
+ ];
2300
 
2301
  if ( ! in_array( $mode, $valid_modes, true ) ) {
2302
  $mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
2305
  return $mode;
2306
  }
2307
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2308
  /**
2309
  * Gets the configured Facebook messenger locale.
2310
  *
2313
  * @return string
2314
  */
2315
  public function get_messenger_locale() {
 
2316
  /**
2317
  * Filters the configured Facebook messenger locale.
2318
  *
2324
  return (string) apply_filters( 'wc_facebook_messenger_locale', get_option( self::SETTING_MESSENGER_LOCALE, 'en_US' ), $this );
2325
  }
2326
 
 
2327
  /**
2328
  * Gets the configured Facebook messenger greeting.
2329
  *
2332
  * @return string
2333
  */
2334
  public function get_messenger_greeting() {
 
2335
  /**
2336
  * Filters the configured Facebook messenger greeting.
2337
  *
2341
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2342
  */
2343
  $greeting = (string) apply_filters( 'wc_facebook_messenger_greeting', get_option( self::SETTING_MESSENGER_GREETING, __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' ) ), $this );
2344
+ return Helper::str_truncate( $greeting, $this->get_messenger_greeting_max_characters(), '' );
 
2345
  }
2346
 
 
2347
  /**
2348
  * Gets the maximum number of characters allowed in the messenger greeting.
2349
  *
2352
  * @return int
2353
  */
2354
  public function get_messenger_greeting_max_characters() {
 
2355
  $default = 80;
2356
 
2357
  /**
2367
  return $max < 1 ? $default : $max;
2368
  }
2369
 
 
2370
  /**
2371
  * Gets the configured Facebook messenger color hex.
2372
  *
2377
  * @return string
2378
  */
2379
  public function get_messenger_color_hex() {
 
2380
  /**
2381
  * Filters the configured Facebook messenger color hex.
2382
  *
2388
  return (string) apply_filters( 'wc_facebook_messenger_color_hex', get_option( self::SETTING_MESSENGER_COLOR_HEX, '#0084ff' ), $this );
2389
  }
2390
 
 
2391
  /** Setter methods ************************************************************************************************/
2392
 
2393
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2394
  /**
2395
  * Updates the Facebook product catalog ID.
2396
  *
2399
  * @param string $value product catalog ID value
2400
  */
2401
  public function update_product_catalog_id( $value ) {
 
2402
  $this->product_catalog_id = $this->sanitize_facebook_credential( $value );
2403
 
2404
  update_option( self::OPTION_PRODUCT_CATALOG_ID, $this->product_catalog_id );
2405
  }
2406
 
 
2407
  /**
2408
  * Updates the Facebook external merchant settings ID.
2409
  *
2412
  * @param string $value external merchant settings ID value
2413
  */
2414
  public function update_external_merchant_settings_id( $value ) {
 
2415
  $this->external_merchant_settings_id = $this->sanitize_facebook_credential( $value );
2416
 
2417
  update_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, $this->external_merchant_settings_id );
2418
  }
2419
 
 
2420
  /**
2421
  * Updates the Facebook feed ID.
2422
  *
2425
  * @param string $value feed ID value
2426
  */
2427
  public function update_feed_id( $value ) {
 
2428
  $this->feed_id = $this->sanitize_facebook_credential( $value );
2429
 
2430
  update_option( self::OPTION_FEED_ID, $this->feed_id );
2431
  }
2432
 
 
2433
  /**
2434
  * Updates the Facebook upload ID.
2435
  *
2438
  * @param string $value upload ID value
2439
  */
2440
  public function update_upload_id( $value ) {
 
2441
  $this->upload_id = $this->sanitize_facebook_credential( $value );
2442
 
2443
  update_option( self::OPTION_UPLOAD_ID, $this->upload_id );
2444
  }
2445
 
 
2446
  /**
2447
  * Updates the Facebook pixel install time.
2448
  *
2451
  * @param int $value pixel install time, in UTC seconds
2452
  */
2453
  public function update_pixel_install_time( $value ) {
 
2454
  $value = (int) $value;
2455
 
2456
  $this->pixel_install_time = $value ?: null;
2458
  update_option( self::OPTION_PIXEL_INSTALL_TIME, $value ?: '' );
2459
  }
2460
 
 
2461
  /**
2462
  * Updates the Facebook JS SDK version.
2463
  *
2466
  * @param string $value JS SDK version
2467
  */
2468
  public function update_js_sdk_version( $value ) {
 
2469
  $this->js_sdk_version = $this->sanitize_facebook_credential( $value );
 
2470
  update_option( self::OPTION_JS_SDK_VERSION, $this->js_sdk_version );
2471
  }
2472
 
2480
  * @return string
2481
  */
2482
  private function sanitize_facebook_credential( $value ) {
 
2483
  return wc_clean( is_string( $value ) ? $value : '' );
2484
  }
2485
 
 
 
 
 
2486
  /**
2487
  * Determines whether Facebook for WooCommerce is configured.
2488
  *
2491
  * @return bool
2492
  */
2493
  public function is_configured() {
2494
+ return $this->get_facebook_page_id() && $this->facebook_for_woocommerce->get_connection_handler()->is_connected();
 
2495
  }
2496
 
 
2497
  /**
2498
  * Determines whether advanced matching is enabled.
2499
  *
2502
  * @return bool
2503
  */
2504
  public function is_advanced_matching_enabled() {
 
2505
  /**
2506
  * Filters whether advanced matching is enabled.
2507
  *
2513
  return (bool) apply_filters( 'wc_facebook_is_advanced_matching_enabled', true, $this );
2514
  }
2515
 
 
2516
  /**
2517
  * Determines whether product sync is enabled.
2518
  *
2521
  * @return bool
2522
  */
2523
  public function is_product_sync_enabled() {
 
2524
  /**
2525
  * Filters whether product sync is enabled.
2526
  *
2549
  * @return bool
2550
  */
2551
  public function is_legacy_feed_file_generation_enabled() {
2552
+ return 'yes' === get_option( self::OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED, 'yes' );
2553
  }
2554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2555
  /**
2556
  * Determines whether the Facebook messenger is enabled.
2557
  *
2560
  * @return bool
2561
  */
2562
  public function is_messenger_enabled() {
 
2563
  /**
2564
  * Filters whether the Facebook messenger is enabled.
2565
  *
2571
  return (bool) apply_filters( 'wc_facebook_is_messenger_enabled', 'yes' === get_option( self::SETTING_ENABLE_MESSENGER ), $this );
2572
  }
2573
 
 
2574
  /**
2575
  * Determines whether debug mode is enabled.
2576
  *
2579
  * @return bool
2580
  */
2581
  public function is_debug_mode_enabled() {
 
2582
  /**
2583
  * Filters whether debug mode is enabled.
2584
  *
2607
  * They will be disabled by default. Enabling them will require setting an option in the options table.
2608
  *
2609
  * @since 2.6.6
 
2610
  */
2611
  public function are_headers_requested_for_debug() {
2612
  return (bool) get_option( self::SETTING_REQUEST_HEADERS_IN_DEBUG_MODE, false );
2613
  }
2614
 
 
2615
  /***
2616
  * Determines if the feed has been migrated from FBE 1 to FBE 1.5
2617
  *
2620
  * @return bool
2621
  */
2622
  public function is_feed_migrated() {
 
2623
  if ( ! is_bool( $this->feed_migrated ) ) {
2624
+ $value = get_option( 'wc_facebook_feed_migrated', 'no' );
 
 
2625
  $this->feed_migrated = wc_string_to_bool( $value );
2626
  }
 
2627
  return $this->feed_migrated;
2628
  }
2629
 
 
2630
  /**
2631
  * Gets message HTML.
2632
  *
2633
+ * @param string $message
2634
+ * @param string $type
2635
  * @return string
2636
  */
2637
+ private function get_message_html( string $message, string $type = 'error' ): string {
2638
  ob_start();
2639
+ $type = esc_attr( $type );
2640
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2641
+ echo <<<MESSAGE
2642
+ <div class="notice is-dismissible notice-{$type}"><p>{$message}</p></div>
2643
+ MESSAGE;
 
 
 
 
 
 
 
2644
  return ob_get_clean();
2645
  }
2646
 
 
2647
  /**
2648
  * Displays relevant messages to user from transients, clear once displayed.
2649
  */
2650
  public function maybe_display_facebook_api_messages() {
2651
+ $error_msg = get_transient( 'facebook_plugin_api_error' );
2652
+ if ( $error_msg ) {
 
2653
  $message = '<strong>' . __( 'Facebook for WooCommerce error:', 'facebook-for-woocommerce' ) . '</strong></br>' . $error_msg;
 
2654
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2655
  echo $this->get_message_html( $message );
 
2656
  delete_transient( 'facebook_plugin_api_error' );
 
2657
  WC_Facebookcommerce_Utils::fblog(
2658
  $error_msg,
2659
+ [],
2660
  true
2661
  );
2662
  }
 
2663
  $warning_msg = get_transient( 'facebook_plugin_api_warning' );
 
2664
  if ( $warning_msg ) {
 
2665
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2666
  echo $this->get_message_html( $warning_msg, 'warning' );
 
2667
  delete_transient( 'facebook_plugin_api_warning' );
2668
  }
 
2669
  $success_msg = get_transient( 'facebook_plugin_api_success' );
 
2670
  if ( $success_msg ) {
 
2671
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2672
  echo $this->get_message_html( $success_msg, 'success' );
 
2673
  delete_transient( 'facebook_plugin_api_success' );
2674
  }
 
2675
  $info_msg = get_transient( 'facebook_plugin_api_info' );
 
2676
  if ( $info_msg ) {
 
2677
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2678
  echo $this->get_message_html( $info_msg, 'info' );
 
2679
  delete_transient( 'facebook_plugin_api_info' );
2680
  }
 
2681
  $sticky_msg = get_transient( 'facebook_plugin_api_sticky' );
 
2682
  if ( $sticky_msg ) {
 
2683
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2684
  echo $this->get_message_html( $sticky_msg, 'info' );
 
2685
  // transient must be deleted elsewhere, or wait for timeout
2686
  }
2687
  }
2688
 
 
2689
  /**
2690
+ * Admin Panel Options
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2691
  */
2692
+ public function admin_options() {
2693
+ $this->facebook_for_woocommerce->get_message_handler()->show_messages();
 
2694
 
2695
+ $display = ! $this->is_configured() ? 'style="display: none"' : '';
2696
+ $settings = $this->generate_settings_html( $this->get_form_fields() );
2697
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2698
+ echo <<<HTML
2699
+ <div id="integration-settings" {$display}>
2700
+ <table class="form-table">{$settings}</table>
2701
+ </div>
2702
+ HTML;
2703
  }
2704
 
 
2705
  /**
2706
+ * Delete product item by id.
 
 
2707
  *
2708
+ * @param int $wp_id
2709
+ * @return void
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2710
  */
2711
+ public function delete_product_item( int $wp_id ): void {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2712
  $fb_product_item_id = $this->get_product_fbid(
2713
  self::FB_PRODUCT_ITEM_ID,
2714
  $wp_id
2715
  );
2716
  if ( $fb_product_item_id ) {
2717
+ $pi_result = $this->facebook_for_woocommerce->get_api()->delete_product_item( $fb_product_item_id );
 
2718
  WC_Facebookcommerce_Utils::log( $pi_result );
2719
  }
2720
  }
2721
 
 
2722
  /**
2723
  * Uses the Graph API to delete the Product Group associated with the given product.
2724
  *
2726
  *
2727
  * @param int $product_id product ID
2728
  */
2729
+ private function delete_product_group( int $product_id ) {
 
2730
  $product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $product_id );
 
2731
  if ( $product_group_id ) {
 
2732
  // TODO: replace with a call to API::delete_product_group() {WV 2020-05-26}
2733
+ $pg_result = $this->facebook_for_woocommerce->get_api()->delete_product_group( $product_group_id );
 
2734
  \WC_Facebookcommerce_Utils::log( $pg_result );
2735
  }
2736
  }
2737
 
2738
+ /**
2739
+ * Filter function for woocommerce_duplicate_product_exclude_meta filter.
2740
+ *
2741
+ * @param array $to_delete
2742
+ * @return array
2743
+ */
2744
+ public function fb_duplicate_product_reset_meta( array $to_delete ): array {
2745
+ $to_delete[] = self::FB_PRODUCT_ITEM_ID;
2746
+ $to_delete[] = self::FB_PRODUCT_GROUP_ID;
2747
  return $to_delete;
2748
  }
2749
 
 
2750
  /**
2751
  * Helper function to update FB visibility.
2752
  *
2753
+ * @param int|WC_Product $product_id product ID or product object
2754
+ * @param string $visibility visibility
2755
  */
2756
+ public function update_fb_visibility( $product_id, $visibility ) {
 
2757
  // bail if the plugin is not configured properly
2758
  if ( ! $this->is_configured() || ! $this->get_product_catalog_id() ) {
2759
  return;
2760
  }
2761
 
2762
+ $product = $product_id instanceof WC_Product ? $product_id : wc_get_product( $product_id );
2763
 
2764
  // bail if product isn't found
2765
+ if ( ! $product instanceof WC_Product ) {
2766
  return;
2767
  }
2768
 
2769
  $should_set_visible = $visibility === self::FB_SHOP_PRODUCT_VISIBLE;
 
2770
  if ( $product->is_type( 'variation' ) ) {
 
2771
  Products::set_product_visibility( $product, $should_set_visible );
2772
+ $this->facebook_for_woocommerce->get_products_sync_handler()->create_or_update_products( [ $product->get_id() ] );
 
 
2773
  } elseif ( $product->is_type( 'variable' ) ) {
 
2774
  // parent product
2775
  Products::set_product_visibility( $product, $should_set_visible );
 
2776
  // we should not add the parent product ID to the array of product IDs to be
2777
  // updated because product groups, which are used to represent the parent product
2778
  // for variable products, don't have the visibility property on Facebook
2779
+ $product_ids = [];
 
2780
  // set visibility for all children
2781
  foreach ( $product->get_children() as $index => $id ) {
 
2782
  $product = wc_get_product( $id );
2783
+ if ( ! $product instanceof WC_Product ) {
 
2784
  continue;
2785
  }
 
2786
  Products::set_product_visibility( $product, $should_set_visible );
 
2787
  $product_ids[] = $product->get_id();
2788
  }
 
2789
  // sync product with all variations
2790
+ $this->facebook_for_woocommerce->get_products_sync_handler()->create_or_update_products( $product_ids );
 
2791
  } else {
 
2792
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $product_id );
 
2793
  if ( ! $fb_product_item_id ) {
2794
+ \WC_Facebookcommerce_Utils::fblog( $fb_product_item_id . " doesn't exist but underwent a visibility transform.", [], true );
2795
  return;
2796
  }
2797
+ $set_visibility = $this->facebook_for_woocommerce->get_api()->update_product_item( $fb_product_item_id, [ 'visibility' => $visibility ] );
2798
+ if ( $set_visibility->success ) {
 
 
2799
  Products::set_product_visibility( $product, $should_set_visible );
2800
  }
2801
  }
2802
  }
2803
 
 
2804
  /**
2805
  * Sync product upon quick or bulk edit save action.
2806
  *
2809
  * @param \WC_Product $product product object
2810
  */
2811
  public function on_quick_and_bulk_edit_save( $product ) {
 
2812
  // bail if not a product or product is not enabled for sync
2813
  if ( ! $product instanceof \WC_Product || ! Products::published_product_should_be_synced( $product ) ) {
2814
  return;
2827
  }
2828
  }
2829
 
 
2830
  /**
2831
  * Gets Facebook product ID from meta or from Facebook API.
2832
  *
2833
  * @param string $fbid_type ID type (group or item)
2834
  * @param int $wp_id post ID
2835
  * @param WC_Facebook_Product|null $woo_product product
2836
+ * @return string facebook product id or an empty string
2837
  */
2838
+ public function get_product_fbid( string $fbid_type, int $wp_id, $woo_product = null ) {
2839
+ $fb_id = WC_Facebookcommerce_Utils::get_fbid_post_meta( $wp_id, $fbid_type );
 
 
 
 
 
2840
  if ( $fb_id ) {
2841
  return $fb_id;
2842
  }
 
2843
  if ( ! $woo_product ) {
2844
  $woo_product = new WC_Facebook_Product( $wp_id );
2845
  }
 
2846
  $products = WC_Facebookcommerce_Utils::get_product_array( $woo_product );
 
2847
  // if the product with ID equal to $wp_id is variable, $woo_product will be the first child
2848
  $woo_product = new WC_Facebook_Product( current( $products ) );
2849
 
2850
  $fb_retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
2851
 
2852
+ try {
2853
+ $facebook_ids = $this->facebook_for_woocommerce->get_api()->get_product_facebook_ids(
2854
+ $this->get_product_catalog_id(),
2855
+ $fb_retailer_id
2856
+ );
 
2857
 
2858
+ if ( $facebook_ids->id ) {
2859
+ $fb_id = $fbid_type == self::FB_PRODUCT_GROUP_ID
2860
+ ? $facebook_ids->get_facebook_product_group_id()
2861
+ : $facebook_ids->id;
2862
+ update_post_meta( $wp_id, $fbid_type, $fb_id );
2863
 
2864
+ return $fb_id;
2865
+ }
2866
+ } catch ( Exception $e ) {
2867
+ /* @TODO: Log exception. */
2868
+ WC_Facebookcommerce_Utils::log( $e->getMessage() );
2869
  $this->display_error_message(
2870
  sprintf(
2871
  /* translators: Placeholders %1$s - original error message from Facebook API */
2872
  esc_html__( 'There was an issue connecting to the Facebook API: %s', 'facebook-for-woocommerce' ),
2873
+ $e->getMessage()
2874
  )
2875
  );
 
 
2876
  }
2877
 
2878
+ return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2879
  }
2880
 
2881
  /**
2882
  * Display test result.
2883
  **/
2884
+ public function ajax_display_test_result() {
2885
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'test result', true );
2886
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2887
+ $response = [
2888
  'pass' => 'true',
2889
+ ];
2890
  $test_pass = get_option( 'fb_test_pass', null );
2891
  if ( ! isset( $test_pass ) ) {
2892
  $response['pass'] = 'in progress';
2893
+ } elseif ( $test_pass === 0 ) {
2894
  $response['pass'] = 'false';
2895
  $response['debug_info'] = get_transient( 'facebook_plugin_test_fail' );
2896
  $response['stack_trace'] =
2905
  wp_die();
2906
  }
2907
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2908
  }
facebook-config-warmer.php CHANGED
@@ -9,18 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit; // Exit if accessed directly
14
- }
15
-
16
-
17
- if ( ! class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) :
18
 
19
- class WC_Facebookcommerce_WarmConfig {
20
- static $fb_warm_pixel_id = null;
21
- static $fb_warm_is_advanced_matching_enabled = null;
22
- static $fb_warm_use_s2s = null;
23
- static $fb_warm_access_token = null;
24
- }
25
-
26
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
 
 
 
13
 
14
+ class WC_Facebookcommerce_WarmConfig {
15
+ static $fb_warm_pixel_id = null;
16
+ static $fb_warm_is_advanced_matching_enabled = null;
17
+ static $fb_warm_use_s2s = null;
18
+ static $fb_warm_access_token = null;
19
+ }
 
 
facebook-for-woocommerce.php CHANGED
@@ -11,11 +11,11 @@
11
  * 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.
12
  * Author: Facebook
13
  * Author URI: https://www.facebook.com/
14
- * Version: 2.6.30
15
  * Text Domain: facebook-for-woocommerce
16
  * Tested up to: 6.1
17
  * WC requires at least: 5.3
18
- * WC tested up to: 7.1
19
  * Requires PHP: 7.0
20
  *
21
  * @package FacebookCommerce
@@ -44,7 +44,7 @@ class WC_Facebook_Loader {
44
  /**
45
  * @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
46
  */
47
- const PLUGIN_VERSION = '2.6.30'; // WRCS: DEFINED_VERSION.
48
 
49
  // Minimum PHP version required by this plugin.
50
  const MINIMUM_PHP_VERSION = '7.0.0';
@@ -131,8 +131,6 @@ class WC_Facebook_Loader {
131
  return;
132
  }
133
 
134
- $this->load_framework();
135
-
136
  require_once plugin_dir_path( __FILE__ ) . 'class-wc-facebookcommerce.php';
137
 
138
  // fire it up!
@@ -142,19 +140,6 @@ class WC_Facebook_Loader {
142
  }
143
 
144
 
145
- /**
146
- * Loads the base framework classes.
147
- *
148
- * @since 1.10.0
149
- */
150
- private function load_framework() {
151
-
152
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\' . $this->get_framework_version_namespace() . '\\SV_WC_Plugin' ) ) {
153
- require_once plugin_dir_path( __FILE__ ) . 'vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin.php';
154
- }
155
- }
156
-
157
-
158
  /**
159
  * Gets the framework version in namespace form.
160
  *
@@ -163,7 +148,6 @@ class WC_Facebook_Loader {
163
  * @return string
164
  */
165
  public function get_framework_version_namespace() {
166
-
167
  return 'v' . str_replace( '.', '_', $this->get_framework_version() );
168
  }
169
 
@@ -320,7 +304,6 @@ class WC_Facebook_Loader {
320
  * @return bool
321
  */
322
  private function plugins_compatible() {
323
-
324
  return $this->is_wp_compatible() && $this->is_wc_compatible();
325
  }
326
 
@@ -458,7 +441,6 @@ class WC_Facebook_Loader {
458
  * @return bool
459
  */
460
  private function is_environment_compatible() {
461
-
462
  return version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '>=' );
463
  }
464
 
11
  * 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.
12
  * Author: Facebook
13
  * Author URI: https://www.facebook.com/
14
+ * Version: 3.0.0
15
  * Text Domain: facebook-for-woocommerce
16
  * Tested up to: 6.1
17
  * WC requires at least: 5.3
18
+ * WC tested up to: 5.4
19
  * Requires PHP: 7.0
20
  *
21
  * @package FacebookCommerce
44
  /**
45
  * @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
46
  */
47
+ const PLUGIN_VERSION = '3.0.0'; // WRCS: DEFINED_VERSION.
48
 
49
  // Minimum PHP version required by this plugin.
50
  const MINIMUM_PHP_VERSION = '7.0.0';
131
  return;
132
  }
133
 
 
 
134
  require_once plugin_dir_path( __FILE__ ) . 'class-wc-facebookcommerce.php';
135
 
136
  // fire it up!
140
  }
141
 
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  /**
144
  * Gets the framework version in namespace form.
145
  *
148
  * @return string
149
  */
150
  public function get_framework_version_namespace() {
 
151
  return 'v' . str_replace( '.', '_', $this->get_framework_version() );
152
  }
153
 
304
  * @return bool
305
  */
306
  private function plugins_compatible() {
 
307
  return $this->is_wp_compatible() && $this->is_wc_compatible();
308
  }
309
 
441
  * @return bool
442
  */
443
  private function is_environment_compatible() {
 
444
  return version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '>=' );
445
  }
446
 
i18n/languages/facebook-for-woocommerce.pot CHANGED
@@ -2,24 +2,24 @@
2
  # This file is distributed under the same license as the Facebook for WooCommerce plugin.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Facebook for WooCommerce 2.6.30\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/facebook-for-woocommerce\n"
7
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
8
  "Language-Team: LANGUAGE <LL@li.org>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "POT-Creation-Date: 2022-11-09T04:20:40+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
- "X-Generator: WP-CLI 2.5.0\n"
15
  "X-Domain: facebook-for-woocommerce\n"
16
 
17
  #. Plugin Name of the plugin
18
- #: class-wc-facebookcommerce.php:1095
19
- #: facebook-commerce.php:229
20
- #: includes/Admin.php:1626
21
- #: includes/Admin/Settings.php:100
22
- #: includes/Admin/Settings.php:170
23
  msgid "Facebook for WooCommerce"
24
  msgstr ""
25
 
@@ -32,9 +32,9 @@ msgid "Grow your business on Facebook! Use this official plugin to help sell mor
32
  msgstr ""
33
 
34
  #. Author of the plugin
35
- #: includes/Admin.php:1262
36
- #: includes/Admin/Settings.php:101
37
- #: includes/Admin/Settings.php:381
38
  msgid "Facebook"
39
  msgstr ""
40
 
@@ -42,319 +42,294 @@ msgstr ""
42
  msgid "https://www.facebook.com/"
43
  msgstr ""
44
 
45
- #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
46
- #: class-wc-facebookcommerce.php:293
47
- msgid "%1$sHeads up!%2$s You're ready to migrate to a more secure, reliable Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!"
48
- msgstr ""
49
-
50
- #. translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link HTML tag
51
- #: class-wc-facebookcommerce.php:314
52
- msgid "For your convenience, the Facebook for WooCommerce settings are now located under %1$sWooCommerce > Facebook%2$s."
53
- msgstr ""
54
-
55
- #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag
56
- #: class-wc-facebookcommerce.php:342
57
- msgid "%1$sHeads up!%2$s If you've customized your Facebook Messenger color or greeting settings, please update those settings again from the %3$sManage Connection%4$s area."
58
- msgstr ""
59
-
60
- #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag
61
- #: class-wc-facebookcommerce.php:365
62
- msgid "%1$sHeads up!%2$s Your connection to Facebook is no longer valid. Please %3$sclick here%4$s to securely reconnect your account and continue syncing products."
63
- msgstr ""
64
-
65
- #: class-wc-facebookcommerce.php:478
66
- #: includes/Admin/Settings.php:131
67
- #: includes/Admin/Settings.php:132
68
  msgid "Facebook Product Sets"
69
  msgstr ""
70
 
71
- #: class-wc-facebookcommerce.php:479
72
  msgid "Facebook Product Set"
73
  msgstr ""
74
 
75
  #. translators: Edit item label
76
- #: class-wc-facebookcommerce.php:487
77
  msgid "Edit %s"
78
  msgstr ""
79
 
80
  #. translators: Add new label
81
- #: class-wc-facebookcommerce.php:489
82
  msgid "Add new %s"
83
  msgstr ""
84
 
85
  #. translators: No items found text
86
- #: class-wc-facebookcommerce.php:492
87
  msgid "No %s found."
88
  msgstr ""
89
 
90
  #. translators: Search label
91
- #: class-wc-facebookcommerce.php:494
92
  msgid "Search %s."
93
  msgstr ""
94
 
95
  #. translators: Text label
96
- #: class-wc-facebookcommerce.php:496
97
  msgid "Separate %s with commas"
98
  msgstr ""
99
 
100
  #. translators: Text label
101
- #: class-wc-facebookcommerce.php:498
102
  msgid "Choose from the most used %s"
103
  msgstr ""
104
 
105
- #: class-wc-facebookcommerce.php:639
106
  msgid "Cannot create the API instance because the access token is missing."
107
  msgstr ""
108
 
109
- #: facebook-commerce.php:233
110
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
111
  msgstr ""
112
 
113
- #: facebook-commerce.php:1472
 
114
  msgid "Nothing to update for product group for %1$s"
115
  msgstr ""
116
 
117
- #. translators: Placeholders %1$s - original error message from Facebook API
118
- #: facebook-commerce.php:1902
119
- msgid "There was an issue connecting to the Facebook API: %1$s"
120
- msgstr ""
121
-
122
- #: facebook-commerce.php:2227
123
  msgid "Your connection has expired."
124
  msgstr ""
125
 
126
- #: facebook-commerce.php:2227
127
  msgid "Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook."
128
  msgstr ""
129
 
130
  #. translators: Placeholders %s - error message
131
- #: facebook-commerce.php:2234
132
  msgid "There was an error trying to sync the products to Facebook. %s"
133
  msgstr ""
134
 
135
- #: facebook-commerce.php:2257
136
  msgid "Product sync is disabled."
137
  msgstr ""
138
 
139
- #: facebook-commerce.php:2264
140
  msgid "The plugin is not configured or the Catalog ID is missing."
141
  msgstr ""
142
 
143
- #: facebook-commerce.php:2287
144
  msgid "A product sync is in progress. Please wait until the sync finishes before starting a new one."
145
  msgstr ""
146
 
147
- #: facebook-commerce.php:2299
148
  msgid "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again."
149
  msgstr ""
150
 
151
- #: facebook-commerce.php:2828
152
  msgid "Hi! We're here to answer any questions you may have."
153
  msgstr ""
154
 
155
- #: facebook-commerce.php:3218
156
  msgid "Facebook for WooCommerce error:"
157
  msgstr ""
158
 
159
  #. translators: Placeholders %1$s - original error message from Facebook API
160
- #: facebook-commerce.php:3587
161
  msgid "There was an issue connecting to the Facebook API: %s"
162
  msgstr ""
163
 
164
  #. translators: %1$s - plugin name, %2$s - minimum WordPress version required, %3$s - update WordPress link open, %4$s - update WordPress link close
165
- #: facebook-for-woocommerce.php:238
166
  msgid "%1$s requires WordPress version %2$s or higher. Please %3$supdate WordPress &raquo;%4$s"
167
  msgstr ""
168
 
169
  #. translators: %1$s - Plugin Name, %2$s - activate WooCommerce link open, %3$s - activate WooCommerce link close.
170
- #: facebook-for-woocommerce.php:264
171
  msgid "%1$s requires WooCommerce to be activated. Please %2$sactivate WooCommerce%3$s."
172
  msgstr ""
173
 
174
  #. translators: %1$s - Plugin Name, %2$s - install WooCommerce link open, %3$s - install WooCommerce link close.
175
- #: facebook-for-woocommerce.php:281
176
  msgid "%1$s requires WooCommerce to be installed and activated. Please %2$sinstall WooCommerce%3$s."
177
  msgstr ""
178
 
179
  #. translators: %1$s - Plugin Name, %2$s - minimum WooCommerce version, %3$s - update WooCommerce link open, %4$s - update WooCommerce link close, %5$s - download minimum WooCommerce link open, %6$s - download minimum WooCommerce link close.
180
- #: facebook-for-woocommerce.php:301
181
  msgid "%1$s requires WooCommerce version %2$s or higher. Please %3$supdate WooCommerce%4$s to the latest version, or %5$sdownload the minimum required version &raquo;%6$s"
182
  msgstr ""
183
 
184
- #: includes/Admin.php:132
185
  msgid "The name is how it appears on Facebook Catalog."
186
  msgstr ""
187
 
188
- #: includes/Admin.php:173
189
  msgid "Search main categories..."
190
  msgstr ""
191
 
192
- #: includes/Admin.php:174
193
  msgid "Choose a main category"
194
  msgstr ""
195
 
196
- #: includes/Admin.php:175
197
  msgid "Choose a category"
198
  msgstr ""
199
 
200
- #: includes/Admin.php:211
201
  msgid "You have selected one or more categories currently excluded from the Facebook sync. Products belonging to the excluded categories will not be added to your Facebook Product Set."
202
  msgstr ""
203
 
204
- #: includes/Admin.php:252
205
  msgid "Please enter a Google product category and at least one sub-category to sell this product on Instagram."
206
  msgstr ""
207
 
208
- #: includes/Admin.php:318
209
  msgid "To sell this product on Instagram, please ensure it meets the following requirements:"
210
  msgstr ""
211
 
212
- #: includes/Admin.php:321
213
  msgid "Has a price defined"
214
  msgstr ""
215
 
216
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag
217
- #: includes/Admin.php:327
218
  msgid "Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab"
219
  msgstr ""
220
 
221
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag
222
- #: includes/Admin.php:339
223
  msgid "Has the %1$sFacebook Sync%2$s setting set to \"Sync and show\" or \"Sync and hide\""
224
  msgstr ""
225
 
226
  #. translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a> link tag
227
- #: includes/Admin.php:393
228
  msgid "You're removing a product from the Facebook sync that is currently listed in your %1$sFacebook catalog%2$s. Would you like to delete the product from the Facebook catalog as well?"
229
  msgstr ""
230
 
231
- #: includes/Admin.php:462
232
- #: includes/Admin.php:1305
233
- #: includes/Admin.php:1447
234
  msgid "Facebook sync"
235
  msgstr ""
236
 
237
- #: includes/Admin.php:489
238
- #: includes/Admin.php:520
239
  msgid "Sync and show"
240
  msgstr ""
241
 
242
- #: includes/Admin.php:491
243
- #: includes/Admin.php:521
244
  msgid "Sync and hide"
245
  msgstr ""
246
 
247
- #: includes/Admin.php:495
248
- #: includes/Admin.php:522
249
- #: includes/Admin.php:1309
250
- #: includes/Admin.php:1451
251
  msgid "Do not sync"
252
  msgstr ""
253
 
254
- #: includes/Admin.php:519
255
  msgid "Filter by Facebook sync setting"
256
  msgstr ""
257
 
258
- #: includes/Admin.php:932
259
  msgid "Include in Facebook sync"
260
  msgstr ""
261
 
262
- #: includes/Admin.php:933
263
  msgid "Exclude from Facebook sync"
264
  msgstr ""
265
 
266
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - <a> tag
267
- #: includes/Admin.php:1126
268
  msgid "%1$sHeads up!%2$s If this product was previously visible in Facebook, you may need to delete it from the %3$sFacebook catalog%4$s to completely hide it from customer view."
269
  msgid_plural "%1$sHeads up!%2$s If these products were previously visible in Facebook, you may need to delete them from the %3$sFacebook catalog%4$s to completely hide them from customer view."
270
  msgstr[0] ""
271
  msgstr[1] ""
272
 
273
- #: includes/Admin.php:1133
274
  msgid "Don't show this notice again"
275
  msgstr ""
276
 
277
  #. translators: Placeholders: %1$s - number of affected products, %2$s opening HTML <a> tag, %3$s - closing HTML </a> tag, %4$s - opening HTML <a> tag, %5$s - closing HTML </a> tag
278
- #: includes/Admin.php:1167
279
  msgid "%2$s%1$s product%3$s or some of its variations could not be updated to show in the Facebook catalog — %4$sFacebook Commerce Policies%5$s prohibit selling some product types (like virtual products). You may still advertise Virtual products on Facebook."
280
  msgid_plural "%2$s%1$s products%3$s or some of their variations could not be updated to show in the Facebook catalog — %4$sFacebook Commerce Policies%5$s prohibit selling some product types (like virtual products). You may still advertise Virtual products on Facebook."
281
  msgstr[0] ""
282
  msgstr[1] ""
283
 
284
  #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a> tag
285
- #: includes/Admin.php:1233
286
  msgid "%1$sHeads up!%2$s Facebook's %3$sCommerce Policies%4$s do not support selling virtual products, so we have hidden your synced Virtual products in your Facebook catalog. You may still advertise Virtual products on Facebook."
287
  msgstr ""
288
 
289
- #: includes/Admin.php:1307
290
- #: includes/Admin.php:1449
291
  msgid "Sync and show in catalog"
292
  msgstr ""
293
 
294
- #: includes/Admin.php:1308
295
- #: includes/Admin.php:1450
296
  msgid "Sync and hide in catalog"
297
  msgstr ""
298
 
299
- #: includes/Admin.php:1318
300
- #: includes/Admin.php:1463
301
  msgid "Facebook Description"
302
  msgstr ""
303
 
304
- #: includes/Admin.php:1320
305
- #: includes/Admin.php:1465
306
  msgid "Custom (plain-text only) description for product on Facebook. If blank, product description will be used. If product description is blank, shortname will be used."
307
  msgstr ""
308
 
309
- #: includes/Admin.php:1331
310
- #: includes/Admin.php:1478
311
  msgid "Facebook Product Image"
312
  msgstr ""
313
 
314
- #: includes/Admin.php:1333
315
- #: includes/Admin.php:1480
316
  msgid "Choose the product image that should be synced to the Facebook catalog for this product. If using a custom image, please enter an absolute URL (e.g. https://domain.com/image.jpg)."
317
  msgstr ""
318
 
319
- #: includes/Admin.php:1335
320
  msgid "Use WooCommerce image"
321
  msgstr ""
322
 
323
- #: includes/Admin.php:1336
324
- #: includes/Admin.php:1484
325
  msgid "Use custom image"
326
  msgstr ""
327
 
328
- #: includes/Admin.php:1347
329
- #: includes/Admin.php:1496
330
  msgid "Custom Image URL"
331
  msgstr ""
332
 
333
  #. translators: Placeholders %1$s - WC currency symbol
334
- #. translators: Placeholders %1$s - WC currency symbol
335
- #: includes/Admin.php:1358
336
- #: includes/Admin.php:1509
337
  msgid "Facebook Price (%1$s)"
338
  msgstr ""
339
 
340
- #: includes/Admin.php:1362
341
- #: includes/Admin.php:1513
342
  msgid "Custom price for product on Facebook. Please enter in monetary decimal (.) format without thousand separators and currency symbols. If blank, product price will be used."
343
  msgstr ""
344
 
345
- #: includes/Admin.php:1482
346
  msgid "Use variation image"
347
  msgstr ""
348
 
349
- #: includes/Admin.php:1483
350
  msgid "Use parent image"
351
  msgstr ""
352
 
353
- #: includes/Admin.php:1628
354
  msgid "Close modal panel"
355
  msgstr ""
356
 
357
- #: includes/Admin/Abstract_Settings_Screen.php:73
358
  msgid "Save changes"
359
  msgstr ""
360
 
@@ -362,169 +337,157 @@ msgstr ""
362
  msgid "Show advanced options"
363
  msgstr ""
364
 
365
- #: includes/Admin/Notes/SettingsMoved.php:76
366
- msgid "Sync your products and reach customers across Facebook, Instagram, Messenger and WhatsApp through your Facebook plugin, which can be found at Marketing > Facebook."
367
- msgstr ""
368
-
369
- #: includes/Admin/Notes/SettingsMoved.php:79
370
- msgid "Facebook is now found under Marketing"
371
- msgstr ""
372
-
373
- #: includes/Admin/Notes/SettingsMoved.php:85
374
- msgid "Go to Facebook"
375
- msgstr ""
376
-
377
- #: includes/Admin/Products.php:101
378
  msgid "Select values for enhanced attributes for this product"
379
  msgstr ""
380
 
381
- #: includes/Admin/Products.php:119
382
- #: includes/Admin/Product_Categories.php:376
383
  msgid "Category Specific Attributes"
384
  msgstr ""
385
 
386
- #: includes/Admin/Products.php:140
387
  msgid "Google product category"
388
  msgstr ""
389
 
390
- #: includes/Admin/Products.php:141
391
  msgid "Choose the Google product category and (optionally) sub-categories associated with this product."
392
  msgstr ""
393
 
394
- #: includes/Admin/Products.php:214
395
  msgid "Sell on Instagram"
396
  msgstr ""
397
 
398
- #: includes/Admin/Products.php:216
399
  msgid "Enable to sell this product on Instagram. Products that are hidden in the Facebook catalog can be synced, but won’t be available for purchase."
400
  msgstr ""
401
 
402
- #: includes/Admin/Products.php:226
403
  msgid "This product does not meet the requirements to sell on Instagram."
404
  msgstr ""
405
 
406
- #: includes/Admin/Products.php:227
407
  msgid "Click here to learn more."
408
  msgstr ""
409
 
410
  #. translators: Placeholders %1$s - strong opening tag, %2$s - strong closing tag
411
- #: includes/Admin/Products.php:236
412
  msgid "To sell this product on Instagram, at least one variation must be synced to Facebook. You can control variation sync on the %1$sVariations%2$s tab with the %1$sFacebook Sync%2$s setting."
413
  msgstr ""
414
 
415
- #: includes/Admin/Product_Categories.php:100
416
- #: includes/Admin/Settings_Screens/Product_Sync.php:121
417
  msgid "Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?"
418
  msgstr ""
419
 
420
- #: includes/Admin/Product_Categories.php:119
421
- #: includes/Admin/Settings_Screens/Product_Sync.php:158
422
- #: includes/AJAX.php:348
423
- #: includes/AJAX.php:416
424
- #: includes/AJAX.php:482
425
  msgid "Cancel"
426
  msgstr ""
427
 
428
- #: includes/Admin/Product_Categories.php:123
429
- #: includes/Admin/Settings_Screens/Product_Sync.php:162
430
  msgid "Update default Google product category"
431
  msgstr ""
432
 
433
- #: includes/Admin/Product_Categories.php:164
434
  msgid "Facebook catalogs now support category specific fields, to make best use of them you need to select a category. WooCommerce uses the google taxonomy as it is the most widely accepted form of categorisation."
435
  msgstr ""
436
 
437
- #: includes/Admin/Product_Categories.php:215
438
  msgid "Choose a default Google product category for products in this category. Products need at least two category levels defined for tax to be correctly applied."
439
  msgstr ""
440
 
441
- #: includes/Admin/Product_Categories.php:233
442
- #: includes/Admin/Settings_Screens/Product_Sync.php:338
443
  msgid "Default Google product category"
444
  msgstr ""
445
 
446
- #: includes/Admin/Product_Categories.php:358
447
  msgid "Select default values for enhanced attributes within this category"
448
  msgstr ""
449
 
450
- #: includes/Admin/Product_Sets.php:150
451
  msgid "WC Product Categories"
452
  msgstr ""
453
 
454
- #: includes/Admin/Product_Sets.php:182
455
  msgid "Map Facebook Product Set to WC Product Categories"
456
  msgstr ""
457
 
458
- #: includes/Admin/Product_Sync_Meta_Box.php:37
459
  msgid "Facebook Product Sync"
460
  msgstr ""
461
 
462
- #: includes/Admin/Product_Sync_Meta_Box.php:78
463
  msgid "Facebook ID:"
464
  msgstr ""
465
 
466
- #: includes/Admin/Product_Sync_Meta_Box.php:89
467
  msgid "Variant IDs:"
468
  msgstr ""
469
 
470
- #: includes/Admin/Product_Sync_Meta_Box.php:121
471
  msgid "Reset Facebook metadata"
472
  msgstr ""
473
 
474
- #: includes/Admin/Product_Sync_Meta_Box.php:126
475
  msgid "Delete product(s) on Facebook"
476
  msgstr ""
477
 
478
- #: includes/Admin/Product_Sync_Meta_Box.php:138
479
  msgid "This product is not yet synced to Facebook."
480
  msgstr ""
481
 
482
- #: includes/Admin/Settings.php:176
483
  #: includes/Admin/Settings_Screens/Connection.php:35
484
  #: includes/Admin/Settings_Screens/Connection.php:36
485
  msgid "Connection"
486
  msgstr ""
487
 
488
- #: includes/Admin/Settings.php:179
489
  #: includes/Admin/Settings_Screens/Messenger.php:37
490
  #: includes/Admin/Settings_Screens/Messenger.php:38
491
- #: includes/Admin/Settings_Screens/Messenger.php:140
492
  msgid "Messenger"
493
  msgstr ""
494
 
495
- #: includes/Admin/Settings.php:182
 
496
  #: includes/Admin/Settings_Screens/Product_Sync.php:43
497
- #: includes/Admin/Settings_Screens/Product_Sync.php:44
498
- #: includes/Admin/Settings_Screens/Product_Sync.php:181
499
- #: includes/Admin/Settings_Screens/Product_Sync.php:284
500
  msgid "Product sync"
501
  msgstr ""
502
 
503
- #: includes/Admin/Settings.php:185
504
- #: includes/Admin/Settings_Screens/Advertise.php:38
505
- #: includes/Admin/Settings_Screens/Advertise.php:39
506
  msgid "Advertise"
507
  msgstr ""
508
 
509
- #: includes/Admin/Settings.php:271
510
  msgid "You do not have permission to save these settings."
511
  msgstr ""
512
 
513
- #: includes/Admin/Settings.php:280
514
  msgid "Your settings have been saved."
515
  msgstr ""
516
 
517
  #. translators: Placeholders: %s - user-friendly error message
518
- #: includes/Admin/Settings.php:287
519
  msgid "Your settings could not be saved. %s"
520
  msgstr ""
521
 
522
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag
523
- #: includes/Admin/Settings_Screens/Advertise.php:203
524
  msgid "Please %1$sconnect your store%2$s to Facebook to create ads."
525
  msgstr ""
526
 
527
- #: includes/Admin/Settings_Screens/Advertise.php:221
528
  msgid "If you are connected to Facebook but cannot display ads, please contact Facebook support."
529
  msgstr ""
530
 
@@ -561,195 +524,195 @@ msgstr ""
561
  msgid "Commerce Merchant Settings ID"
562
  msgstr ""
563
 
564
- #: includes/Admin/Settings_Screens/Connection.php:255
565
  msgid "Reach the Right People and Sell More Online"
566
  msgstr ""
567
 
568
- #: includes/Admin/Settings_Screens/Connection.php:257
569
  msgid "Grow your business on Facebook"
570
  msgstr ""
571
 
572
- #: includes/Admin/Settings_Screens/Connection.php:260
573
  msgid "Use this WooCommerce and Facebook integration to:"
574
  msgstr ""
575
 
576
- #: includes/Admin/Settings_Screens/Connection.php:262
577
  msgid "Create an ad in a few steps"
578
  msgstr ""
579
 
580
- #: includes/Admin/Settings_Screens/Connection.php:263
581
  msgid "Use built-in best practices for online sales"
582
  msgstr ""
583
 
584
- #: includes/Admin/Settings_Screens/Connection.php:264
585
  msgid "Get reporting on sales and revenue"
586
  msgstr ""
587
 
588
- #: includes/Admin/Settings_Screens/Connection.php:287
589
  msgid "Manage Connection"
590
  msgstr ""
591
 
592
- #: includes/Admin/Settings_Screens/Connection.php:291
593
  msgid "Disconnect"
594
  msgstr ""
595
 
596
- #: includes/Admin/Settings_Screens/Connection.php:297
597
  msgid "Get Started"
598
  msgstr ""
599
 
600
- #: includes/Admin/Settings_Screens/Connection.php:322
601
  msgid "Debug"
602
  msgstr ""
603
 
604
- #: includes/Admin/Settings_Screens/Connection.php:328
605
  msgid "Enable debug mode"
606
  msgstr ""
607
 
608
- #: includes/Admin/Settings_Screens/Connection.php:330
609
  msgid "Log plugin events for debugging."
610
  msgstr ""
611
 
612
- #: includes/Admin/Settings_Screens/Connection.php:331
613
  msgid "Only enable this if you are experiencing problems with the plugin."
614
  msgstr ""
615
 
616
- #: includes/Admin/Settings_Screens/Connection.php:337
617
  msgid "Experimental! Enable new style feed generation"
618
  msgstr ""
619
 
620
- #: includes/Admin/Settings_Screens/Connection.php:339
621
  msgid "Use new, memory improved, feed generation process."
622
  msgstr ""
623
 
624
- #: includes/Admin/Settings_Screens/Connection.php:340
625
  msgid "Experimental feature. Only enable this if you are experiencing problems with feed generation. This is an experimental feature in testing phase."
626
  msgstr ""
627
 
628
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
629
- #: includes/Admin/Settings_Screens/Messenger.php:114
630
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
631
  msgstr ""
632
 
633
- #: includes/Admin/Settings_Screens/Messenger.php:146
634
  msgid "Enable Messenger"
635
  msgstr ""
636
 
637
- #: includes/Admin/Settings_Screens/Messenger.php:148
638
  msgid "Enable and customize Facebook Messenger on your store"
639
  msgstr ""
640
 
641
- #: includes/Admin/Settings_Screens/Messenger.php:158
642
  msgid "Language"
643
  msgstr ""
644
 
645
- #: includes/Admin/Settings_Screens/Messenger.php:163
646
  msgid "Greeting & Colors"
647
  msgstr ""
648
 
649
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
650
- #: includes/Admin/Settings_Screens/Messenger.php:185
651
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger."
652
  msgstr ""
653
 
654
- #: includes/Admin/Settings_Screens/Messenger.php:262
655
  msgid "Please try again."
656
  msgstr ""
657
 
658
- #: includes/Admin/Settings_Screens/Product_Sets.php:34
659
- #: includes/Admin/Settings_Screens/Product_Sets.php:35
660
  msgid "Product sets"
661
  msgstr ""
662
 
663
  #. translators: Placeholders: {count} number of remaining items
664
- #: includes/Admin/Settings_Screens/Product_Sync.php:82
665
  msgid "{count} item remaining."
666
  msgid_plural "{count} items remaining."
667
  msgstr[0] ""
668
  msgstr[1] ""
669
 
670
  #. translators: Placeholders %s - html code for a spinner icon
671
- #: includes/Admin/Settings_Screens/Product_Sync.php:97
672
  msgid "Your products will now be resynced to Facebook, this may take some time."
673
  msgstr ""
674
 
675
- #: includes/Admin/Settings_Screens/Product_Sync.php:98
676
  msgid ""
677
  "Facebook for WooCommerce automatically syncs your products on create/update. Are you sure you want to force product resync?\n"
678
  "\n"
679
  "This will query all published products and may take some time. You only need to do this if your products are out of sync or some of your products did not sync."
680
  msgstr ""
681
 
682
- #: includes/Admin/Settings_Screens/Product_Sync.php:99
683
  msgid "Your products are syncing - you may safely leave this page %s"
684
  msgstr ""
685
 
686
- #: includes/Admin/Settings_Screens/Product_Sync.php:102
687
  msgid "There was an error trying to sync the products to Facebook."
688
  msgstr ""
689
 
690
- #: includes/Admin/Settings_Screens/Product_Sync.php:103
691
  msgid "Something went wrong while uploading the product information, please try again."
692
  msgstr ""
693
 
694
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
695
- #: includes/Admin/Settings_Screens/Product_Sync.php:136
696
  msgid "Products and categories that inherit this global setting (they do not have a specific Google product category set) will use the new default immediately. %1$sIf you have cleared the Google Product Category%2$s, items inheriting the default will not be available for Instagram checkout. Are you sure you want to proceed?"
697
  msgstr ""
698
 
699
- #: includes/Admin/Settings_Screens/Product_Sync.php:189
700
  msgid "Sync products"
701
  msgstr ""
702
 
703
- #: includes/Admin/Settings_Screens/Product_Sync.php:289
704
  msgid "Enable product sync"
705
  msgstr ""
706
 
707
- #: includes/Admin/Settings_Screens/Product_Sync.php:297
708
  msgid "Exclude categories from sync"
709
  msgstr ""
710
 
711
- #: includes/Admin/Settings_Screens/Product_Sync.php:301
712
  msgid "Products in one or more of these categories will not sync to Facebook."
713
  msgstr ""
714
 
715
- #: includes/Admin/Settings_Screens/Product_Sync.php:305
716
  msgid "Search for a product category&hellip;"
717
  msgstr ""
718
 
719
- #: includes/Admin/Settings_Screens/Product_Sync.php:311
720
  msgid "Exclude tags from sync"
721
  msgstr ""
722
 
723
- #: includes/Admin/Settings_Screens/Product_Sync.php:315
724
  msgid "Products with one or more of these tags will not sync to Facebook."
725
  msgstr ""
726
 
727
- #: includes/Admin/Settings_Screens/Product_Sync.php:319
728
  msgid "Search for a product tag&hellip;"
729
  msgstr ""
730
 
731
- #: includes/Admin/Settings_Screens/Product_Sync.php:325
732
  msgid "Product description sync"
733
  msgstr ""
734
 
735
- #: includes/Admin/Settings_Screens/Product_Sync.php:328
736
  msgid "Choose which product description to display in the Facebook catalog."
737
  msgstr ""
738
 
739
- #: includes/Admin/Settings_Screens/Product_Sync.php:331
740
  msgid "Standard description"
741
  msgstr ""
742
 
743
- #: includes/Admin/Settings_Screens/Product_Sync.php:332
744
  msgid "Short description"
745
  msgstr ""
746
 
747
- #: includes/Admin/Settings_Screens/Product_Sync.php:339
748
  msgid "Choose a default Google product category for your products. Defaults can also be set for product categories. Products need at least two category levels defined for tax to be correctly applied."
749
  msgstr ""
750
 
751
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
752
- #: includes/Admin/Settings_Screens/Product_Sync.php:385
753
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage product sync."
754
  msgstr ""
755
 
@@ -765,203 +728,329 @@ msgstr ""
765
  msgid "20 minutes"
766
  msgstr ""
767
 
768
- #: includes/AJAX.php:82
769
  msgid "Invalid nonce."
770
  msgstr ""
771
 
772
- #: includes/AJAX.php:89
773
  msgid "Order ID is required."
774
  msgstr ""
775
 
776
- #: includes/AJAX.php:93
777
  msgid "Cancel reason is required."
778
  msgstr ""
779
 
780
- #: includes/AJAX.php:99
781
  msgid "A valid Order ID is required."
782
  msgstr ""
783
 
784
- #: includes/AJAX.php:182
785
  msgid "Order ID is required"
786
  msgstr ""
787
 
788
- #: includes/AJAX.php:186
789
  msgid "Tracking number is required"
790
  msgstr ""
791
 
792
- #: includes/AJAX.php:190
793
  msgid "Carrier code is required"
794
  msgstr ""
795
 
796
- #: includes/AJAX.php:196
797
  msgid "Order not found"
798
  msgstr ""
799
 
800
- #: includes/AJAX.php:220
801
  msgid "Full product sync disabled by filter."
802
  msgstr ""
803
 
804
- #: includes/AJAX.php:343
805
- #: includes/AJAX.php:411
806
  msgid "Go to Settings"
807
  msgstr ""
808
 
809
  #. translators: Placeholder %s - <br/> tag
810
- #: includes/AJAX.php:357
811
  msgid "This product belongs to a category or tag that is excluded from the Facebook catalog sync. It will not sync to Facebook. %sTo sync this product to Facebook, click Go to Settings and remove the category or tag exclusion or click Cancel and update the product's category / tag assignments."
812
  msgstr ""
813
 
814
- #: includes/AJAX.php:423
815
  msgid "One or more of the selected products belongs to a category or tag that is excluded from the Facebook catalog sync. To sync these products to Facebook, please remove the category or tag exclusion from the plugin settings."
816
  msgstr ""
817
 
818
- #: includes/AJAX.php:476
819
  msgid "Exclude Products"
820
  msgstr ""
821
 
822
  #. translators: Placeholder %s - <br/> tags
823
- #: includes/AJAX.php:491
824
  msgid "The categories and/or tags that you have selected to exclude from sync contain products that are currently synced to Facebook.%sTo exclude these products from the Facebook sync, click Exclude Products. To review the category / tag exclusion settings, click Cancel."
825
  msgstr ""
826
 
827
- #: includes/Commerce/Orders.php:433
828
  msgid "Order %1$s paid in %2$s"
829
  msgstr ""
830
 
831
- #: includes/Commerce/Orders.php:585
832
- #: includes/Commerce/Orders.php:820
833
  msgid "Remote ID not found."
834
  msgstr ""
835
 
836
- #: includes/Commerce/Orders.php:592
837
  msgid "%s is not a valid shipping carrier code."
838
  msgstr ""
839
 
840
- #: includes/Commerce/Orders.php:610
841
- #: includes/Commerce/Orders.php:787
842
  msgid "No valid Facebook products were found."
843
  msgstr ""
844
 
845
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
846
- #: includes/Commerce/Orders.php:628
847
  msgid "%s order fulfilled."
848
  msgstr ""
849
 
850
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
851
- #: includes/Commerce/Orders.php:638
852
  msgid "%1$s order could not be fulfilled. %2$s"
853
  msgstr ""
854
 
855
- #: includes/Commerce/Orders.php:682
856
  msgid "Parent order not found."
857
  msgstr ""
858
 
859
- #: includes/Commerce/Orders.php:688
860
  msgid "Remote ID for parent order not found."
861
  msgstr ""
862
 
863
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
864
- #: includes/Commerce/Orders.php:719
865
  msgid "Order refunded on %s."
866
  msgstr ""
867
 
868
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
869
- #: includes/Commerce/Orders.php:731
870
  msgid "Could not refund %1$s order: %2$s"
871
  msgstr ""
872
 
873
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
874
- #: includes/Commerce/Orders.php:828
875
  msgid "%s order cancelled."
876
  msgstr ""
877
 
878
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
879
- #: includes/Commerce/Orders.php:838
880
  msgid "%1$s order could not be cancelled. %2$s"
881
  msgstr ""
882
 
883
- #: includes/Commerce/Orders.php:860
884
  msgid "Customer requested cancellation"
885
  msgstr ""
886
 
887
- #: includes/Commerce/Orders.php:861
888
  msgid "Product(s) are out of stock"
889
  msgstr ""
890
 
891
- #: includes/Commerce/Orders.php:862
892
  msgid "Customer address is invalid"
893
  msgstr ""
894
 
895
- #: includes/Commerce/Orders.php:863
896
  msgid "Suspicious order"
897
  msgstr ""
898
 
899
- #: includes/Commerce/Orders.php:864
900
  msgid "Other"
901
  msgstr ""
902
 
903
- #: includes/fbgraph.php:106
904
- msgid "HTTP %1$s: %2$s"
905
- msgstr ""
906
-
907
- #: includes/fbinfobanner.php:212
908
  msgid "Click and redirect."
909
  msgstr ""
910
 
911
- #: includes/fbinfobanner.php:213
912
  msgid "Dismiss this notice."
913
  msgstr ""
914
 
915
- #: includes/fbinfobanner.php:213
916
  msgid "Dismiss"
917
  msgstr ""
918
 
919
- #: includes/fbproductfeed.php:278
920
  msgid "Could not create product catalog feed directory"
921
  msgstr ""
922
 
923
- #: includes/fbproductfeed.php:382
924
  msgid "Could not open the product catalog temporary feed file for writing"
925
  msgstr ""
926
 
927
- #: includes/fbproductfeed.php:389
928
  msgid "Could not open the product catalog feed file for writing"
929
  msgstr ""
930
 
931
- #: includes/fbproductfeed.php:453
932
  msgid "Could not rename the product catalog feed file"
933
  msgstr ""
934
 
935
- #: includes/fbwpml.php:115
936
  msgid "Facebook Visibility"
937
  msgstr ""
938
 
939
- #: includes/fbwpml.php:118
940
  msgid "WooCommerce Products with languages that are selected here will be visible to customers who see your Facebook Shop."
941
  msgstr ""
942
 
943
- #: includes/fbwpml.php:135
944
  msgid "Saved. An automated sync from Facebook will run every hour to update the catalog with any changes you've made."
945
  msgstr ""
946
 
947
- #: includes/Handlers/Connection.php:298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
948
  msgid "Connection successful!"
949
  msgstr ""
950
 
951
- #: includes/Handlers/Connection.php:360
952
  msgid "You do not have permission to uninstall Facebook Business Extension."
953
  msgstr ""
954
 
955
- #: includes/Handlers/Connection.php:370
956
  msgid "Disconnection successful."
957
  msgstr ""
958
 
959
  #. translators: Placeholders: %s - Facebook page ID
960
- #: includes/Handlers/Connection.php:450
961
  msgid "Page %s not authorized."
962
  msgstr ""
963
 
964
- #: includes/Handlers/Connection.php:1423
965
  msgid "You do not have permission to finish App Store login."
966
  msgstr ""
967
 
@@ -970,58 +1059,63 @@ msgctxt "language"
970
  msgid "English (United States)"
971
  msgstr ""
972
 
973
- #: includes/ProductSync/ProductValidator.php:195
 
 
 
 
 
974
  msgid "Product sync is globally disabled."
975
  msgstr ""
976
 
977
- #: includes/ProductSync/ProductValidator.php:208
978
  msgid "Product is not published."
979
  msgstr ""
980
 
981
- #: includes/ProductSync/ProductValidator.php:219
982
  msgid "Product must be in stock."
983
  msgstr ""
984
 
985
- #: includes/ProductSync/ProductValidator.php:234
986
  msgid "This product cannot be synced to Facebook because it is hidden from your store catalog."
987
  msgstr ""
988
 
989
- #: includes/ProductSync/ProductValidator.php:249
990
  msgid "Product excluded because of categories."
991
  msgstr ""
992
 
993
- #: includes/ProductSync/ProductValidator.php:256
994
  msgid "Product excluded because of tags."
995
  msgstr ""
996
 
997
- #: includes/ProductSync/ProductValidator.php:267
998
  msgid "Sync disabled in product field."
999
  msgstr ""
1000
 
1001
- #: includes/ProductSync/ProductValidator.php:277
1002
  msgid "Product excluded by wc_facebook_should_sync_product filter."
1003
  msgstr ""
1004
 
1005
- #: includes/ProductSync/ProductValidator.php:313
1006
  msgid "If product is not simple, variable or variation it must have a price."
1007
  msgstr ""
1008
 
1009
- #: includes/ProductSync/ProductValidator.php:339
1010
  msgid "Product description is all capital letters. Please change the description to sentence case in order to allow synchronization of your product."
1011
  msgstr ""
1012
 
1013
- #: includes/ProductSync/ProductValidator.php:342
1014
  msgid "Product description is too long. Maximum allowed length is 5000 characters."
1015
  msgstr ""
1016
 
1017
- #: includes/ProductSync/ProductValidator.php:361
1018
  msgid "Product title is all capital letters. Please change the title to sentence case in order to allow synchronization of your product."
1019
  msgstr ""
1020
 
1021
- #: includes/ProductSync/ProductValidator.php:364
1022
  msgid "Product title is too long. Maximum allowed length is 150 characters."
1023
  msgstr ""
1024
 
1025
- #: includes/ProductSync/ProductValidator.php:388
1026
  msgid "Too many attributes selected for product. Use 4 or less."
1027
  msgstr ""
2
  # This file is distributed under the same license as the Facebook for WooCommerce plugin.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Facebook for WooCommerce 3.0.0\n"
6
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/facebook-for-woocommerce\n"
7
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
8
  "Language-Team: LANGUAGE <LL@li.org>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
+ "POT-Creation-Date: 2022-11-17T19:55:50+00:00\n"
13
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
+ "X-Generator: WP-CLI 2.7.0\n"
15
  "X-Domain: facebook-for-woocommerce\n"
16
 
17
  #. Plugin Name of the plugin
18
+ #: class-wc-facebookcommerce.php:701
19
+ #: facebook-commerce.php:200
20
+ #: includes/Admin.php:1468
21
+ #: includes/Admin/Settings.php:93
22
+ #: includes/Admin/Settings.php:160
23
  msgid "Facebook for WooCommerce"
24
  msgstr ""
25
 
32
  msgstr ""
33
 
34
  #. Author of the plugin
35
+ #: includes/Admin.php:1127
36
+ #: includes/Admin/Settings.php:94
37
+ #: includes/Admin/Settings.php:332
38
  msgid "Facebook"
39
  msgstr ""
40
 
42
  msgid "https://www.facebook.com/"
43
  msgstr ""
44
 
45
+ #: class-wc-facebookcommerce.php:323
46
+ #: includes/Admin/Settings.php:123
47
+ #: includes/Admin/Settings.php:124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  msgid "Facebook Product Sets"
49
  msgstr ""
50
 
51
+ #: class-wc-facebookcommerce.php:324
52
  msgid "Facebook Product Set"
53
  msgstr ""
54
 
55
  #. translators: Edit item label
56
+ #: class-wc-facebookcommerce.php:332
57
  msgid "Edit %s"
58
  msgstr ""
59
 
60
  #. translators: Add new label
61
+ #: class-wc-facebookcommerce.php:334
62
  msgid "Add new %s"
63
  msgstr ""
64
 
65
  #. translators: No items found text
66
+ #: class-wc-facebookcommerce.php:337
67
  msgid "No %s found."
68
  msgstr ""
69
 
70
  #. translators: Search label
71
+ #: class-wc-facebookcommerce.php:339
72
  msgid "Search %s."
73
  msgstr ""
74
 
75
  #. translators: Text label
76
+ #: class-wc-facebookcommerce.php:341
77
  msgid "Separate %s with commas"
78
  msgstr ""
79
 
80
  #. translators: Text label
81
+ #: class-wc-facebookcommerce.php:343
82
  msgid "Choose from the most used %s"
83
  msgstr ""
84
 
85
+ #: class-wc-facebookcommerce.php:478
86
  msgid "Cannot create the API instance because the access token is missing."
87
  msgstr ""
88
 
89
+ #: facebook-commerce.php:204
90
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
91
  msgstr ""
92
 
93
+ #. translators: %1$s is referring to facebook product group id.
94
+ #: facebook-commerce.php:1248
95
  msgid "Nothing to update for product group for %1$s"
96
  msgstr ""
97
 
98
+ #: facebook-commerce.php:1909
 
 
 
 
 
99
  msgid "Your connection has expired."
100
  msgstr ""
101
 
102
+ #: facebook-commerce.php:1909
103
  msgid "Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook."
104
  msgstr ""
105
 
106
  #. translators: Placeholders %s - error message
107
+ #: facebook-commerce.php:1916
108
  msgid "There was an error trying to sync the products to Facebook. %s"
109
  msgstr ""
110
 
111
+ #: facebook-commerce.php:1935
112
  msgid "Product sync is disabled."
113
  msgstr ""
114
 
115
+ #: facebook-commerce.php:1940
116
  msgid "The plugin is not configured or the Catalog ID is missing."
117
  msgstr ""
118
 
119
+ #: facebook-commerce.php:1960
120
  msgid "A product sync is in progress. Please wait until the sync finishes before starting a new one."
121
  msgstr ""
122
 
123
+ #: facebook-commerce.php:1971
124
  msgid "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again."
125
  msgstr ""
126
 
127
+ #: facebook-commerce.php:2343
128
  msgid "Hi! We're here to answer any questions you may have."
129
  msgstr ""
130
 
131
+ #: facebook-commerce.php:2653
132
  msgid "Facebook for WooCommerce error:"
133
  msgstr ""
134
 
135
  #. translators: Placeholders %1$s - original error message from Facebook API
136
+ #: facebook-commerce.php:2872
137
  msgid "There was an issue connecting to the Facebook API: %s"
138
  msgstr ""
139
 
140
  #. translators: %1$s - plugin name, %2$s - minimum WordPress version required, %3$s - update WordPress link open, %4$s - update WordPress link close
141
+ #: facebook-for-woocommerce.php:222
142
  msgid "%1$s requires WordPress version %2$s or higher. Please %3$supdate WordPress &raquo;%4$s"
143
  msgstr ""
144
 
145
  #. translators: %1$s - Plugin Name, %2$s - activate WooCommerce link open, %3$s - activate WooCommerce link close.
146
+ #: facebook-for-woocommerce.php:248
147
  msgid "%1$s requires WooCommerce to be activated. Please %2$sactivate WooCommerce%3$s."
148
  msgstr ""
149
 
150
  #. translators: %1$s - Plugin Name, %2$s - install WooCommerce link open, %3$s - install WooCommerce link close.
151
+ #: facebook-for-woocommerce.php:265
152
  msgid "%1$s requires WooCommerce to be installed and activated. Please %2$sinstall WooCommerce%3$s."
153
  msgstr ""
154
 
155
  #. translators: %1$s - Plugin Name, %2$s - minimum WooCommerce version, %3$s - update WooCommerce link open, %4$s - update WooCommerce link close, %5$s - download minimum WooCommerce link open, %6$s - download minimum WooCommerce link close.
156
+ #: facebook-for-woocommerce.php:285
157
  msgid "%1$s requires WooCommerce version %2$s or higher. Please %3$supdate WooCommerce%4$s to the latest version, or %5$sdownload the minimum required version &raquo;%6$s"
158
  msgstr ""
159
 
160
+ #: includes/Admin.php:125
161
  msgid "The name is how it appears on Facebook Catalog."
162
  msgstr ""
163
 
164
+ #: includes/Admin.php:165
165
  msgid "Search main categories..."
166
  msgstr ""
167
 
168
+ #: includes/Admin.php:166
169
  msgid "Choose a main category"
170
  msgstr ""
171
 
172
+ #: includes/Admin.php:167
173
  msgid "Choose a category"
174
  msgstr ""
175
 
176
+ #: includes/Admin.php:202
177
  msgid "You have selected one or more categories currently excluded from the Facebook sync. Products belonging to the excluded categories will not be added to your Facebook Product Set."
178
  msgstr ""
179
 
180
+ #: includes/Admin.php:240
181
  msgid "Please enter a Google product category and at least one sub-category to sell this product on Instagram."
182
  msgstr ""
183
 
184
+ #: includes/Admin.php:296
185
  msgid "To sell this product on Instagram, please ensure it meets the following requirements:"
186
  msgstr ""
187
 
188
+ #: includes/Admin.php:298
189
  msgid "Has a price defined"
190
  msgstr ""
191
 
192
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag
193
+ #: includes/Admin.php:304
194
  msgid "Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab"
195
  msgstr ""
196
 
197
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag
198
+ #: includes/Admin.php:316
199
  msgid "Has the %1$sFacebook Sync%2$s setting set to \"Sync and show\" or \"Sync and hide\""
200
  msgstr ""
201
 
202
  #. translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a> link tag
203
+ #: includes/Admin.php:362
204
  msgid "You're removing a product from the Facebook sync that is currently listed in your %1$sFacebook catalog%2$s. Would you like to delete the product from the Facebook catalog as well?"
205
  msgstr ""
206
 
207
+ #: includes/Admin.php:422
208
+ #: includes/Admin.php:1170
209
+ #: includes/Admin.php:1307
210
  msgid "Facebook sync"
211
  msgstr ""
212
 
213
+ #: includes/Admin.php:445
214
+ #: includes/Admin.php:473
215
  msgid "Sync and show"
216
  msgstr ""
217
 
218
+ #: includes/Admin.php:447
219
+ #: includes/Admin.php:474
220
  msgid "Sync and hide"
221
  msgstr ""
222
 
223
+ #: includes/Admin.php:450
224
+ #: includes/Admin.php:475
225
+ #: includes/Admin.php:1174
226
+ #: includes/Admin.php:1311
227
  msgid "Do not sync"
228
  msgstr ""
229
 
230
+ #: includes/Admin.php:472
231
  msgid "Filter by Facebook sync setting"
232
  msgstr ""
233
 
234
+ #: includes/Admin.php:831
235
  msgid "Include in Facebook sync"
236
  msgstr ""
237
 
238
+ #: includes/Admin.php:832
239
  msgid "Exclude from Facebook sync"
240
  msgstr ""
241
 
242
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - <a> tag
243
+ #: includes/Admin.php:991
244
  msgid "%1$sHeads up!%2$s If this product was previously visible in Facebook, you may need to delete it from the %3$sFacebook catalog%4$s to completely hide it from customer view."
245
  msgid_plural "%1$sHeads up!%2$s If these products were previously visible in Facebook, you may need to delete them from the %3$sFacebook catalog%4$s to completely hide them from customer view."
246
  msgstr[0] ""
247
  msgstr[1] ""
248
 
249
+ #: includes/Admin.php:998
250
  msgid "Don't show this notice again"
251
  msgstr ""
252
 
253
  #. translators: Placeholders: %1$s - number of affected products, %2$s opening HTML <a> tag, %3$s - closing HTML </a> tag, %4$s - opening HTML <a> tag, %5$s - closing HTML </a> tag
254
+ #: includes/Admin.php:1032
255
  msgid "%2$s%1$s product%3$s or some of its variations could not be updated to show in the Facebook catalog — %4$sFacebook Commerce Policies%5$s prohibit selling some product types (like virtual products). You may still advertise Virtual products on Facebook."
256
  msgid_plural "%2$s%1$s products%3$s or some of their variations could not be updated to show in the Facebook catalog — %4$sFacebook Commerce Policies%5$s prohibit selling some product types (like virtual products). You may still advertise Virtual products on Facebook."
257
  msgstr[0] ""
258
  msgstr[1] ""
259
 
260
  #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a> tag
261
+ #: includes/Admin.php:1098
262
  msgid "%1$sHeads up!%2$s Facebook's %3$sCommerce Policies%4$s do not support selling virtual products, so we have hidden your synced Virtual products in your Facebook catalog. You may still advertise Virtual products on Facebook."
263
  msgstr ""
264
 
265
+ #: includes/Admin.php:1172
266
+ #: includes/Admin.php:1309
267
  msgid "Sync and show in catalog"
268
  msgstr ""
269
 
270
+ #: includes/Admin.php:1173
271
+ #: includes/Admin.php:1310
272
  msgid "Sync and hide in catalog"
273
  msgstr ""
274
 
275
+ #: includes/Admin.php:1183
276
+ #: includes/Admin.php:1323
277
  msgid "Facebook Description"
278
  msgstr ""
279
 
280
+ #: includes/Admin.php:1185
281
+ #: includes/Admin.php:1325
282
  msgid "Custom (plain-text only) description for product on Facebook. If blank, product description will be used. If product description is blank, shortname will be used."
283
  msgstr ""
284
 
285
+ #: includes/Admin.php:1196
286
+ #: includes/Admin.php:1338
287
  msgid "Facebook Product Image"
288
  msgstr ""
289
 
290
+ #: includes/Admin.php:1198
291
+ #: includes/Admin.php:1340
292
  msgid "Choose the product image that should be synced to the Facebook catalog for this product. If using a custom image, please enter an absolute URL (e.g. https://domain.com/image.jpg)."
293
  msgstr ""
294
 
295
+ #: includes/Admin.php:1200
296
  msgid "Use WooCommerce image"
297
  msgstr ""
298
 
299
+ #: includes/Admin.php:1201
300
+ #: includes/Admin.php:1344
301
  msgid "Use custom image"
302
  msgstr ""
303
 
304
+ #: includes/Admin.php:1212
305
+ #: includes/Admin.php:1356
306
  msgid "Custom Image URL"
307
  msgstr ""
308
 
309
  #. translators: Placeholders %1$s - WC currency symbol
310
+ #: includes/Admin.php:1223
311
+ #: includes/Admin.php:1369
 
312
  msgid "Facebook Price (%1$s)"
313
  msgstr ""
314
 
315
+ #: includes/Admin.php:1227
316
+ #: includes/Admin.php:1373
317
  msgid "Custom price for product on Facebook. Please enter in monetary decimal (.) format without thousand separators and currency symbols. If blank, product price will be used."
318
  msgstr ""
319
 
320
+ #: includes/Admin.php:1342
321
  msgid "Use variation image"
322
  msgstr ""
323
 
324
+ #: includes/Admin.php:1343
325
  msgid "Use parent image"
326
  msgstr ""
327
 
328
+ #: includes/Admin.php:1470
329
  msgid "Close modal panel"
330
  msgstr ""
331
 
332
+ #: includes/Admin/Abstract_Settings_Screen.php:74
333
  msgid "Save changes"
334
  msgstr ""
335
 
337
  msgid "Show advanced options"
338
  msgstr ""
339
 
340
+ #: includes/Admin/Products.php:96
 
 
 
 
 
 
 
 
 
 
 
 
341
  msgid "Select values for enhanced attributes for this product"
342
  msgstr ""
343
 
344
+ #: includes/Admin/Products.php:112
345
+ #: includes/Admin/Product_Categories.php:362
346
  msgid "Category Specific Attributes"
347
  msgstr ""
348
 
349
+ #: includes/Admin/Products.php:130
350
  msgid "Google product category"
351
  msgstr ""
352
 
353
+ #: includes/Admin/Products.php:131
354
  msgid "Choose the Google product category and (optionally) sub-categories associated with this product."
355
  msgstr ""
356
 
357
+ #: includes/Admin/Products.php:194
358
  msgid "Sell on Instagram"
359
  msgstr ""
360
 
361
+ #: includes/Admin/Products.php:196
362
  msgid "Enable to sell this product on Instagram. Products that are hidden in the Facebook catalog can be synced, but won’t be available for purchase."
363
  msgstr ""
364
 
365
+ #: includes/Admin/Products.php:206
366
  msgid "This product does not meet the requirements to sell on Instagram."
367
  msgstr ""
368
 
369
+ #: includes/Admin/Products.php:207
370
  msgid "Click here to learn more."
371
  msgstr ""
372
 
373
  #. translators: Placeholders %1$s - strong opening tag, %2$s - strong closing tag
374
+ #: includes/Admin/Products.php:216
375
  msgid "To sell this product on Instagram, at least one variation must be synced to Facebook. You can control variation sync on the %1$sVariations%2$s tab with the %1$sFacebook Sync%2$s setting."
376
  msgstr ""
377
 
378
+ #: includes/Admin/Product_Categories.php:99
379
+ #: includes/Admin/Settings_Screens/Product_Sync.php:114
380
  msgid "Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?"
381
  msgstr ""
382
 
383
+ #: includes/Admin/Product_Categories.php:116
384
+ #: includes/Admin/Settings_Screens/Product_Sync.php:148
385
+ #: includes/AJAX.php:349
386
+ #: includes/AJAX.php:417
387
+ #: includes/AJAX.php:483
388
  msgid "Cancel"
389
  msgstr ""
390
 
391
+ #: includes/Admin/Product_Categories.php:120
392
+ #: includes/Admin/Settings_Screens/Product_Sync.php:152
393
  msgid "Update default Google product category"
394
  msgstr ""
395
 
396
+ #: includes/Admin/Product_Categories.php:158
397
  msgid "Facebook catalogs now support category specific fields, to make best use of them you need to select a category. WooCommerce uses the google taxonomy as it is the most widely accepted form of categorisation."
398
  msgstr ""
399
 
400
+ #: includes/Admin/Product_Categories.php:206
401
  msgid "Choose a default Google product category for products in this category. Products need at least two category levels defined for tax to be correctly applied."
402
  msgstr ""
403
 
404
+ #: includes/Admin/Product_Categories.php:222
405
+ #: includes/Admin/Settings_Screens/Product_Sync.php:314
406
  msgid "Default Google product category"
407
  msgstr ""
408
 
409
+ #: includes/Admin/Product_Categories.php:346
410
  msgid "Select default values for enhanced attributes within this category"
411
  msgstr ""
412
 
413
+ #: includes/Admin/Product_Sets.php:139
414
  msgid "WC Product Categories"
415
  msgstr ""
416
 
417
+ #: includes/Admin/Product_Sets.php:169
418
  msgid "Map Facebook Product Set to WC Product Categories"
419
  msgstr ""
420
 
421
+ #: includes/Admin/Product_Sync_Meta_Box.php:38
422
  msgid "Facebook Product Sync"
423
  msgstr ""
424
 
425
+ #: includes/Admin/Product_Sync_Meta_Box.php:79
426
  msgid "Facebook ID:"
427
  msgstr ""
428
 
429
+ #: includes/Admin/Product_Sync_Meta_Box.php:90
430
  msgid "Variant IDs:"
431
  msgstr ""
432
 
433
+ #: includes/Admin/Product_Sync_Meta_Box.php:122
434
  msgid "Reset Facebook metadata"
435
  msgstr ""
436
 
437
+ #: includes/Admin/Product_Sync_Meta_Box.php:127
438
  msgid "Delete product(s) on Facebook"
439
  msgstr ""
440
 
441
+ #: includes/Admin/Product_Sync_Meta_Box.php:139
442
  msgid "This product is not yet synced to Facebook."
443
  msgstr ""
444
 
445
+ #: includes/Admin/Settings.php:165
446
  #: includes/Admin/Settings_Screens/Connection.php:35
447
  #: includes/Admin/Settings_Screens/Connection.php:36
448
  msgid "Connection"
449
  msgstr ""
450
 
451
+ #: includes/Admin/Settings.php:168
452
  #: includes/Admin/Settings_Screens/Messenger.php:37
453
  #: includes/Admin/Settings_Screens/Messenger.php:38
454
+ #: includes/Admin/Settings_Screens/Messenger.php:131
455
  msgid "Messenger"
456
  msgstr ""
457
 
458
+ #: includes/Admin/Settings.php:171
459
+ #: includes/Admin/Settings_Screens/Product_Sync.php:42
460
  #: includes/Admin/Settings_Screens/Product_Sync.php:43
461
+ #: includes/Admin/Settings_Screens/Product_Sync.php:171
462
+ #: includes/Admin/Settings_Screens/Product_Sync.php:261
 
463
  msgid "Product sync"
464
  msgstr ""
465
 
466
+ #: includes/Admin/Settings.php:174
467
+ #: includes/Admin/Settings_Screens/Advertise.php:34
468
+ #: includes/Admin/Settings_Screens/Advertise.php:35
469
  msgid "Advertise"
470
  msgstr ""
471
 
472
+ #: includes/Admin/Settings.php:239
473
  msgid "You do not have permission to save these settings."
474
  msgstr ""
475
 
476
+ #: includes/Admin/Settings.php:244
477
  msgid "Your settings have been saved."
478
  msgstr ""
479
 
480
  #. translators: Placeholders: %s - user-friendly error message
481
+ #: includes/Admin/Settings.php:249
482
  msgid "Your settings could not be saved. %s"
483
  msgstr ""
484
 
485
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag
486
+ #: includes/Admin/Settings_Screens/Advertise.php:193
487
  msgid "Please %1$sconnect your store%2$s to Facebook to create ads."
488
  msgstr ""
489
 
490
+ #: includes/Admin/Settings_Screens/Advertise.php:211
491
  msgid "If you are connected to Facebook but cannot display ads, please contact Facebook support."
492
  msgstr ""
493
 
524
  msgid "Commerce Merchant Settings ID"
525
  msgstr ""
526
 
527
+ #: includes/Admin/Settings_Screens/Connection.php:253
528
  msgid "Reach the Right People and Sell More Online"
529
  msgstr ""
530
 
531
+ #: includes/Admin/Settings_Screens/Connection.php:255
532
  msgid "Grow your business on Facebook"
533
  msgstr ""
534
 
535
+ #: includes/Admin/Settings_Screens/Connection.php:258
536
  msgid "Use this WooCommerce and Facebook integration to:"
537
  msgstr ""
538
 
539
+ #: includes/Admin/Settings_Screens/Connection.php:260
540
  msgid "Create an ad in a few steps"
541
  msgstr ""
542
 
543
+ #: includes/Admin/Settings_Screens/Connection.php:261
544
  msgid "Use built-in best practices for online sales"
545
  msgstr ""
546
 
547
+ #: includes/Admin/Settings_Screens/Connection.php:262
548
  msgid "Get reporting on sales and revenue"
549
  msgstr ""
550
 
551
+ #: includes/Admin/Settings_Screens/Connection.php:285
552
  msgid "Manage Connection"
553
  msgstr ""
554
 
555
+ #: includes/Admin/Settings_Screens/Connection.php:289
556
  msgid "Disconnect"
557
  msgstr ""
558
 
559
+ #: includes/Admin/Settings_Screens/Connection.php:295
560
  msgid "Get Started"
561
  msgstr ""
562
 
563
+ #: includes/Admin/Settings_Screens/Connection.php:320
564
  msgid "Debug"
565
  msgstr ""
566
 
567
+ #: includes/Admin/Settings_Screens/Connection.php:326
568
  msgid "Enable debug mode"
569
  msgstr ""
570
 
571
+ #: includes/Admin/Settings_Screens/Connection.php:328
572
  msgid "Log plugin events for debugging."
573
  msgstr ""
574
 
575
+ #: includes/Admin/Settings_Screens/Connection.php:329
576
  msgid "Only enable this if you are experiencing problems with the plugin."
577
  msgstr ""
578
 
579
+ #: includes/Admin/Settings_Screens/Connection.php:335
580
  msgid "Experimental! Enable new style feed generation"
581
  msgstr ""
582
 
583
+ #: includes/Admin/Settings_Screens/Connection.php:337
584
  msgid "Use new, memory improved, feed generation process."
585
  msgstr ""
586
 
587
+ #: includes/Admin/Settings_Screens/Connection.php:338
588
  msgid "Experimental feature. Only enable this if you are experiencing problems with feed generation. This is an experimental feature in testing phase."
589
  msgstr ""
590
 
591
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
592
+ #: includes/Admin/Settings_Screens/Messenger.php:108
593
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
594
  msgstr ""
595
 
596
+ #: includes/Admin/Settings_Screens/Messenger.php:136
597
  msgid "Enable Messenger"
598
  msgstr ""
599
 
600
+ #: includes/Admin/Settings_Screens/Messenger.php:138
601
  msgid "Enable and customize Facebook Messenger on your store"
602
  msgstr ""
603
 
604
+ #: includes/Admin/Settings_Screens/Messenger.php:146
605
  msgid "Language"
606
  msgstr ""
607
 
608
+ #: includes/Admin/Settings_Screens/Messenger.php:150
609
  msgid "Greeting & Colors"
610
  msgstr ""
611
 
612
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
613
+ #: includes/Admin/Settings_Screens/Messenger.php:169
614
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger."
615
  msgstr ""
616
 
617
+ #: includes/Admin/Settings_Screens/Messenger.php:227
618
  msgid "Please try again."
619
  msgstr ""
620
 
621
+ #: includes/Admin/Settings_Screens/Product_Sets.php:31
622
+ #: includes/Admin/Settings_Screens/Product_Sets.php:32
623
  msgid "Product sets"
624
  msgstr ""
625
 
626
  #. translators: Placeholders: {count} number of remaining items
627
+ #: includes/Admin/Settings_Screens/Product_Sync.php:76
628
  msgid "{count} item remaining."
629
  msgid_plural "{count} items remaining."
630
  msgstr[0] ""
631
  msgstr[1] ""
632
 
633
  #. translators: Placeholders %s - html code for a spinner icon
634
+ #: includes/Admin/Settings_Screens/Product_Sync.php:91
635
  msgid "Your products will now be resynced to Facebook, this may take some time."
636
  msgstr ""
637
 
638
+ #: includes/Admin/Settings_Screens/Product_Sync.php:92
639
  msgid ""
640
  "Facebook for WooCommerce automatically syncs your products on create/update. Are you sure you want to force product resync?\n"
641
  "\n"
642
  "This will query all published products and may take some time. You only need to do this if your products are out of sync or some of your products did not sync."
643
  msgstr ""
644
 
645
+ #: includes/Admin/Settings_Screens/Product_Sync.php:93
646
  msgid "Your products are syncing - you may safely leave this page %s"
647
  msgstr ""
648
 
649
+ #: includes/Admin/Settings_Screens/Product_Sync.php:96
650
  msgid "There was an error trying to sync the products to Facebook."
651
  msgstr ""
652
 
653
+ #: includes/Admin/Settings_Screens/Product_Sync.php:97
654
  msgid "Something went wrong while uploading the product information, please try again."
655
  msgstr ""
656
 
657
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
658
+ #: includes/Admin/Settings_Screens/Product_Sync.php:128
659
  msgid "Products and categories that inherit this global setting (they do not have a specific Google product category set) will use the new default immediately. %1$sIf you have cleared the Google Product Category%2$s, items inheriting the default will not be available for Instagram checkout. Are you sure you want to proceed?"
660
  msgstr ""
661
 
662
+ #: includes/Admin/Settings_Screens/Product_Sync.php:179
663
  msgid "Sync products"
664
  msgstr ""
665
 
666
+ #: includes/Admin/Settings_Screens/Product_Sync.php:265
667
  msgid "Enable product sync"
668
  msgstr ""
669
 
670
+ #: includes/Admin/Settings_Screens/Product_Sync.php:273
671
  msgid "Exclude categories from sync"
672
  msgstr ""
673
 
674
+ #: includes/Admin/Settings_Screens/Product_Sync.php:277
675
  msgid "Products in one or more of these categories will not sync to Facebook."
676
  msgstr ""
677
 
678
+ #: includes/Admin/Settings_Screens/Product_Sync.php:281
679
  msgid "Search for a product category&hellip;"
680
  msgstr ""
681
 
682
+ #: includes/Admin/Settings_Screens/Product_Sync.php:287
683
  msgid "Exclude tags from sync"
684
  msgstr ""
685
 
686
+ #: includes/Admin/Settings_Screens/Product_Sync.php:291
687
  msgid "Products with one or more of these tags will not sync to Facebook."
688
  msgstr ""
689
 
690
+ #: includes/Admin/Settings_Screens/Product_Sync.php:295
691
  msgid "Search for a product tag&hellip;"
692
  msgstr ""
693
 
694
+ #: includes/Admin/Settings_Screens/Product_Sync.php:301
695
  msgid "Product description sync"
696
  msgstr ""
697
 
698
+ #: includes/Admin/Settings_Screens/Product_Sync.php:304
699
  msgid "Choose which product description to display in the Facebook catalog."
700
  msgstr ""
701
 
702
+ #: includes/Admin/Settings_Screens/Product_Sync.php:307
703
  msgid "Standard description"
704
  msgstr ""
705
 
706
+ #: includes/Admin/Settings_Screens/Product_Sync.php:308
707
  msgid "Short description"
708
  msgstr ""
709
 
710
+ #: includes/Admin/Settings_Screens/Product_Sync.php:315
711
  msgid "Choose a default Google product category for your products. Defaults can also be set for product categories. Products need at least two category levels defined for tax to be correctly applied."
712
  msgstr ""
713
 
714
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
715
+ #: includes/Admin/Settings_Screens/Product_Sync.php:358
716
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage product sync."
717
  msgstr ""
718
 
728
  msgid "20 minutes"
729
  msgstr ""
730
 
731
+ #: includes/AJAX.php:83
732
  msgid "Invalid nonce."
733
  msgstr ""
734
 
735
+ #: includes/AJAX.php:90
736
  msgid "Order ID is required."
737
  msgstr ""
738
 
739
+ #: includes/AJAX.php:94
740
  msgid "Cancel reason is required."
741
  msgstr ""
742
 
743
+ #: includes/AJAX.php:100
744
  msgid "A valid Order ID is required."
745
  msgstr ""
746
 
747
+ #: includes/AJAX.php:183
748
  msgid "Order ID is required"
749
  msgstr ""
750
 
751
+ #: includes/AJAX.php:187
752
  msgid "Tracking number is required"
753
  msgstr ""
754
 
755
+ #: includes/AJAX.php:191
756
  msgid "Carrier code is required"
757
  msgstr ""
758
 
759
+ #: includes/AJAX.php:197
760
  msgid "Order not found"
761
  msgstr ""
762
 
763
+ #: includes/AJAX.php:221
764
  msgid "Full product sync disabled by filter."
765
  msgstr ""
766
 
767
+ #: includes/AJAX.php:344
768
+ #: includes/AJAX.php:412
769
  msgid "Go to Settings"
770
  msgstr ""
771
 
772
  #. translators: Placeholder %s - <br/> tag
773
+ #: includes/AJAX.php:358
774
  msgid "This product belongs to a category or tag that is excluded from the Facebook catalog sync. It will not sync to Facebook. %sTo sync this product to Facebook, click Go to Settings and remove the category or tag exclusion or click Cancel and update the product's category / tag assignments."
775
  msgstr ""
776
 
777
+ #: includes/AJAX.php:424
778
  msgid "One or more of the selected products belongs to a category or tag that is excluded from the Facebook catalog sync. To sync these products to Facebook, please remove the category or tag exclusion from the plugin settings."
779
  msgstr ""
780
 
781
+ #: includes/AJAX.php:477
782
  msgid "Exclude Products"
783
  msgstr ""
784
 
785
  #. translators: Placeholder %s - <br/> tags
786
+ #: includes/AJAX.php:492
787
  msgid "The categories and/or tags that you have selected to exclude from sync contain products that are currently synced to Facebook.%sTo exclude these products from the Facebook sync, click Exclude Products. To review the category / tag exclusion settings, click Cancel."
788
  msgstr ""
789
 
790
+ #: includes/Commerce/Orders.php:428
791
  msgid "Order %1$s paid in %2$s"
792
  msgstr ""
793
 
794
+ #: includes/Commerce/Orders.php:580
795
+ #: includes/Commerce/Orders.php:815
796
  msgid "Remote ID not found."
797
  msgstr ""
798
 
799
+ #: includes/Commerce/Orders.php:587
800
  msgid "%s is not a valid shipping carrier code."
801
  msgstr ""
802
 
803
+ #: includes/Commerce/Orders.php:605
804
+ #: includes/Commerce/Orders.php:782
805
  msgid "No valid Facebook products were found."
806
  msgstr ""
807
 
808
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
809
+ #: includes/Commerce/Orders.php:623
810
  msgid "%s order fulfilled."
811
  msgstr ""
812
 
813
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
814
+ #: includes/Commerce/Orders.php:633
815
  msgid "%1$s order could not be fulfilled. %2$s"
816
  msgstr ""
817
 
818
+ #: includes/Commerce/Orders.php:677
819
  msgid "Parent order not found."
820
  msgstr ""
821
 
822
+ #: includes/Commerce/Orders.php:683
823
  msgid "Remote ID for parent order not found."
824
  msgstr ""
825
 
826
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
827
+ #: includes/Commerce/Orders.php:714
828
  msgid "Order refunded on %s."
829
  msgstr ""
830
 
831
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
832
+ #: includes/Commerce/Orders.php:726
833
  msgid "Could not refund %1$s order: %2$s"
834
  msgstr ""
835
 
836
  #. translators: Placeholder: %s - sales channel name, like Facebook or Instagram
837
+ #: includes/Commerce/Orders.php:823
838
  msgid "%s order cancelled."
839
  msgstr ""
840
 
841
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message
842
+ #: includes/Commerce/Orders.php:833
843
  msgid "%1$s order could not be cancelled. %2$s"
844
  msgstr ""
845
 
846
+ #: includes/Commerce/Orders.php:855
847
  msgid "Customer requested cancellation"
848
  msgstr ""
849
 
850
+ #: includes/Commerce/Orders.php:856
851
  msgid "Product(s) are out of stock"
852
  msgstr ""
853
 
854
+ #: includes/Commerce/Orders.php:857
855
  msgid "Customer address is invalid"
856
  msgstr ""
857
 
858
+ #: includes/Commerce/Orders.php:858
859
  msgid "Suspicious order"
860
  msgstr ""
861
 
862
+ #: includes/Commerce/Orders.php:859
863
  msgid "Other"
864
  msgstr ""
865
 
866
+ #: includes/fbinfobanner.php:176
 
 
 
 
867
  msgid "Click and redirect."
868
  msgstr ""
869
 
870
+ #: includes/fbinfobanner.php:177
871
  msgid "Dismiss this notice."
872
  msgstr ""
873
 
874
+ #: includes/fbinfobanner.php:177
875
  msgid "Dismiss"
876
  msgstr ""
877
 
878
+ #: includes/fbproductfeed.php:202
879
  msgid "Could not create product catalog feed directory"
880
  msgstr ""
881
 
882
+ #: includes/fbproductfeed.php:306
883
  msgid "Could not open the product catalog temporary feed file for writing"
884
  msgstr ""
885
 
886
+ #: includes/fbproductfeed.php:313
887
  msgid "Could not open the product catalog feed file for writing"
888
  msgstr ""
889
 
890
+ #: includes/fbproductfeed.php:377
891
  msgid "Could not rename the product catalog feed file"
892
  msgstr ""
893
 
894
+ #: includes/fbwpml.php:111
895
  msgid "Facebook Visibility"
896
  msgstr ""
897
 
898
+ #: includes/fbwpml.php:114
899
  msgid "WooCommerce Products with languages that are selected here will be visible to customers who see your Facebook Shop."
900
  msgstr ""
901
 
902
+ #: includes/fbwpml.php:131
903
  msgid "Saved. An automated sync from Facebook will run every hour to update the catalog with any changes you've made."
904
  msgstr ""
905
 
906
+ #: includes/Framework/Lifecycle.php:261
907
+ msgid "Awesome"
908
+ msgstr ""
909
+
910
+ #: includes/Framework/Lifecycle.php:262
911
+ msgid "Fantastic"
912
+ msgstr ""
913
+
914
+ #: includes/Framework/Lifecycle.php:263
915
+ msgid "Cowabunga"
916
+ msgstr ""
917
+
918
+ #: includes/Framework/Lifecycle.php:264
919
+ msgid "Congratulations"
920
+ msgstr ""
921
+
922
+ #: includes/Framework/Lifecycle.php:265
923
+ msgid "Hot dog"
924
+ msgstr ""
925
+
926
+ #. translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a> tag, %4$s - <a> tag, %5$s - </a> tag
927
+ #: includes/Framework/Lifecycle.php:270
928
+ msgid "Are you having a great experience with %1$s so far? Please consider %2$sleaving a review%3$s! If things aren't going quite as expected, we're happy to help -- please %4$sreach out to our support team%5$s."
929
+ msgstr ""
930
+
931
+ #. translators: Placeholders: %s - plugin name
932
+ #: includes/Framework/Plugin.php:210
933
+ msgid "You cannot clone instances of %s."
934
+ msgstr ""
935
+
936
+ #. translators: Placeholders: %s - plugin name
937
+ #: includes/Framework/Plugin.php:221
938
+ msgid "You cannot unserialize instances of %s."
939
+ msgstr ""
940
+
941
+ #. translators: Placeholders: %1$s - plugin name, %2$s - WooCommerce version number, %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag
942
+ #: includes/Framework/Plugin.php:359
943
+ msgid "Heads up! %1$s will soon discontinue support for WooCommerce %2$s. Please %3$supdate WooCommerce%4$s to take advantage of the latest updates and features."
944
+ msgstr ""
945
+
946
+ #. translators: Docs as in Documentation
947
+ #: includes/Framework/Plugin.php:390
948
+ msgid "Docs"
949
+ msgstr ""
950
+
951
+ #: includes/Framework/Plugin.php:395
952
+ msgctxt "noun"
953
+ msgid "Support"
954
+ msgstr ""
955
+
956
+ #: includes/Framework/Plugin.php:400
957
+ msgctxt "verb"
958
+ msgid "Review"
959
+ msgstr ""
960
+
961
+ #: includes/Framework/Plugin.php:472
962
+ msgid "%1$s - A minimum of %2$s is required."
963
+ msgstr ""
964
+
965
+ #: includes/Framework/Plugin.php:481
966
+ msgid "Set as %1$s - %2$s is required."
967
+ msgstr ""
968
+
969
+ #: includes/Framework/Plugin.php:657
970
+ msgid "Configure"
971
+ msgstr ""
972
+
973
+ #. translators: Placeholders: %1$s - plugin name, %2$s - a PHP extension/comma-separated list of PHP extensions
974
+ #: includes/Framework/Plugin/Dependencies.php:120
975
+ msgid "%1$s requires the %2$s PHP extension to function. Contact your host or server administrator to install and configure the missing extension."
976
+ msgid_plural "%1$s requires the following PHP extensions to function: %2$s. Contact your host or server administrator to install and configure the missing extensions."
977
+ msgstr[0] ""
978
+ msgstr[1] ""
979
+
980
+ #. translators: Placeholders: %1$s - plugin name, %2$s - a PHP function/comma-separated list of PHP functions
981
+ #: includes/Framework/Plugin/Dependencies.php:144
982
+ msgid "%1$s requires the %2$s PHP function to exist. Contact your host or server administrator to install and configure the missing function."
983
+ msgid_plural "%1$s requires the following PHP functions to exist: %2$s. Contact your host or server administrator to install and configure the missing functions."
984
+ msgstr[0] ""
985
+ msgstr[1] ""
986
+
987
+ #: includes/Framework/Plugin/Dependencies.php:178
988
+ msgid "%s or higher"
989
+ msgstr ""
990
+
991
+ #: includes/Framework/Plugin/Dependencies.php:185
992
+ msgid "Please contact your hosting provider or server administrator to configure these settings."
993
+ msgstr ""
994
+
995
+ #. translators: Placeholders: %1$s - <strong>, %2$s - </strong>
996
+ #: includes/Framework/Plugin/Dependencies.php:203
997
+ msgid ""
998
+ "Hey there! We've noticed that your server is running %1$san outdated version of PHP%2$s, which is the programming language that WooCommerce and its extensions are built on.\n"
999
+ "\t\t\t\t\tThe PHP version that is currently used for your site is no longer maintained, nor %1$sreceives security updates%2$s; newer versions are faster and more secure.\n"
1000
+ "\t\t\t\t\tAs a result, %3$s no longer supports this version and you should upgrade PHP as soon as possible.\n"
1001
+ "\t\t\t\t\tYour hosting provider can do this for you. %4$sHere are some resources to help you upgrade%5$s and to explain PHP versions further."
1002
+ msgstr ""
1003
+
1004
+ #. translators: Placeholders: %s - user-friendly error message
1005
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:613
1006
+ #: includes/Products/Sync/Background.php:59
1007
+ msgid "Job data key \"%s\" not set"
1008
+ msgstr ""
1009
+
1010
+ #. translators: Placeholders: %s - user-friendly error message
1011
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:617
1012
+ #: includes/Products/Sync/Background.php:64
1013
+ msgid "Job data key \"%s\" is not an array"
1014
+ msgstr ""
1015
+
1016
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:976
1017
+ msgid "Background Processing Test"
1018
+ msgstr ""
1019
+
1020
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:977
1021
+ msgid "Run Test"
1022
+ msgstr ""
1023
+
1024
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:978
1025
+ msgid "This tool will test whether your server is capable of processing background jobs."
1026
+ msgstr ""
1027
+
1028
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:996
1029
+ msgid "Success! You should be able to process background jobs."
1030
+ msgstr ""
1031
+
1032
+ #: includes/Framework/Utilities/BackgroundJobHandler.php:999
1033
+ msgid "Could not connect. Please ask your hosting company to ensure your server has loopback connections enabled."
1034
+ msgstr ""
1035
+
1036
+ #: includes/Handlers/Connection.php:284
1037
  msgid "Connection successful!"
1038
  msgstr ""
1039
 
1040
+ #: includes/Handlers/Connection.php:333
1041
  msgid "You do not have permission to uninstall Facebook Business Extension."
1042
  msgstr ""
1043
 
1044
+ #: includes/Handlers/Connection.php:339
1045
  msgid "Disconnection successful."
1046
  msgstr ""
1047
 
1048
  #. translators: Placeholders: %s - Facebook page ID
1049
+ #: includes/Handlers/Connection.php:402
1050
  msgid "Page %s not authorized."
1051
  msgstr ""
1052
 
1053
+ #: includes/Handlers/Connection.php:1280
1054
  msgid "You do not have permission to finish App Store login."
1055
  msgstr ""
1056
 
1059
  msgid "English (United States)"
1060
  msgstr ""
1061
 
1062
+ #. translators: Placeholders: %1$s - <string job ID, %2$s - <strong> error message
1063
+ #: includes/Products/Sync/Background.php:139
1064
+ msgid "There was an error trying sync products using the Catalog Batch API for job %1$s: %2$s"
1065
+ msgstr ""
1066
+
1067
+ #: includes/ProductSync/ProductValidator.php:196
1068
  msgid "Product sync is globally disabled."
1069
  msgstr ""
1070
 
1071
+ #: includes/ProductSync/ProductValidator.php:209
1072
  msgid "Product is not published."
1073
  msgstr ""
1074
 
1075
+ #: includes/ProductSync/ProductValidator.php:220
1076
  msgid "Product must be in stock."
1077
  msgstr ""
1078
 
1079
+ #: includes/ProductSync/ProductValidator.php:235
1080
  msgid "This product cannot be synced to Facebook because it is hidden from your store catalog."
1081
  msgstr ""
1082
 
1083
+ #: includes/ProductSync/ProductValidator.php:250
1084
  msgid "Product excluded because of categories."
1085
  msgstr ""
1086
 
1087
+ #: includes/ProductSync/ProductValidator.php:257
1088
  msgid "Product excluded because of tags."
1089
  msgstr ""
1090
 
1091
+ #: includes/ProductSync/ProductValidator.php:268
1092
  msgid "Sync disabled in product field."
1093
  msgstr ""
1094
 
1095
+ #: includes/ProductSync/ProductValidator.php:278
1096
  msgid "Product excluded by wc_facebook_should_sync_product filter."
1097
  msgstr ""
1098
 
1099
+ #: includes/ProductSync/ProductValidator.php:314
1100
  msgid "If product is not simple, variable or variation it must have a price."
1101
  msgstr ""
1102
 
1103
+ #: includes/ProductSync/ProductValidator.php:340
1104
  msgid "Product description is all capital letters. Please change the description to sentence case in order to allow synchronization of your product."
1105
  msgstr ""
1106
 
1107
+ #: includes/ProductSync/ProductValidator.php:343
1108
  msgid "Product description is too long. Maximum allowed length is 5000 characters."
1109
  msgstr ""
1110
 
1111
+ #: includes/ProductSync/ProductValidator.php:362
1112
  msgid "Product title is all capital letters. Please change the title to sentence case in order to allow synchronization of your product."
1113
  msgstr ""
1114
 
1115
+ #: includes/ProductSync/ProductValidator.php:365
1116
  msgid "Product title is too long. Maximum allowed length is 150 characters."
1117
  msgstr ""
1118
 
1119
+ #: includes/ProductSync/ProductValidator.php:389
1120
  msgid "Too many attributes selected for product. Use 4 or less."
1121
  msgstr ""
includes/AJAX.php CHANGED
@@ -9,10 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
- use SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens\Product_Sync;
15
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
16
 
17
  defined( 'ABSPATH' ) or exit;
18
 
@@ -78,32 +79,32 @@ class AJAX {
78
 
79
  try {
80
 
81
- if ( ! wp_verify_nonce( Framework\SV_WC_Helper::get_posted_value( 'security' ), self::ACTION_CANCEL_ORDER ) ) {
82
- throw new Framework\SV_WC_Plugin_Exception( __( 'Invalid nonce.', 'facebook-for-woocommerce' ) );
83
  }
84
 
85
- $order_id = Framework\SV_WC_Helper::get_posted_value( 'order_id' );
86
- $reason_code = Framework\SV_WC_Helper::get_posted_value( 'reason_code' );
87
 
88
  if ( empty( $order_id ) ) {
89
- throw new Framework\SV_WC_Plugin_Exception( __( 'Order ID is required.', 'facebook-for-woocommerce' ) );
90
  }
91
 
92
  if ( empty( $reason_code ) ) {
93
- throw new Framework\SV_WC_Plugin_Exception( __( 'Cancel reason is required.', 'facebook-for-woocommerce' ) );
94
  }
95
 
96
  $order = wc_get_order( absint( $order_id ) );
97
 
98
  if ( false === $order ) {
99
- throw new Framework\SV_WC_Plugin_Exception( __( 'A valid Order ID is required.', 'facebook-for-woocommerce' ) );
100
  }
101
 
102
  facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->cancel_order( $order, $reason_code );
103
 
104
  wp_send_json_success();
105
 
106
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
107
 
108
  wp_send_json_error( $exception->getMessage() );
109
  }
@@ -121,20 +122,20 @@ class AJAX {
121
 
122
  try {
123
 
124
- if ( ! wp_verify_nonce( Framework\SV_WC_Helper::get_requested_value( 'security' ), self::ACTION_SEARCH_PRODUCT_ATTRIBUTES ) ) {
125
- throw new Framework\SV_WC_Plugin_Exception( 'Invalid nonce' );
126
  }
127
 
128
- $term = Framework\SV_WC_Helper::get_requested_value( 'term' );
129
 
130
  if ( ! $term ) {
131
- throw new Framework\SV_WC_Plugin_Exception( 'A search term is required' );
132
  }
133
 
134
- $product = wc_get_product( (int) Framework\SV_WC_Helper::get_requested_value( 'request_data' ) );
135
 
136
  if ( ! $product instanceof \WC_Product ) {
137
- throw new Framework\SV_WC_Plugin_Exception( 'A valid product ID is required' );
138
  }
139
 
140
  $attributes = Admin\Products::get_available_product_attribute_names( $product );
@@ -152,7 +153,7 @@ class AJAX {
152
 
153
  wp_send_json( $results );
154
 
155
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
156
 
157
  die();
158
  }
@@ -170,37 +171,37 @@ class AJAX {
170
 
171
  try {
172
 
173
- if ( ! wp_verify_nonce( Framework\SV_WC_Helper::get_posted_value( 'nonce' ), self::ACTION_COMPLETE_ORDER ) ) {
174
- throw new Framework\SV_WC_Plugin_Exception( 'Invalid nonce', 403 );
175
  }
176
 
177
- $order_id = (int) Framework\SV_WC_Helper::get_posted_value( 'order_id' );
178
- $tracking_number = wc_clean( Framework\SV_WC_Helper::get_posted_value( 'tracking_number' ) );
179
- $carrier_code = wc_clean( Framework\SV_WC_Helper::get_posted_value( 'carrier_code' ) );
180
 
181
  if ( empty( $order_id ) ) {
182
- throw new Framework\SV_WC_Plugin_Exception( __( 'Order ID is required', 'facebook-for-woocommerce' ) );
183
  }
184
 
185
  if ( empty( $tracking_number ) ) {
186
- throw new Framework\SV_WC_Plugin_Exception( __( 'Tracking number is required', 'facebook-for-woocommerce' ) );
187
  }
188
 
189
  if ( empty( $carrier_code ) ) {
190
- throw new Framework\SV_WC_Plugin_Exception( __( 'Carrier code is required', 'facebook-for-woocommerce' ) );
191
  }
192
 
193
  $order = wc_get_order( $order_id );
194
 
195
  if ( ! $order instanceof \WC_Order ) {
196
- throw new Framework\SV_WC_Plugin_Exception( __( 'Order not found', 'facebook-for-woocommerce' ) );
197
  }
198
 
199
  facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->fulfill_order( $order, $tracking_number, $carrier_code );
200
 
201
  wp_send_json_success();
202
 
203
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
204
 
205
  wp_send_json_error( $exception->getMessage(), $exception->getCode() );
206
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
+ use WooCommerce\Facebook\Framework\Helper;
15
+ use WooCommerce\Facebook\Admin\Settings_Screens\Product_Sync;
16
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
17
 
18
  defined( 'ABSPATH' ) or exit;
19
 
79
 
80
  try {
81
 
82
+ if ( ! wp_verify_nonce( Helper::get_posted_value( 'security' ), self::ACTION_CANCEL_ORDER ) ) {
83
+ throw new PluginException( __( 'Invalid nonce.', 'facebook-for-woocommerce' ) );
84
  }
85
 
86
+ $order_id = Helper::get_posted_value( 'order_id' );
87
+ $reason_code = Helper::get_posted_value( 'reason_code' );
88
 
89
  if ( empty( $order_id ) ) {
90
+ throw new PluginException( __( 'Order ID is required.', 'facebook-for-woocommerce' ) );
91
  }
92
 
93
  if ( empty( $reason_code ) ) {
94
+ throw new PluginException( __( 'Cancel reason is required.', 'facebook-for-woocommerce' ) );
95
  }
96
 
97
  $order = wc_get_order( absint( $order_id ) );
98
 
99
  if ( false === $order ) {
100
+ throw new PluginException( __( 'A valid Order ID is required.', 'facebook-for-woocommerce' ) );
101
  }
102
 
103
  facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->cancel_order( $order, $reason_code );
104
 
105
  wp_send_json_success();
106
 
107
+ } catch ( PluginException $exception ) {
108
 
109
  wp_send_json_error( $exception->getMessage() );
110
  }
122
 
123
  try {
124
 
125
+ if ( ! wp_verify_nonce( Helper::get_requested_value( 'security' ), self::ACTION_SEARCH_PRODUCT_ATTRIBUTES ) ) {
126
+ throw new PluginException( 'Invalid nonce' );
127
  }
128
 
129
+ $term = Helper::get_requested_value( 'term' );
130
 
131
  if ( ! $term ) {
132
+ throw new PluginException( 'A search term is required' );
133
  }
134
 
135
+ $product = wc_get_product( (int) Helper::get_requested_value( 'request_data' ) );
136
 
137
  if ( ! $product instanceof \WC_Product ) {
138
+ throw new PluginException( 'A valid product ID is required' );
139
  }
140
 
141
  $attributes = Admin\Products::get_available_product_attribute_names( $product );
153
 
154
  wp_send_json( $results );
155
 
156
+ } catch ( PluginException $exception ) {
157
 
158
  die();
159
  }
171
 
172
  try {
173
 
174
+ if ( ! wp_verify_nonce( Helper::get_posted_value( 'nonce' ), self::ACTION_COMPLETE_ORDER ) ) {
175
+ throw new PluginException( 'Invalid nonce', 403 );
176
  }
177
 
178
+ $order_id = (int) Helper::get_posted_value( 'order_id' );
179
+ $tracking_number = wc_clean( Helper::get_posted_value( 'tracking_number' ) );
180
+ $carrier_code = wc_clean( Helper::get_posted_value( 'carrier_code' ) );
181
 
182
  if ( empty( $order_id ) ) {
183
+ throw new PluginException( __( 'Order ID is required', 'facebook-for-woocommerce' ) );
184
  }
185
 
186
  if ( empty( $tracking_number ) ) {
187
+ throw new PluginException( __( 'Tracking number is required', 'facebook-for-woocommerce' ) );
188
  }
189
 
190
  if ( empty( $carrier_code ) ) {
191
+ throw new PluginException( __( 'Carrier code is required', 'facebook-for-woocommerce' ) );
192
  }
193
 
194
  $order = wc_get_order( $order_id );
195
 
196
  if ( ! $order instanceof \WC_Order ) {
197
+ throw new PluginException( __( 'Order not found', 'facebook-for-woocommerce' ) );
198
  }
199
 
200
  facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->fulfill_order( $order, $tracking_number, $carrier_code );
201
 
202
  wp_send_json_success();
203
 
204
+ } catch ( PluginException $exception ) {
205
 
206
  wp_send_json_error( $exception->getMessage(), $exception->getCode() );
207
  }
includes/API.php CHANGED
@@ -9,36 +9,39 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API\Orders\Order;
17
- use SkyVerge\WooCommerce\Facebook\API\Request;
18
- use SkyVerge\WooCommerce\Facebook\API\Response;
19
- use SkyVerge\WooCommerce\Facebook\Events\Event;
20
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
 
21
 
22
  /**
23
  * API handler.
24
  *
25
  * @since 2.0.0
26
  *
27
- * @method API\Request get_request()
28
  */
29
- class API extends Framework\SV_WC_API_Base {
30
-
31
 
32
  use API\Traits\Rate_Limited_API;
33
 
 
 
 
34
 
35
  /** @var string URI used for the request */
36
- protected $request_uri = \WC_Facebookcommerce_Graph_API::GRAPH_API_URL . \WC_Facebookcommerce_Graph_API::API_VERSION;
37
 
38
  /** @var string the configured access token */
39
  protected $access_token;
40
 
41
-
42
  /**
43
  * Constructor.
44
  *
@@ -47,13 +50,10 @@ class API extends Framework\SV_WC_API_Base {
47
  * @param string $access_token access token to use for API requests
48
  */
49
  public function __construct( $access_token ) {
50
-
51
  $this->access_token = $access_token;
52
-
53
  $this->request_headers = array(
54
  'Authorization' => "Bearer {$access_token}",
55
  );
56
-
57
  $this->set_request_content_type_header( 'application/json' );
58
  $this->set_request_accept_header( 'application/json' );
59
  }
@@ -67,7 +67,6 @@ class API extends Framework\SV_WC_API_Base {
67
  * @return string
68
  */
69
  public function get_access_token() {
70
-
71
  return $this->access_token;
72
  }
73
 
@@ -80,7 +79,6 @@ class API extends Framework\SV_WC_API_Base {
80
  * @param string $access_token access token to set
81
  */
82
  public function set_access_token( $access_token ) {
83
-
84
  $this->access_token = $access_token;
85
  }
86
 
@@ -88,27 +86,19 @@ class API extends Framework\SV_WC_API_Base {
88
  /**
89
  * Performs an API request.
90
  *
91
- * @since 2.1.0
92
- *
93
  * @param API\Request $request request object
94
  * @return API\Response
95
- * @throws API\Exceptions\Request_Limit_Reached|Framework\SV_WC_API_Exception
96
  */
97
- public function perform_request( $request ) {
98
-
99
  $rate_limit_id = $request::get_rate_limit_id();
100
  $delay_timestamp = $this->get_rate_limit_delay( $rate_limit_id );
101
-
102
  // if there is a delayed timestamp in the future, throw an exception
103
  if ( $delay_timestamp >= time() ) {
104
-
105
  $this->handle_throttled_request( $rate_limit_id, $delay_timestamp );
106
-
107
  } else {
108
-
109
  $this->set_rate_limit_delay( $rate_limit_id, 0 );
110
  }
111
-
112
  return parent::perform_request( $request );
113
  }
114
 
@@ -120,19 +110,15 @@ class API extends Framework\SV_WC_API_Base {
120
  *
121
  * @since 2.0.0
122
  *
123
- * @throws Framework\SV_WC_API_Exception
124
  */
125
  protected function do_post_parse_response_validation() {
126
-
127
  /** @var API\Response $response */
128
  $response = $this->get_response();
129
  $request = $this->get_request();
130
-
131
  if ( $response && $response->has_api_error() ) {
132
-
133
  $code = $response->get_api_error_code();
134
  $message = sprintf( '%s: %s', $response->get_api_error_type(), $response->get_user_error_message() ?: $response->get_api_error_message() );
135
-
136
  /**
137
  * Graph API
138
  *
@@ -150,20 +136,13 @@ class API extends Framework\SV_WC_API_Base {
150
  * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/batch/#validation-rules
151
  */
152
  if ( in_array( $code, array( 4, 17, 32, 613, 80001, 80004 ), true ) ) {
153
-
154
  $delay_in_seconds = $this->calculate_rate_limit_delay( $response, $this->get_response_headers() );
155
-
156
  if ( $delay_in_seconds > 0 ) {
157
-
158
  $rate_limit_id = $request::get_rate_limit_id();
159
  $timestamp = time() + $delay_in_seconds;
160
-
161
  $this->set_rate_limit_delay( $rate_limit_id, $timestamp );
162
-
163
  $this->handle_throttled_request( $rate_limit_id, $timestamp );
164
-
165
  } else {
166
-
167
  throw new API\Exceptions\Request_Limit_Reached( $message, $code );
168
  }
169
  }
@@ -179,20 +158,14 @@ class API extends Framework\SV_WC_API_Base {
179
  // this was an unrelated error, so the OAuth connection may still be valid
180
  delete_transient( 'wc_facebook_connection_invalid' );
181
  }
182
-
183
  // if the code indicates a retry and we've not hit the retry limit, perform the request again
184
  if ( in_array( $code, $request->get_retry_codes(), false ) && $request->get_retry_count() < $request->get_retry_limit() ) {
185
-
186
  $request->mark_retry();
187
-
188
  $this->response = $this->perform_request( $request );
189
-
190
  return;
191
  }
192
-
193
- throw new Framework\SV_WC_API_Exception( $message, $code );
194
  }
195
-
196
  // if we get this far we're connected, so delete any invalid connection flag
197
  delete_transient( 'wc_facebook_connection_invalid' );
198
  }
@@ -208,18 +181,13 @@ class API extends Framework\SV_WC_API_Base {
208
  * @throws API\Exceptions\Request_Limit_Reached
209
  */
210
  private function handle_throttled_request( $rate_limit_id, $timestamp ) {
211
-
212
  if ( time() > $timestamp ) {
213
  return;
214
  }
215
-
216
  $exception = new API\Exceptions\Request_Limit_Reached( "{$rate_limit_id} requests are currently throttled.", 401 );
217
-
218
  $date_time = new \DateTime();
219
  $date_time->setTimestamp( $timestamp );
220
-
221
  $exception->set_throttle_end( $date_time );
222
-
223
  throw $exception;
224
  }
225
 
@@ -227,18 +195,13 @@ class API extends Framework\SV_WC_API_Base {
227
  /**
228
  * Gets the FBE installation IDs.
229
  *
230
- * @since 2.0.0
231
- *
232
- * @param string $external_business_id external business ID
233
- * @return API\FBE\Installation\Read\Response
234
- * @throws Framework\SV_WC_API_Exception
235
  */
236
- public function get_installation_ids( $external_business_id ) {
237
-
238
  $request = new API\FBE\Installation\Read\Request( $external_business_id );
239
-
240
  $this->set_response_handler( API\FBE\Installation\Read\Response::class );
241
-
242
  return $this->perform_request( $request );
243
  }
244
 
@@ -249,15 +212,12 @@ class API extends Framework\SV_WC_API_Base {
249
  * @since 2.0.0
250
  *
251
  * @param string $page_id page ID
252
- * @return API\Pages\Read\Response
253
- * @throws Framework\SV_WC_API_Exception
254
  */
255
- public function get_page( $page_id ) {
256
-
257
  $request = new API\Pages\Read\Request( $page_id );
258
-
259
  $this->set_response_handler( API\Pages\Read\Response::class );
260
-
261
  return $this->perform_request( $request );
262
  }
263
 
@@ -265,18 +225,13 @@ class API extends Framework\SV_WC_API_Base {
265
  /**
266
  * Gets a Catalog object from Facebook.
267
  *
268
- * @since 2.0.0
269
- *
270
- * @param string $catalog_id catalog ID
271
- * @return API\Catalog\Response
272
- * @throws Framework\SV_WC_API_Exception
273
  */
274
- public function get_catalog( $catalog_id ) {
275
-
276
  $request = new API\Catalog\Request( $catalog_id );
277
-
278
  $this->set_response_handler( API\Catalog\Response::class );
279
-
280
  return $this->perform_request( $request );
281
  }
282
 
@@ -284,40 +239,30 @@ class API extends Framework\SV_WC_API_Base {
284
  /**
285
  * Gets a user object from Facebook.
286
  *
287
- * @since 2.0.0
288
- *
289
  * @param string $user_id user ID. Defaults to the currently authenticated user
290
- * @return API\User\Response
291
- * @throws Framework\SV_WC_API_Exception
292
  */
293
- public function get_user( $user_id = '' ) {
294
-
295
  $request = new API\User\Request( $user_id );
296
-
297
  $this->set_response_handler( API\User\Response::class );
298
-
299
  return $this->perform_request( $request );
300
  }
301
 
302
 
303
  /**
304
- * Delete's a user's API permission.
305
  *
306
  * This is their form of "revoke".
307
  *
308
- * @since 2.0.0
309
- *
310
  * @param string $user_id user ID. Defaults to the currently authenticated user
311
  * @param string $permission permission to delete
312
- * @return API\User\Response
313
- * @throws Framework\SV_WC_API_Exception
314
  */
315
- public function delete_user_permission( $user_id, $permission ) {
316
-
317
  $request = new API\User\Permissions\Delete\Request( $user_id, $permission );
318
-
319
- $this->set_response_handler( API\User\Response::class );
320
-
321
  return $this->perform_request( $request );
322
  }
323
 
@@ -325,18 +270,13 @@ class API extends Framework\SV_WC_API_Base {
325
  /**
326
  * Gets the business configuration.
327
  *
328
- * @since 2.0.0
329
- *
330
  * @param string $external_business_id external business ID
331
- * @return API\FBE\Configuration\Read\Response
332
- * @throws Framework\SV_WC_API_Exception
333
  */
334
  public function get_business_configuration( $external_business_id ) {
335
-
336
  $request = new API\FBE\Configuration\Request( $external_business_id, 'GET' );
337
-
338
  $this->set_response_handler( API\FBE\Configuration\Read\Response::class );
339
-
340
  return $this->perform_request( $request );
341
  }
342
 
@@ -344,21 +284,15 @@ class API extends Framework\SV_WC_API_Base {
344
  /**
345
  * Updates the messenger configuration.
346
  *
347
- * @since 2.0.0
348
- *
349
  * @param string $external_business_id external business ID
350
  * @param API\FBE\Configuration\Messenger $configuration messenger configuration
351
- * @return Response
352
- * @throws Framework\SV_WC_API_Exception
353
  */
354
- public function update_messenger_configuration( $external_business_id, API\FBE\Configuration\Messenger $configuration ) {
355
-
356
  $request = new API\FBE\Configuration\Update\Request( $external_business_id );
357
-
358
  $request->set_messenger_configuration( $configuration );
359
-
360
- $this->set_response_handler( API\Response::class );
361
-
362
  return $this->perform_request( $request );
363
  }
364
 
@@ -368,50 +302,29 @@ class API extends Framework\SV_WC_API_Base {
368
  *
369
  * @see Sync::create_or_update_products()
370
  *
371
- * @since 2.0.0
372
- *
373
- * @param string $catalog_id catalog ID
374
- * @param array $requests array of prefixed product IDs to create, update or remove
375
- * @param bool $allow_upsert whether to allow updates to insert new items
376
- * @return \SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates\Response
377
- * @throws Framework\SV_WC_API_Exception
378
  */
379
- public function send_item_updates( $catalog_id, $requests, $allow_upsert ) {
380
-
381
- $request = new \SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates\Request( $catalog_id );
382
-
383
- $request->set_requests( $requests );
384
- $request->set_allow_upsert( $allow_upsert );
385
-
386
- $this->set_response_handler( \SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates\Response::class );
387
-
388
  return $this->perform_request( $request );
389
  }
390
 
391
 
392
  /**
393
- * Creates a Product Group object.
394
  *
395
- * @since 2.0.0
396
- *
397
- * @param string $catalog_id catalog ID
398
- * @param array $data product group data
399
- * @return Response
400
- * @throws Framework\SV_WC_API_Exception
401
  */
402
- public function create_product_group( $catalog_id, $data ) {
403
-
404
- $request = $this->get_new_request(
405
- array(
406
- 'path' => "/{$catalog_id}/product_groups",
407
- 'method' => 'POST',
408
- )
409
- );
410
-
411
- $request->set_data( $data );
412
-
413
- $this->set_response_handler( Response::class );
414
-
415
  return $this->perform_request( $request );
416
  }
417
 
@@ -419,50 +332,28 @@ class API extends Framework\SV_WC_API_Base {
419
  /**
420
  * Updates the default product item and the available variation attributes of a product group.
421
  *
422
- * @since 2.0.0
423
- *
424
- * @param string $product_group_id product group ID
425
- * @param array $data product group data
426
- * @return Response
427
- * @throws Framework\SV_WC_API_Exception
428
  */
429
- public function update_product_group( $product_group_id, $data ) {
430
-
431
- $request = $this->get_new_request(
432
- array(
433
- 'path' => "/{$product_group_id}",
434
- 'method' => 'POST',
435
- )
436
- );
437
-
438
- $request->set_data( $data );
439
-
440
- $this->set_response_handler( Response::class );
441
-
442
  return $this->perform_request( $request );
443
  }
444
 
445
 
446
  /**
447
- * Deletes a Product Group object.
448
- *
449
- * @since 2.0.0
450
  *
451
- * @param string $product_group_id
452
- * @return Response
453
- * @throws Framework\SV_WC_API_Exception
454
  */
455
- public function delete_product_group( $product_group_id ) {
456
-
457
- $request = $this->get_new_request(
458
- array(
459
- 'path' => "/{$product_group_id}",
460
- 'method' => 'DELETE',
461
- )
462
- );
463
-
464
- $this->set_response_handler( Response::class );
465
-
466
  return $this->perform_request( $request );
467
  }
468
 
@@ -470,19 +361,14 @@ class API extends Framework\SV_WC_API_Base {
470
  /**
471
  * Gets a list of Product Items in the given Product Group.
472
  *
473
- * @since 2.0.0
474
- *
475
  * @param string $product_group_id product group ID
476
  * @param int $limit max number of results returned per page of data
477
- * @return API\Catalog\Product_Group\Products\Read\Response
478
- * @throws Framework\SV_WC_API_Exception
479
  */
480
- public function get_product_group_products( $product_group_id, $limit = 1000 ) {
481
-
482
- $request = new API\Catalog\Product_Group\Products\Read\Request( $product_group_id, $limit );
483
-
484
- $this->set_response_handler( API\Catalog\Product_Group\Products\Read\Response::class );
485
-
486
  return $this->perform_request( $request );
487
  }
488
 
@@ -495,96 +381,183 @@ class API extends Framework\SV_WC_API_Base {
495
  * @param string $catalog_id catalog ID
496
  * @param string $retailer_id retailer ID of the product
497
  * @return Response
498
- * @throws Framework\SV_WC_API_Exception
499
  */
500
  public function find_product_item( $catalog_id, $retailer_id ) {
501
-
502
- $request = new \SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Item\Find\Request( $catalog_id, $retailer_id );
503
-
504
- $this->set_response_handler( \SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Item\Response::class );
505
-
506
  return $this->perform_request( $request );
507
  }
508
 
509
 
510
  /**
511
- * Creates a Product Item object.
512
  *
513
  * @since 2.0.0
514
  *
515
- * @param string $product_group_id parent product ID
516
- * @param array $data product data
517
- * @return Response
518
- * @throws Framework\SV_WC_API_Exception
519
  */
520
- public function create_product_item( $product_group_id, $data ) {
 
 
 
 
521
 
522
- $request = $this->get_new_request(
523
- array(
524
- 'path' => "/{$product_group_id}/products",
525
- 'method' => 'POST',
526
- )
527
- );
528
 
529
- $request->set_data( $data );
 
 
 
 
 
 
 
 
 
 
 
 
530
 
531
- $this->set_response_handler( Response::class );
532
 
 
 
 
 
 
 
 
 
 
 
533
  return $this->perform_request( $request );
534
  }
535
 
536
 
537
  /**
538
- * Updates a Product Item object.
539
- *
540
- * @since 2.0.0
541
  *
542
- * @param string $product_item_id product item ID
543
- * @param array $data product data
544
- * @return Response
545
- * @throws Framework\SV_WC_API_Exception
 
546
  */
547
- public function update_product_item( $product_item_id, $data ) {
 
 
 
 
548
 
549
- $request = $this->get_new_request(
550
- array(
551
- 'path' => "/{$product_item_id}",
552
- 'method' => 'POST',
553
- )
554
- );
555
 
556
- $request->set_data( $data );
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
- $this->set_response_handler( Response::class );
 
 
 
 
 
 
 
 
 
 
 
559
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  return $this->perform_request( $request );
561
  }
562
 
563
 
564
  /**
565
- * Deletes a Product Item object.
566
- *
567
- * @since 2.0.0
568
- *
569
- * @param string $product_item_id product item ID
570
  * @return Response
571
- * @throws Framework\SV_WC_API_Exception
 
572
  */
573
- public function delete_product_item( $product_item_id ) {
 
 
 
 
574
 
575
- $request = $this->get_new_request(
576
- array(
577
- 'path' => "/{$product_item_id}",
578
- 'method' => 'DELETE',
579
- )
580
- );
581
 
582
- $this->set_response_handler( Response::class );
 
 
 
 
 
 
 
 
 
 
583
 
 
 
 
 
 
 
 
 
 
 
584
  return $this->perform_request( $request );
585
  }
586
 
587
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
  /**
589
  * Sends Pixel events.
590
  *
@@ -593,14 +566,11 @@ class API extends Framework\SV_WC_API_Base {
593
  * @param string $pixel_id pixel ID
594
  * @param Event[] $events events to send
595
  * @return Response
596
- * @throws Framework\SV_WC_API_Exception
597
  */
598
  public function send_pixel_events( $pixel_id, array $events ) {
599
-
600
  $request = new API\Pixel\Events\Request( $pixel_id, $events );
601
-
602
  $this->set_response_handler( Response::class );
603
-
604
  return $this->perform_request( $request );
605
  }
606
 
@@ -613,33 +583,25 @@ class API extends Framework\SV_WC_API_Base {
613
  * @param API\Response $response previous response object
614
  * @param int $additional_pages number of additional pages of results to retrieve
615
  * @return API\Response|null
616
- * @throws Framework\SV_WC_API_Exception
617
  */
618
- public function next( API\Response $response, $additional_pages = null ) {
619
-
620
  $next_response = null;
621
-
622
  // get the next page if we haven't reached the limit of pages to retrieve and the endpoint for the next page is available
623
- if ( ( null === $additional_pages || $response->get_pages_retrieved() <= $additional_pages ) && $response->get_next_page_endpoint() ) {
624
-
625
  $components = parse_url( str_replace( $this->request_uri, '', $response->get_next_page_endpoint() ) );
626
-
627
  $request = $this->get_new_request(
628
- array(
629
- 'path' => isset( $components['path'] ) ? $components['path'] : '',
630
  'method' => 'GET',
631
- 'params' => isset( $components['query'] ) ? wp_parse_args( $components['query'] ) : array(),
632
- )
633
  );
634
-
635
  $this->set_response_handler( get_class( $response ) );
636
-
637
  $next_response = $this->perform_request( $request );
638
-
639
  // this is the n + 1 page of results for the original response
640
  $next_response->set_pages_retrieved( $response->get_pages_retrieved() + 1 );
641
  }
642
-
643
  return $next_response;
644
  }
645
 
@@ -651,21 +613,17 @@ class API extends Framework\SV_WC_API_Base {
651
  *
652
  * @param string $page_id page ID
653
  * @return API\Orders\Response
654
- * @throws Framework\SV_WC_API_Exception
655
  */
656
  public function get_new_orders( $page_id ) {
657
-
658
  $request_args = array(
659
  'state' => array(
660
  Order::STATUS_PROCESSING,
661
  Order::STATUS_CREATED,
662
  ),
663
  );
664
-
665
  $request = new API\Orders\Request( $page_id, $request_args );
666
-
667
  $this->set_response_handler( API\Orders\Response::class );
668
-
669
  return $this->perform_request( $request );
670
  }
671
 
@@ -677,10 +635,9 @@ class API extends Framework\SV_WC_API_Base {
677
  *
678
  * @param string $page_id page ID
679
  * @return API\Orders\Response
680
- * @throws Framework\SV_WC_API_Exception
681
  */
682
  public function get_cancelled_orders( $page_id ) {
683
-
684
  $request_args = array(
685
  'state' => array(
686
  Order::STATUS_COMPLETED,
@@ -688,11 +645,8 @@ class API extends Framework\SV_WC_API_Base {
688
  'updated_after' => time() - facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->get_order_update_interval(),
689
  'filters' => 'has_cancellations',
690
  );
691
-
692
  $request = new API\Orders\Request( $page_id, $request_args );
693
-
694
  $this->set_response_handler( API\Orders\Response::class );
695
-
696
  return $this->perform_request( $request );
697
  }
698
 
@@ -704,14 +658,11 @@ class API extends Framework\SV_WC_API_Base {
704
  *
705
  * @param string $remote_id remote order ID
706
  * @return API\Orders\Read\Response
707
- * @throws Framework\SV_WC_API_Exception
708
  */
709
  public function get_order( $remote_id ) {
710
-
711
  $request = new API\Orders\Read\Request( $remote_id );
712
-
713
  $this->set_response_handler( API\Orders\Read\Response::class );
714
-
715
  return $this->perform_request( $request );
716
  }
717
 
@@ -724,14 +675,11 @@ class API extends Framework\SV_WC_API_Base {
724
  * @param string $remote_id remote order ID
725
  * @param string $merchant_order_reference WC order ID
726
  * @return API\Response
727
- * @throws Framework\SV_WC_API_Exception
728
  */
729
  public function acknowledge_order( $remote_id, $merchant_order_reference ) {
730
-
731
  $request = new API\Orders\Acknowledge\Request( $remote_id, $merchant_order_reference );
732
-
733
  $this->set_response_handler( API\Response::class );
734
-
735
  return $this->perform_request( $request );
736
  }
737
 
@@ -746,14 +694,11 @@ class API extends Framework\SV_WC_API_Base {
746
  * @param string $remote_id remote order ID
747
  * @param array $fulfillment_data fulfillment data to be sent on the request
748
  * @return API\Response
749
- * @throws Framework\SV_WC_API_Exception
750
  */
751
  public function fulfill_order( $remote_id, $fulfillment_data ) {
752
-
753
  $request = new API\Orders\Fulfillment\Request( $remote_id, $fulfillment_data );
754
-
755
  $this->set_response_handler( API\Response::class );
756
-
757
  return $this->perform_request( $request );
758
  }
759
 
@@ -767,14 +712,11 @@ class API extends Framework\SV_WC_API_Base {
767
  * @param string $reason cancellation reason
768
  * @param bool $restock_items whether to restock items remotely
769
  * @return API\Response
770
- * @throws Framework\SV_WC_API_Exception
771
  */
772
  public function cancel_order( $remote_id, $reason, $restock_items = true ) {
773
-
774
  $request = new API\Orders\Cancel\Request( $remote_id, $reason, $restock_items );
775
-
776
  $this->set_response_handler( API\Response::class );
777
-
778
  return $this->perform_request( $request );
779
  }
780
 
@@ -789,14 +731,11 @@ class API extends Framework\SV_WC_API_Base {
789
  * @param string $remote_id remote order ID
790
  * @param array $refund_data refund data to be sent on the request
791
  * @return API\Response
792
- * @throws Framework\SV_WC_API_Exception
793
  */
794
  public function add_order_refund( $remote_id, $refund_data ) {
795
-
796
  $request = new API\Orders\Refund\Request( $remote_id, $refund_data );
797
-
798
  $this->set_response_handler( API\Response::class );
799
-
800
  return $this->perform_request( $request );
801
  }
802
 
@@ -815,21 +754,17 @@ class API extends Framework\SV_WC_API_Base {
815
  * }
816
  * @return Request
817
  */
818
- protected function get_new_request( $args = array() ) {
819
-
820
  $defaults = array(
821
  'path' => '/',
822
  'method' => 'GET',
823
- 'params' => array(),
824
  );
825
-
826
  $args = wp_parse_args( $args, $defaults );
827
  $request = new Request( $args['path'], $args['method'] );
828
-
829
  if ( $args['params'] ) {
830
  $request->set_params( $args['params'] );
831
  }
832
-
833
  return $request;
834
  }
835
 
@@ -842,9 +777,6 @@ class API extends Framework\SV_WC_API_Base {
842
  * @return \WC_Facebookcommerce
843
  */
844
  protected function get_plugin() {
845
-
846
  return facebook_for_woocommerce();
847
  }
848
-
849
-
850
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API\Orders\Order;
17
+ use WooCommerce\Facebook\API\Request;
18
+ use WooCommerce\Facebook\API\Response;
19
+ use WooCommerce\Facebook\Events\Event;
20
+
21
+ use WooCommerce\Facebook\Framework\Api\Base;
22
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
23
 
24
  /**
25
  * API handler.
26
  *
27
  * @since 2.0.0
28
  *
29
+ * @method Framework\Api\Request get_request()
30
  */
31
+ class API extends Base {
 
32
 
33
  use API\Traits\Rate_Limited_API;
34
 
35
+ public const GRAPH_API_URL = 'https://graph.facebook.com/';
36
+
37
+ public const API_VERSION = 'v13.0';
38
 
39
  /** @var string URI used for the request */
40
+ protected $request_uri = self::GRAPH_API_URL . self::API_VERSION;
41
 
42
  /** @var string the configured access token */
43
  protected $access_token;
44
 
 
45
  /**
46
  * Constructor.
47
  *
50
  * @param string $access_token access token to use for API requests
51
  */
52
  public function __construct( $access_token ) {
 
53
  $this->access_token = $access_token;
 
54
  $this->request_headers = array(
55
  'Authorization' => "Bearer {$access_token}",
56
  );
 
57
  $this->set_request_content_type_header( 'application/json' );
58
  $this->set_request_accept_header( 'application/json' );
59
  }
67
  * @return string
68
  */
69
  public function get_access_token() {
 
70
  return $this->access_token;
71
  }
72
 
79
  * @param string $access_token access token to set
80
  */
81
  public function set_access_token( $access_token ) {
 
82
  $this->access_token = $access_token;
83
  }
84
 
86
  /**
87
  * Performs an API request.
88
  *
 
 
89
  * @param API\Request $request request object
90
  * @return API\Response
91
+ * @throws API\Exceptions\Request_Limit_Reached|ApiException
92
  */
93
+ protected function perform_request( $request ): API\Response {
 
94
  $rate_limit_id = $request::get_rate_limit_id();
95
  $delay_timestamp = $this->get_rate_limit_delay( $rate_limit_id );
 
96
  // if there is a delayed timestamp in the future, throw an exception
97
  if ( $delay_timestamp >= time() ) {
 
98
  $this->handle_throttled_request( $rate_limit_id, $delay_timestamp );
 
99
  } else {
 
100
  $this->set_rate_limit_delay( $rate_limit_id, 0 );
101
  }
 
102
  return parent::perform_request( $request );
103
  }
104
 
110
  *
111
  * @since 2.0.0
112
  *
113
+ * @throws ApiException
114
  */
115
  protected function do_post_parse_response_validation() {
 
116
  /** @var API\Response $response */
117
  $response = $this->get_response();
118
  $request = $this->get_request();
 
119
  if ( $response && $response->has_api_error() ) {
 
120
  $code = $response->get_api_error_code();
121
  $message = sprintf( '%s: %s', $response->get_api_error_type(), $response->get_user_error_message() ?: $response->get_api_error_message() );
 
122
  /**
123
  * Graph API
124
  *
136
  * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/batch/#validation-rules
137
  */
138
  if ( in_array( $code, array( 4, 17, 32, 613, 80001, 80004 ), true ) ) {
 
139
  $delay_in_seconds = $this->calculate_rate_limit_delay( $response, $this->get_response_headers() );
 
140
  if ( $delay_in_seconds > 0 ) {
 
141
  $rate_limit_id = $request::get_rate_limit_id();
142
  $timestamp = time() + $delay_in_seconds;
 
143
  $this->set_rate_limit_delay( $rate_limit_id, $timestamp );
 
144
  $this->handle_throttled_request( $rate_limit_id, $timestamp );
 
145
  } else {
 
146
  throw new API\Exceptions\Request_Limit_Reached( $message, $code );
147
  }
148
  }
158
  // this was an unrelated error, so the OAuth connection may still be valid
159
  delete_transient( 'wc_facebook_connection_invalid' );
160
  }
 
161
  // if the code indicates a retry and we've not hit the retry limit, perform the request again
162
  if ( in_array( $code, $request->get_retry_codes(), false ) && $request->get_retry_count() < $request->get_retry_limit() ) {
 
163
  $request->mark_retry();
 
164
  $this->response = $this->perform_request( $request );
 
165
  return;
166
  }
167
+ throw new ApiException( $message, $code );
 
168
  }
 
169
  // if we get this far we're connected, so delete any invalid connection flag
170
  delete_transient( 'wc_facebook_connection_invalid' );
171
  }
181
  * @throws API\Exceptions\Request_Limit_Reached
182
  */
183
  private function handle_throttled_request( $rate_limit_id, $timestamp ) {
 
184
  if ( time() > $timestamp ) {
185
  return;
186
  }
 
187
  $exception = new API\Exceptions\Request_Limit_Reached( "{$rate_limit_id} requests are currently throttled.", 401 );
 
188
  $date_time = new \DateTime();
189
  $date_time->setTimestamp( $timestamp );
 
190
  $exception->set_throttle_end( $date_time );
 
191
  throw $exception;
192
  }
193
 
195
  /**
196
  * Gets the FBE installation IDs.
197
  *
198
+ * @param string $external_business_id External business id.
199
+ * @return API\Response|API\FBE\Installation\Read\Response
200
+ * @throws ApiException
 
 
201
  */
202
+ public function get_installation_ids( string $external_business_id ): API\FBE\Installation\Read\Response {
 
203
  $request = new API\FBE\Installation\Read\Request( $external_business_id );
 
204
  $this->set_response_handler( API\FBE\Installation\Read\Response::class );
 
205
  return $this->perform_request( $request );
206
  }
207
 
212
  * @since 2.0.0
213
  *
214
  * @param string $page_id page ID
215
+ * @return API\Response|API\Pages\Read\Response
216
+ * @throws ApiException
217
  */
218
+ public function get_page( $page_id ): API\Pages\Read\Response {
 
219
  $request = new API\Pages\Read\Request( $page_id );
 
220
  $this->set_response_handler( API\Pages\Read\Response::class );
 
221
  return $this->perform_request( $request );
222
  }
223
 
225
  /**
226
  * Gets a Catalog object from Facebook.
227
  *
228
+ * @param string $catalog_id Facebook catalog id.
229
+ * @return API\Response|API\Catalog\Response
230
+ * @throws ApiException
 
 
231
  */
232
+ public function get_catalog( string $catalog_id ): API\Catalog\Response {
 
233
  $request = new API\Catalog\Request( $catalog_id );
 
234
  $this->set_response_handler( API\Catalog\Response::class );
 
235
  return $this->perform_request( $request );
236
  }
237
 
239
  /**
240
  * Gets a user object from Facebook.
241
  *
 
 
242
  * @param string $user_id user ID. Defaults to the currently authenticated user
243
+ * @return API\Response|API\User\Response
244
+ * @throws ApiException
245
  */
246
+ public function get_user( string $user_id = '' ): API\User\Response {
 
247
  $request = new API\User\Request( $user_id );
 
248
  $this->set_response_handler( API\User\Response::class );
 
249
  return $this->perform_request( $request );
250
  }
251
 
252
 
253
  /**
254
+ * Deletes user API permission.
255
  *
256
  * This is their form of "revoke".
257
  *
 
 
258
  * @param string $user_id user ID. Defaults to the currently authenticated user
259
  * @param string $permission permission to delete
260
+ * @return API\Response|API\User\Permissions\Delete\Response
261
+ * @throws ApiException
262
  */
263
+ public function delete_user_permission( string $user_id, string $permission ): API\User\Permissions\Delete\Response {
 
264
  $request = new API\User\Permissions\Delete\Request( $user_id, $permission );
265
+ $this->set_response_handler( API\User\Permissions\Delete\Response::class );
 
 
266
  return $this->perform_request( $request );
267
  }
268
 
270
  /**
271
  * Gets the business configuration.
272
  *
 
 
273
  * @param string $external_business_id external business ID
274
+ * @return API\Response|API\FBE\Configuration\Read\Response
275
+ * @throws ApiException
276
  */
277
  public function get_business_configuration( $external_business_id ) {
 
278
  $request = new API\FBE\Configuration\Request( $external_business_id, 'GET' );
 
279
  $this->set_response_handler( API\FBE\Configuration\Read\Response::class );
 
280
  return $this->perform_request( $request );
281
  }
282
 
284
  /**
285
  * Updates the messenger configuration.
286
  *
 
 
287
  * @param string $external_business_id external business ID
288
  * @param API\FBE\Configuration\Messenger $configuration messenger configuration
289
+ * @return API\Response|API\FBE\Configuration\Update\Response
290
+ * @throws ApiException
291
  */
292
+ public function update_messenger_configuration( string $external_business_id, API\FBE\Configuration\Messenger $configuration ): API\FBE\Configuration\Update\Response {
 
293
  $request = new API\FBE\Configuration\Update\Request( $external_business_id );
 
294
  $request->set_messenger_configuration( $configuration );
295
+ $this->set_response_handler( API\FBE\Configuration\Update\Response::class );
 
 
296
  return $this->perform_request( $request );
297
  }
298
 
302
  *
303
  * @see Sync::create_or_update_products()
304
  *
305
+ * @param string $facebook_product_catalog_id Facebook Product Catalog ID.
306
+ * @param array $requests array of prefixed product IDs to create, update or remove.
307
+ * @return API\Response|API\ProductCatalog\ItemsBatch\Create\Response
308
+ * @throws ApiException
 
 
 
309
  */
310
+ public function send_item_updates( string $facebook_product_catalog_id, array $requests ) {
311
+ $request = new API\ProductCatalog\ItemsBatch\Create\Request( $facebook_product_catalog_id, $requests );
312
+ $this->set_response_handler( API\ProductCatalog\ItemsBatch\Create\Response::class );
 
 
 
 
 
 
313
  return $this->perform_request( $request );
314
  }
315
 
316
 
317
  /**
318
+ * Creates Facebook Product Group.
319
  *
320
+ * @param string $product_catalog_id Facebook Product Catalog ID.
321
+ * @param array $data Facebook Product Group Data.
322
+ * @return API\Response|API\ProductCatalog\ProductGroups\Create\Response
323
+ * @throws ApiException
 
 
324
  */
325
+ public function create_product_group( string $product_catalog_id, array $data ): API\ProductCatalog\ProductGroups\Create\Response {
326
+ $request = new API\ProductCatalog\ProductGroups\Create\Request( $product_catalog_id, $data );
327
+ $this->set_response_handler( API\ProductCatalog\ProductGroups\Create\Response::class );
 
 
 
 
 
 
 
 
 
 
328
  return $this->perform_request( $request );
329
  }
330
 
332
  /**
333
  * Updates the default product item and the available variation attributes of a product group.
334
  *
335
+ * @param string $product_group_id Facebook Product Group ID.
336
+ * @param array $data Facebook Product Group Data.
337
+ * @return API\ProductCatalog\ProductGroups\Update\Response
338
+ * @throws ApiException
 
 
339
  */
340
+ public function update_product_group( string $product_group_id, array $data ): API\ProductCatalog\ProductGroups\Update\Response {
341
+ $request = new API\ProductCatalog\ProductGroups\Update\Request( $product_group_id , $data );
342
+ $this->set_response_handler( API\ProductCatalog\ProductGroups\Update\Response::class );
 
 
 
 
 
 
 
 
 
 
343
  return $this->perform_request( $request );
344
  }
345
 
346
 
347
  /**
348
+ * Deletes a Facebook Product Group object.
 
 
349
  *
350
+ * @param string $product_group_id Facebook Product Group ID.
351
+ * @return API\ProductCatalog\ProductGroups\Delete\Response
352
+ * @throws ApiException
353
  */
354
+ public function delete_product_group( string $product_group_id ): API\ProductCatalog\ProductGroups\Delete\Response {
355
+ $request = new API\ProductCatalog\ProductGroups\Delete\Request( $product_group_id );
356
+ $this->set_response_handler( API\ProductCatalog\ProductGroups\Delete\Response::class );
 
 
 
 
 
 
 
 
357
  return $this->perform_request( $request );
358
  }
359
 
361
  /**
362
  * Gets a list of Product Items in the given Product Group.
363
  *
 
 
364
  * @param string $product_group_id product group ID
365
  * @param int $limit max number of results returned per page of data
366
+ * @return API\Response|API\ProductCatalog\ProductGroups\Read\Response
367
+ * @throws ApiException
368
  */
369
+ public function get_product_group_products( string $product_group_id, int $limit = 1000 ): API\ProductCatalog\ProductGroups\Read\Response {
370
+ $request = new API\ProductCatalog\ProductGroups\Read\Request( $product_group_id, $limit );
371
+ $this->set_response_handler( API\ProductCatalog\ProductGroups\Read\Response::class );
 
 
 
372
  return $this->perform_request( $request );
373
  }
374
 
381
  * @param string $catalog_id catalog ID
382
  * @param string $retailer_id retailer ID of the product
383
  * @return Response
384
+ * @throws ApiException
385
  */
386
  public function find_product_item( $catalog_id, $retailer_id ) {
387
+ $request = new \WooCommerce\Facebook\API\Catalog\Product_Item\Find\Request( $catalog_id, $retailer_id );
388
+ $this->set_response_handler( \WooCommerce\Facebook\API\Catalog\Product_Item\Response::class );
 
 
 
389
  return $this->perform_request( $request );
390
  }
391
 
392
 
393
  /**
394
+ * Creates a Product under the specified Product Group.
395
  *
396
  * @since 2.0.0
397
  *
398
+ * @param string $product_group_id Facebook Product Group ID.
399
+ * @param array $data Facebook Product Data.
400
+ * @return API\Response|API\ProductCatalog\Products\Create\Response
401
+ * @throws ApiException In case of network request error.
402
  */
403
+ public function create_product_item( string $product_group_id, array $data ): API\ProductCatalog\Products\Create\Response {
404
+ $request = new API\ProductCatalog\Products\Create\Request( $product_group_id, $data );
405
+ $this->set_response_handler( API\ProductCatalog\Products\Create\Response::class );
406
+ return $this->perform_request( $request );
407
+ }
408
 
 
 
 
 
 
 
409
 
410
+ /**
411
+ * Updates a Product Item object.
412
+ *
413
+ * @param string $facebook_product_id Facebook Product ID.
414
+ * @param array $data Product Data.
415
+ * @return API\Response|API\ProductCatalog\Products\Update\Response
416
+ * @throws ApiException In case of network request error.
417
+ */
418
+ public function update_product_item( string $facebook_product_id, array $data ): API\ProductCatalog\Products\Update\Response {
419
+ $request = new API\ProductCatalog\Products\Update\Request( $facebook_product_id, $data );
420
+ $this->set_response_handler( API\ProductCatalog\Products\Update\Response::class );
421
+ return $this->perform_request( $request );
422
+ }
423
 
 
424
 
425
+ /**
426
+ * Deletes a Product Item object.
427
+ *
428
+ * @param string $facebook_product_id Facebook Product ID.
429
+ * @return API\Response|API\ProductCatalog\Products\Delete\Response
430
+ * @throws ApiException In case of network request error.
431
+ */
432
+ public function delete_product_item( string $facebook_product_id ): API\ProductCatalog\Products\Delete\Response {
433
+ $request = new API\ProductCatalog\Products\Delete\Request( $facebook_product_id );
434
+ $this->set_response_handler( API\ProductCatalog\Products\Delete\Response::class );
435
  return $this->perform_request( $request );
436
  }
437
 
438
 
439
  /**
440
+ * Returns product Facebook ID and Facebook Group ID.
 
 
441
  *
442
+ * @param string $facebook_product_catalog_id
443
+ * @param string $facebook_retailer_id
444
+ * @return API\Response|API\ProductCatalog\Products\Id\Response
445
+ * @throws ApiException In case of network request error.
446
+ * @throws API\Exceptions\Request_Limit_Reached
447
  */
448
+ public function get_product_facebook_ids( string $facebook_product_catalog_id, string $facebook_retailer_id ): API\ProductCatalog\Products\Id\Response {
449
+ $request = new API\ProductCatalog\Products\Id\Request( $facebook_product_catalog_id, $facebook_retailer_id );
450
+ $this->set_response_handler( API\ProductCatalog\Products\Id\Response::class );
451
+ return $this->perform_request( $request );
452
+ }
453
 
 
 
 
 
 
 
454
 
455
+ /**
456
+ * @param string $product_catalog_id
457
+ * @param array $data
458
+ * @return API\Response|API\ProductCatalog\ProductSets\Create\Response
459
+ * @throws ApiException
460
+ * @throws API\Exceptions\Request_Limit_Reached
461
+ */
462
+ public function create_product_set_item( string $product_catalog_id, array $data ): API\ProductCatalog\ProductSets\Create\Response {
463
+ $request = new API\ProductCatalog\ProductSets\Create\Request( $product_catalog_id, $data );
464
+ $this->set_response_handler( API\ProductCatalog\ProductSets\Create\Response::class );
465
+ return $this->perform_request( $request );
466
+ }
467
+
468
 
469
+ /**
470
+ * @param string $product_set_id
471
+ * @param array $data
472
+ * @return API\Response|API\ProductCatalog\ProductSets\Update\Response
473
+ * @throws ApiException
474
+ * @throws API\Exceptions\Request_Limit_Reached
475
+ */
476
+ public function update_product_set_item( string $product_set_id, array $data ): API\ProductCatalog\ProductSets\Update\Response {
477
+ $request = new API\ProductCatalog\ProductSets\Update\Request( $product_set_id, $data );
478
+ $this->set_response_handler( API\ProductCatalog\ProductSets\Update\Response::class );
479
+ return $this->perform_request( $request );
480
+ }
481
 
482
+
483
+ /**
484
+ * @param string $product_set_id Facebook Product Set ID.
485
+ * @param bool $allow_live_deletion Allow live Facebook Product Set Deletion.
486
+ * @return API\Response|API\ProductCatalog\ProductSets\Delete\Response
487
+ * @throws ApiException
488
+ * @throws API\Exceptions\Request_Limit_Reached
489
+ */
490
+ public function delete_product_set_item( string $product_set_id, bool $allow_live_deletion ): API\ProductCatalog\ProductSets\Delete\Response {
491
+ $request = new API\ProductCatalog\ProductSets\Delete\Request( $product_set_id, $allow_live_deletion );
492
+ $this->set_response_handler( API\ProductCatalog\ProductSets\Delete\Response::class );
493
+ return $this->perform_request( $request );
494
+ }
495
+
496
+ /**
497
+ * @param string $product_catalog_id
498
+ * @return API\Response|API\ProductCatalog\ProductFeeds\ReadAll\Response
499
+ * @throws ApiException
500
+ * @throws API\Exceptions\Request_Limit_Reached
501
+ */
502
+ public function read_feeds( string $product_catalog_id ): API\ProductCatalog\ProductFeeds\ReadAll\Response {
503
+ $request = new API\ProductCatalog\ProductFeeds\ReadAll\Request( $product_catalog_id );
504
+ $this->set_response_handler( API\ProductCatalog\ProductFeeds\ReadAll\Response::class );
505
  return $this->perform_request( $request );
506
  }
507
 
508
 
509
  /**
510
+ * @param string $product_feed_id Facebook Product Feed ID.
 
 
 
 
511
  * @return Response
512
+ * @throws ApiException
513
+ * @throws API\Exceptions\Request_Limit_Reached
514
  */
515
+ public function read_feed( string $product_feed_id ) {
516
+ $request = new API\ProductCatalog\ProductFeeds\Read\Request( $product_feed_id );
517
+ $this->set_response_handler( API\ProductCatalog\ProductFeeds\Read\Response::class );
518
+ return $this->perform_request( $request );
519
+ }
520
 
 
 
 
 
 
 
521
 
522
+ /**
523
+ * @param string $product_feed_upload_id
524
+ * @return Response
525
+ * @throws ApiException
526
+ * @throws API\Exceptions\Request_Limit_Reached
527
+ */
528
+ public function read_upload( string $product_feed_upload_id ) {
529
+ $request = new API\ProductCatalog\ProductFeedUploads\Read\Request( $product_feed_upload_id );
530
+ $this->set_response_handler( API\ProductCatalog\ProductFeedUploads\Read\Response::class );
531
+ return $this->perform_request( $request );
532
+ }
533
 
534
+
535
+ /**
536
+ * @param string $external_merchant_settings_id
537
+ * @return API\Response|API\Tip\Read\Response
538
+ * @throws ApiException
539
+ * @throws API\Exceptions\Request_Limit_Reached
540
+ */
541
+ public function get_tip_info( string $external_merchant_settings_id ): API\Tip\Read\Response {
542
+ $request = new API\Tip\Read\Request( $external_merchant_settings_id );
543
+ $this->set_response_handler( API\Tip\Read\Response::class );
544
  return $this->perform_request( $request );
545
  }
546
 
547
 
548
+ public function log_tip_event( $tip_id, $channel_id, $event ) {
549
+ $request = new API\Tip\Log\Request( $tip_id, $channel_id, $event );
550
+ $this->set_response_handler( API\Tip\Log\Response::class );
551
+ return $this->perform_request( $request );
552
+ }
553
+
554
+
555
+ public function log( $facebook_external_merchant_settings_id, $message, $error ) {
556
+ $request = new API\Log\Create\Request( $facebook_external_merchant_settings_id, $message, $error );
557
+ $this->set_response_handler( API\Log\Create\Response::class );
558
+ return $this->perform_request( $request );
559
+ }
560
+
561
  /**
562
  * Sends Pixel events.
563
  *
566
  * @param string $pixel_id pixel ID
567
  * @param Event[] $events events to send
568
  * @return Response
569
+ * @throws ApiException
570
  */
571
  public function send_pixel_events( $pixel_id, array $events ) {
 
572
  $request = new API\Pixel\Events\Request( $pixel_id, $events );
 
573
  $this->set_response_handler( Response::class );
 
574
  return $this->perform_request( $request );
575
  }
576
 
583
  * @param API\Response $response previous response object
584
  * @param int $additional_pages number of additional pages of results to retrieve
585
  * @return API\Response|null
586
+ * @throws ApiException
587
  */
588
+ public function next( API\Response $response, int $additional_pages = 0 ) {
 
589
  $next_response = null;
 
590
  // get the next page if we haven't reached the limit of pages to retrieve and the endpoint for the next page is available
591
+ if ( ( 0 === $additional_pages || $response->get_pages_retrieved() <= $additional_pages ) && $response->get_next_page_endpoint() ) {
 
592
  $components = parse_url( str_replace( $this->request_uri, '', $response->get_next_page_endpoint() ) );
 
593
  $request = $this->get_new_request(
594
+ [
595
+ 'path' => $components['path'] ?? '',
596
  'method' => 'GET',
597
+ 'params' => isset( $components['query'] ) ? wp_parse_args( $components['query'] ) : [],
598
+ ]
599
  );
 
600
  $this->set_response_handler( get_class( $response ) );
 
601
  $next_response = $this->perform_request( $request );
 
602
  // this is the n + 1 page of results for the original response
603
  $next_response->set_pages_retrieved( $response->get_pages_retrieved() + 1 );
604
  }
 
605
  return $next_response;
606
  }
607
 
613
  *
614
  * @param string $page_id page ID
615
  * @return API\Orders\Response
616
+ * @throws ApiException
617
  */
618
  public function get_new_orders( $page_id ) {
 
619
  $request_args = array(
620
  'state' => array(
621
  Order::STATUS_PROCESSING,
622
  Order::STATUS_CREATED,
623
  ),
624
  );
 
625
  $request = new API\Orders\Request( $page_id, $request_args );
 
626
  $this->set_response_handler( API\Orders\Response::class );
 
627
  return $this->perform_request( $request );
628
  }
629
 
635
  *
636
  * @param string $page_id page ID
637
  * @return API\Orders\Response
638
+ * @throws ApiException
639
  */
640
  public function get_cancelled_orders( $page_id ) {
 
641
  $request_args = array(
642
  'state' => array(
643
  Order::STATUS_COMPLETED,
645
  'updated_after' => time() - facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->get_order_update_interval(),
646
  'filters' => 'has_cancellations',
647
  );
 
648
  $request = new API\Orders\Request( $page_id, $request_args );
 
649
  $this->set_response_handler( API\Orders\Response::class );
 
650
  return $this->perform_request( $request );
651
  }
652
 
658
  *
659
  * @param string $remote_id remote order ID
660
  * @return API\Orders\Read\Response
661
+ * @throws ApiException
662
  */
663
  public function get_order( $remote_id ) {
 
664
  $request = new API\Orders\Read\Request( $remote_id );
 
665
  $this->set_response_handler( API\Orders\Read\Response::class );
 
666
  return $this->perform_request( $request );
667
  }
668
 
675
  * @param string $remote_id remote order ID
676
  * @param string $merchant_order_reference WC order ID
677
  * @return API\Response
678
+ * @throws ApiException
679
  */
680
  public function acknowledge_order( $remote_id, $merchant_order_reference ) {
 
681
  $request = new API\Orders\Acknowledge\Request( $remote_id, $merchant_order_reference );
 
682
  $this->set_response_handler( API\Response::class );
 
683
  return $this->perform_request( $request );
684
  }
685
 
694
  * @param string $remote_id remote order ID
695
  * @param array $fulfillment_data fulfillment data to be sent on the request
696
  * @return API\Response
697
+ * @throws ApiException
698
  */
699
  public function fulfill_order( $remote_id, $fulfillment_data ) {
 
700
  $request = new API\Orders\Fulfillment\Request( $remote_id, $fulfillment_data );
 
701
  $this->set_response_handler( API\Response::class );
 
702
  return $this->perform_request( $request );
703
  }
704
 
712
  * @param string $reason cancellation reason
713
  * @param bool $restock_items whether to restock items remotely
714
  * @return API\Response
715
+ * @throws ApiException
716
  */
717
  public function cancel_order( $remote_id, $reason, $restock_items = true ) {
 
718
  $request = new API\Orders\Cancel\Request( $remote_id, $reason, $restock_items );
 
719
  $this->set_response_handler( API\Response::class );
 
720
  return $this->perform_request( $request );
721
  }
722
 
731
  * @param string $remote_id remote order ID
732
  * @param array $refund_data refund data to be sent on the request
733
  * @return API\Response
734
+ * @throws ApiException
735
  */
736
  public function add_order_refund( $remote_id, $refund_data ) {
 
737
  $request = new API\Orders\Refund\Request( $remote_id, $refund_data );
 
738
  $this->set_response_handler( API\Response::class );
 
739
  return $this->perform_request( $request );
740
  }
741
 
754
  * }
755
  * @return Request
756
  */
757
+ protected function get_new_request( $args = [] ) {
 
758
  $defaults = array(
759
  'path' => '/',
760
  'method' => 'GET',
761
+ 'params' => [],
762
  );
 
763
  $args = wp_parse_args( $args, $defaults );
764
  $request = new Request( $args['path'], $args['method'] );
 
765
  if ( $args['params'] ) {
766
  $request->set_params( $args['params'] );
767
  }
 
768
  return $request;
769
  }
770
 
777
  * @return \WC_Facebookcommerce
778
  */
779
  protected function get_plugin() {
 
780
  return facebook_for_woocommerce();
781
  }
 
 
782
  }
includes/API/Catalog/Product_Group/Products/Read/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Group\Products\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the API endpoint that returns a list of Product Items in a particular Product Group.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Product_Group\Products\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the API endpoint that returns a list of Product Items in a particular Product Group.
includes/API/Catalog/Product_Group/Products/Read/Response.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Group\Products\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Response object for the API endpoint that returns a list of Product Items in a particular Product Group.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Product_Group\Products\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Response object for the API endpoint that returns a list of Product Items in a particular Product Group.
includes/API/Catalog/Product_Item/Find/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Item\Find;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Find Product Item API request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Product_Item\Find;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Find Product Item API request object.
includes/API/Catalog/Product_Item/Response.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Product_Item;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Response object for API requests that return a Product Item.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Product_Item;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Response object for API requests that return a Product Item.
includes/API/Catalog/Request.php CHANGED
@@ -9,29 +9,25 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the Catalog API.
20
  *
21
- * @since 2.0.0
22
  */
23
- class Request extends API\Request {
24
-
25
-
26
  /**
27
  * Gets the rate limit ID.
28
  *
29
- * @since 2.1.0
30
- *
31
  * @return string
32
  */
33
- public static function get_rate_limit_id() {
34
-
35
  return 'ads_management';
36
  }
37
 
@@ -39,27 +35,9 @@ class Request extends API\Request {
39
  /**
40
  * API request constructor.
41
  *
42
- * @since 2.0.0
43
- *
44
  * @param string $catalog_id catalog ID
45
  */
46
- public function __construct( $catalog_id ) {
47
-
48
- parent::__construct( "/{$catalog_id}", 'GET' );
49
- }
50
-
51
-
52
- /**
53
- * Gets the request parameters.
54
- *
55
- * @since 2.0.0
56
- *
57
- * @return array
58
- */
59
- public function get_params() {
60
-
61
- return array( 'fields' => 'name' );
62
  }
63
-
64
-
65
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API\Request as ApiRequest;
17
 
18
  /**
19
  * Request object for the Catalog API.
20
  *
21
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/v13.0
22
  */
23
+ class Request extends ApiRequest
24
+ {
 
25
  /**
26
  * Gets the rate limit ID.
27
  *
 
 
28
  * @return string
29
  */
30
+ public static function get_rate_limit_id(): string {
 
31
  return 'ads_management';
32
  }
33
 
35
  /**
36
  * API request constructor.
37
  *
 
 
38
  * @param string $catalog_id catalog ID
39
  */
40
+ public function __construct( string $catalog_id ) {
41
+ parent::__construct("/{$catalog_id}?fields=name", 'GET');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
 
 
43
  }
includes/API/Catalog/Response.php CHANGED
@@ -1,37 +1,16 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
  /**
17
  * Catalog API response object
18
  *
19
- * @since 2.0.0
 
20
  */
21
- class Response extends \SkyVerge\WooCommerce\Facebook\API\Response {
22
-
23
-
24
- /**
25
- * Gets the catalog name.
26
- *
27
- * @since 2.0.0
28
- *
29
- * @return string|null
30
- */
31
- public function get_name() {
32
-
33
- return $this->name;
34
- }
35
-
36
-
37
- }
1
  <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Catalog;
 
 
 
 
 
 
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API\Response as ApiResponse;
9
 
10
  /**
11
  * Catalog API response object
12
  *
13
+ * @property-read string id Facebook Catalog ID.
14
+ * @property-read string name Facebook Catalog Name.
15
  */
16
+ class Response extends ApiResponse {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/API/Catalog/Send_Item_Updates/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the Send Item Updates API request.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Send_Item_Updates;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the Send Item Updates API request.
includes/API/Catalog/Send_Item_Updates/Response.php CHANGED
@@ -9,19 +9,16 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
-
18
  /**
19
  * Send_Item_Updates API response object
20
  *
21
  * @since 2.0.0
22
  */
23
- class Response extends \SkyVerge\WooCommerce\Facebook\API\Response {
24
-
25
 
26
  /**
27
  * Gets the handles field from the response.
@@ -31,9 +28,6 @@ class Response extends \SkyVerge\WooCommerce\Facebook\API\Response {
31
  * @return array|null
32
  */
33
  public function get_handles() {
34
-
35
  return $this->handles;
36
  }
37
-
38
-
39
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Catalog\Send_Item_Updates;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
 
 
16
  /**
17
  * Send_Item_Updates API response object
18
  *
19
  * @since 2.0.0
20
  */
21
+ class Response extends \WooCommerce\Facebook\API\Response {
 
22
 
23
  /**
24
  * Gets the handles field from the response.
28
  * @return array|null
29
  */
30
  public function get_handles() {
 
31
  return $this->handles;
32
  }
 
 
33
  }
includes/API/Exceptions/ConnectApiException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+
4
+ namespace WooCommerce\Facebook\API\Exceptions;
5
+
6
+ defined( 'ABSPATH' ) or exit;
7
+
8
+ /**
9
+ * Class Connect_WC_API_Exception.
10
+ * Exception is thrown when Connection with FB fails. @see \WooCommerce\Facebook\Handlers\Connection
11
+ *
12
+ * @package WooCommerce\Facebook\API\Exceptions
13
+ */
14
+ class ConnectApiException extends \Exception {}
includes/API/Exceptions/Request_Limit_Reached.php CHANGED
@@ -9,24 +9,22 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Exceptions;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
 
18
  /**
19
  * Exception thrown in response to a rate limiting error.
20
  *
21
  * @since 2.0.0
22
  */
23
- class Request_Limit_Reached extends Framework\SV_WC_API_Exception {
24
-
25
 
26
  /** @var \DateTime date & time representing when the request limit will be lifted */
27
  protected $throttle_end;
28
 
29
-
30
  /**
31
  * Gets the estimated throttle end.
32
  *
@@ -35,11 +33,9 @@ class Request_Limit_Reached extends Framework\SV_WC_API_Exception {
35
  * @return \DateTime|null
36
  */
37
  public function get_throttle_end() {
38
-
39
  return $this->throttle_end;
40
  }
41
 
42
-
43
  /**
44
  * Sets the estimated throttle end.
45
  *
@@ -48,9 +44,6 @@ class Request_Limit_Reached extends Framework\SV_WC_API_Exception {
48
  * @param \DateTime $date_time date time object representing when the throttle will end
49
  */
50
  public function set_throttle_end( \DateTime $date_time ) {
51
-
52
  $this->throttle_end = $date_time;
53
  }
54
-
55
-
56
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Exceptions;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
17
 
18
  /**
19
  * Exception thrown in response to a rate limiting error.
20
  *
21
  * @since 2.0.0
22
  */
23
+ class Request_Limit_Reached extends ApiException {
 
24
 
25
  /** @var \DateTime date & time representing when the request limit will be lifted */
26
  protected $throttle_end;
27
 
 
28
  /**
29
  * Gets the estimated throttle end.
30
  *
33
  * @return \DateTime|null
34
  */
35
  public function get_throttle_end() {
 
36
  return $this->throttle_end;
37
  }
38
 
 
39
  /**
40
  * Sets the estimated throttle end.
41
  *
44
  * @param \DateTime $date_time date time object representing when the throttle will end
45
  */
46
  public function set_throttle_end( \DateTime $date_time ) {
 
47
  $this->throttle_end = $date_time;
48
  }
 
 
49
  }
includes/API/FBE/Configuration/Messenger.php CHANGED
@@ -9,14 +9,14 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Configuration;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
  /**
17
  * The messenger configuration object.
18
  *
19
- * @since 2.0.0
20
  */
21
  class Messenger {
22
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\FBE\Configuration;
13
 
14
+ defined( 'ABSPATH' ) || exit;
15
 
16
  /**
17
  * The messenger configuration object.
18
  *
19
+ * @linlk https://developers.facebook.com/docs/facebook-business-extension/fbe/reference#FBEMessengerChatConfigData
20
  */
21
  class Messenger {
22
 
includes/API/FBE/Configuration/Read/Response.php CHANGED
@@ -1,80 +1,40 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Configuration\Read;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * FBE Configuration API read response object.
20
- *
21
- * @since 2.0.0
22
  */
23
  class Response extends API\Response {
24
-
25
-
26
  /**
27
  * Gets the messenger configuration object.
28
  *
29
- * @since 2.0.0
30
- *
31
- * @return null|API\FBE\Configuration\Messenger
32
  */
33
- public function get_messenger_configuration() {
34
-
35
- $configuration = null;
36
-
37
- if ( ! empty( $this->response_data->messenger_chat ) && is_object( $this->response_data->messenger_chat ) ) {
38
- $configuration = new API\FBE\Configuration\Messenger( (array) $this->response_data->messenger_chat );
39
- }
40
-
41
- return $configuration;
42
  }
43
 
44
  /**
45
  * Is Instagram Shopping enabled?
46
  *
47
- * @since 2.6.0
48
- *
49
  * @return boolean
50
  */
51
- public function is_ig_shopping_enabled() {
52
-
53
- $ig_shopping_enabled = false;
54
-
55
- if ( ! empty( $this->response_data->ig_shopping ) && is_object( $this->response_data->ig_shopping ) ) {
56
- $ig_shopping_enabled = ! ! $this->response_data->ig_shopping->enabled;
57
- }
58
-
59
- return $ig_shopping_enabled;
60
  }
61
 
62
  /**
63
  * Is Instagram CTA enabled?
64
  *
65
- * @since 2.6.0
66
- *
67
  * @return boolean
68
  */
69
- public function is_ig_cta_enabled() {
70
-
71
- $ig_cta_enabled = false;
72
-
73
- if ( ! empty( $this->response_data->ig_cta ) && is_object( $this->response_data->ig_cta ) ) {
74
- $ig_cta_enabled = ! ! $this->response_data->ig_cta->enabled;
75
- }
76
-
77
- return $ig_cta_enabled;
78
  }
79
-
80
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\FBE\Configuration\Read;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * FBE Configuration API read response object.
 
 
12
  */
13
  class Response extends API\Response {
 
 
14
  /**
15
  * Gets the messenger configuration object.
16
  *
17
+ * @return API\FBE\Configuration\Messenger
 
 
18
  */
19
+ public function get_messenger_configuration(): API\FBE\Configuration\Messenger {
20
+ return new API\FBE\Configuration\Messenger( $this->response_data['messenger_chat'] ?? [] );
 
 
 
 
 
 
 
21
  }
22
 
23
  /**
24
  * Is Instagram Shopping enabled?
25
  *
 
 
26
  * @return boolean
27
  */
28
+ public function is_ig_shopping_enabled(): bool {
29
+ return ! ! $this->response_data['ig_shopping']['enabled'] ?? false;
 
 
 
 
 
 
 
30
  }
31
 
32
  /**
33
  * Is Instagram CTA enabled?
34
  *
 
 
35
  * @return boolean
36
  */
37
+ public function is_ig_cta_enabled(): bool {
38
+ return ! ! $this->response_data['ig_cta']['enabled'];
 
 
 
 
 
 
 
39
  }
 
40
  }
includes/API/FBE/Configuration/Request.php CHANGED
@@ -1,44 +1,23 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Configuration;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * FBE Configuration API request object.
20
- *
21
- * @since 2.0.0
22
  */
23
  class Request extends API\Request {
24
-
25
-
26
  /**
27
  * API request constructor.
28
  *
29
- * @since 2.0.0
30
- *
31
  * @param string $external_business_id external business ID
32
  * @param string $method request method
33
  */
34
  public function __construct( $external_business_id, $method ) {
35
-
36
- parent::__construct( '/fbe_business', $method );
37
-
38
- $this->params = array(
39
- 'fbe_external_business_id' => $external_business_id,
40
- );
41
  }
42
-
43
-
44
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\FBE\Configuration;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * FBE Configuration API request object.
 
 
12
  */
13
  class Request extends API\Request {
 
 
14
  /**
15
  * API request constructor.
16
  *
 
 
17
  * @param string $external_business_id external business ID
18
  * @param string $method request method
19
  */
20
  public function __construct( $external_business_id, $method ) {
21
+ parent::__construct( '/fbe_business?fbe_external_business_id=' . $external_business_id, $method );
 
 
 
 
 
22
  }
 
 
23
  }
includes/API/FBE/Configuration/Update/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Configuration\Update;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API\FBE\Configuration;
17
 
18
  /**
19
  * FBE Configuration update request object.
@@ -31,9 +31,7 @@ class Request extends Configuration\Request {
31
  * @param string $external_business_id external business ID
32
  */
33
  public function __construct( $external_business_id ) {
34
-
35
  parent::__construct( $external_business_id, 'POST' );
36
-
37
  // include the business ID in the request body
38
  $this->data['fbe_external_business_id'] = $external_business_id;
39
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\FBE\Configuration\Update;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API\FBE\Configuration;
17
 
18
  /**
19
  * FBE Configuration update request object.
31
  * @param string $external_business_id external business ID
32
  */
33
  public function __construct( $external_business_id ) {
 
34
  parent::__construct( $external_business_id, 'POST' );
 
35
  // include the business ID in the request body
36
  $this->data['fbe_external_business_id'] = $external_business_id;
37
  }
includes/API/FBE/Configuration/Update/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\FBE\Configuration\Update;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Updating
14
+ * @property-read bool $success Either request was successful or not.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/FBE/Installation/Read/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Installation\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API\FBE\Installation;
17
 
18
  /**
19
  * FBE installation API read request object.
@@ -21,8 +21,6 @@ use SkyVerge\WooCommerce\Facebook\API\FBE\Installation;
21
  * @since 2.0.0
22
  */
23
  class Request extends Installation\Request {
24
-
25
-
26
  /**
27
  * API request constructor.
28
  *
@@ -31,13 +29,9 @@ class Request extends Installation\Request {
31
  * @param string $external_business_id external business_id
32
  */
33
  public function __construct( $external_business_id ) {
34
-
35
  parent::__construct( 'fbe_installs', 'GET' );
36
-
37
  $this->params = array(
38
  'fbe_external_business_id' => $external_business_id,
39
  );
40
  }
41
-
42
-
43
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\FBE\Installation\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API\FBE\Installation;
17
 
18
  /**
19
  * FBE installation API read request object.
21
  * @since 2.0.0
22
  */
23
  class Request extends Installation\Request {
 
 
24
  /**
25
  * API request constructor.
26
  *
29
  * @param string $external_business_id external business_id
30
  */
31
  public function __construct( $external_business_id ) {
 
32
  parent::__construct( 'fbe_installs', 'GET' );
 
33
  $this->params = array(
34
  'fbe_external_business_id' => $external_business_id,
35
  );
36
  }
 
 
37
  }
includes/API/FBE/Installation/Read/Response.php CHANGED
@@ -1,28 +1,17 @@
1
  <?php
2
  // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Installation\Read;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * FBE Installation API read response object.
20
- *
21
- * @since 2.0.0
22
  */
23
  class Response extends API\Response {
24
-
25
-
26
  /**
27
  * Gets the pixel ID.
28
  *
@@ -31,8 +20,7 @@ class Response extends API\Response {
31
  * @return string
32
  */
33
  public function get_pixel_id() {
34
-
35
- return ! empty( $this->get_data()->pixel_id ) ? $this->get_data()->pixel_id : '';
36
  }
37
 
38
 
@@ -44,8 +32,7 @@ class Response extends API\Response {
44
  * @return string
45
  */
46
  public function get_business_manager_id() {
47
-
48
- return ! empty( $this->get_data()->business_manager_id ) ? $this->get_data()->business_manager_id : '';
49
  }
50
 
51
 
@@ -57,40 +44,31 @@ class Response extends API\Response {
57
  * @return string
58
  */
59
  public function get_ad_account_id() {
60
-
61
- return ! empty( $this->get_data()->ad_account_id ) ? $this->get_data()->ad_account_id : '';
62
  }
63
 
64
 
65
  /**
66
- * Gets the catalog ID.
67
- *
68
- * @since 2.0.0
69
  *
70
  * @return string
71
  */
72
- public function get_catalog_id() {
73
-
74
- return ! empty( $this->get_data()->catalog_id ) ? $this->get_data()->catalog_id : '';
75
  }
76
 
77
 
78
  /**
79
- * Gets the page ID.
80
- *
81
- * @since 2.0.0
82
  *
83
  * @return string
84
  */
85
- public function get_page_id() {
86
-
87
- if ( empty( $pages = $this->get_data()->pages ) ) {
88
- return '';
89
- }
90
-
91
- return is_array( $pages ) ? current( $pages ) : $pages;
92
  }
93
 
 
94
  /**
95
  * Gets Instagram Business ID.
96
  *
@@ -99,13 +77,10 @@ class Response extends API\Response {
99
  * @return string
100
  */
101
  public function get_instagram_business_id() {
102
-
103
- $instagram_profiles = ! empty( $this->get_data()->instagram_profiles ) ? $this->get_data()->instagram_profiles : '';
104
-
105
  if ( empty( $instagram_profiles ) ) {
106
  return '';
107
  }
108
-
109
  return is_array( $instagram_profiles ) ? current( $instagram_profiles ) : $instagram_profiles;
110
  }
111
 
@@ -118,8 +93,7 @@ class Response extends API\Response {
118
  * @return string
119
  */
120
  public function get_commerce_merchant_settings_id() {
121
-
122
- return ! empty( $this->get_data()->commerce_merchant_settings_id ) ? $this->get_data()->commerce_merchant_settings_id : '';
123
  }
124
 
125
 
@@ -131,24 +105,16 @@ class Response extends API\Response {
131
  * @return string[]
132
  */
133
  public function get_profiles() {
134
-
135
- return ! empty( $this->get_data()->profiles ) ? $this->get_data()->profiles : array();
136
  }
137
 
138
 
139
  /**
140
  * Gets the response data.
141
  *
142
- * @since 2.0.0
143
- *
144
- * @return \stdClass
145
  */
146
  public function get_data() {
147
-
148
- $data = ! empty( $this->response_data->data ) && is_array( $this->response_data->data ) ? $this->response_data->data : array();
149
-
150
- return is_object( $data[0] ) ? $data[0] : new \stdClass();
151
  }
152
-
153
-
154
  }
1
  <?php
2
  // phpcs:ignoreFile
3
+ declare( strict_types=1 );
 
 
 
 
 
 
 
4
 
5
+ namespace WooCommerce\Facebook\API\FBE\Installation\Read;
6
 
7
+ defined( 'ABSPATH' ) || exit;
8
 
9
+ use WooCommerce\Facebook\API;
10
 
11
  /**
12
  * FBE Installation API read response object.
 
 
13
  */
14
  class Response extends API\Response {
 
 
15
  /**
16
  * Gets the pixel ID.
17
  *
20
  * @return string
21
  */
22
  public function get_pixel_id() {
23
+ return $this->get_data()['pixel_id'] ?? '';
 
24
  }
25
 
26
 
32
  * @return string
33
  */
34
  public function get_business_manager_id() {
35
+ return $this->get_data()['business_manager_id'] ?? '';
 
36
  }
37
 
38
 
44
  * @return string
45
  */
46
  public function get_ad_account_id() {
47
+ return $this->get_data()['ad_account_id'] ?? '';
 
48
  }
49
 
50
 
51
  /**
52
+ * Returns Facebook Catalog id.
 
 
53
  *
54
  * @return string
55
  */
56
+ public function get_catalog_id(): string {
57
+ return $this->get_data()['catalog_id'] ?? '';
 
58
  }
59
 
60
 
61
  /**
62
+ * Returns facebook page id.
 
 
63
  *
64
  * @return string
65
  */
66
+ public function get_page_id(): string {
67
+ $pages = $this->get_data()['pages'] ?? '';
68
+ return is_array( $pages ) ? current( $pages ) : '';
 
 
 
 
69
  }
70
 
71
+
72
  /**
73
  * Gets Instagram Business ID.
74
  *
77
  * @return string
78
  */
79
  public function get_instagram_business_id() {
80
+ $instagram_profiles = $this->get_data()['instagram_profiles'] ?? '';
 
 
81
  if ( empty( $instagram_profiles ) ) {
82
  return '';
83
  }
 
84
  return is_array( $instagram_profiles ) ? current( $instagram_profiles ) : $instagram_profiles;
85
  }
86
 
93
  * @return string
94
  */
95
  public function get_commerce_merchant_settings_id() {
96
+ return $this->get_data()['commerce_merchant_settings_id'] ?? '';
 
97
  }
98
 
99
 
105
  * @return string[]
106
  */
107
  public function get_profiles() {
108
+ return $this->get_data()['profiles'] ?? [];
 
109
  }
110
 
111
 
112
  /**
113
  * Gets the response data.
114
  *
115
+ * @return array
 
 
116
  */
117
  public function get_data() {
118
+ return $this->response_data['data'][0] ?? [];
 
 
 
119
  }
 
 
120
  }
includes/API/FBE/Installation/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\FBE\Installation;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * FBE API request object.
@@ -22,7 +22,6 @@ use SkyVerge\WooCommerce\Facebook\API;
22
  */
23
  class Request extends API\Request {
24
 
25
-
26
  /**
27
  * API request constructor.
28
  *
@@ -32,9 +31,6 @@ class Request extends API\Request {
32
  * @param string $method request method
33
  */
34
  public function __construct( $path, $method ) {
35
-
36
  parent::__construct( "/fbe_business/{$path}", $method );
37
  }
38
-
39
-
40
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\FBE\Installation;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * FBE API request object.
22
  */
23
  class Request extends API\Request {
24
 
 
25
  /**
26
  * API request constructor.
27
  *
31
  * @param string $method request method
32
  */
33
  public function __construct( $path, $method ) {
 
34
  parent::__construct( "/fbe_business/{$path}", $method );
35
  }
 
 
36
  }
includes/API/Log/Create/Request.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Log\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Log > Create Graph Api.
12
+ */
13
+ class Request extends ApiRequest {
14
+
15
+ /**
16
+ * @param string $facebook_external_merchant_settings_id Facebook External Merchant Settings ID.
17
+ * @param string $message Message.
18
+ * @param string $error Error.
19
+ */
20
+ public function __construct( $facebook_external_merchant_settings_id, $message, $error ) {
21
+ parent::__construct( "/{$facebook_external_merchant_settings_id}/log_events", 'POST' );
22
+ $data = [
23
+ 'message' => $message,
24
+ 'error' => $error,
25
+ ];
26
+ parent::set_data( $data );
27
+ }
28
+ }
includes/API/Log/Create/Response.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Log\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Log > Create Graph Api.
12
+ */
13
+ class Response extends ApiResponse {}
includes/API/Orders/Abstract_Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  abstract class Abstract_Request extends API\Request {
19
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  abstract class Abstract_Request extends API\Request {
19
 
includes/API/Orders/Acknowledge/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Acknowledge;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API acknowledge request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Acknowledge;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API acknowledge request object.
includes/API/Orders/Cancel/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Cancel;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API cancel request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Cancel;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API cancel request object.
includes/API/Orders/Fulfillment/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Fulfillment;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API fulfillment request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Fulfillment;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API fulfillment request object.
includes/API/Orders/Order.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/API/Orders/Read/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API read request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API read request object.
includes/API/Orders/Read/Response.php CHANGED
@@ -9,12 +9,12 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
- use SkyVerge\WooCommerce\Facebook\API\Orders\Order;
18
 
19
  /**
20
  * Orders API read response object.
@@ -29,7 +29,7 @@ class Response extends API\Response {
29
  *
30
  * @since 2.1.0
31
  *
32
- * @return \SkyVerge\WooCommerce\Facebook\API\Orders\Order
33
  */
34
  public function get_order() {
35
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Read;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
+ use WooCommerce\Facebook\API\Orders\Order;
18
 
19
  /**
20
  * Orders API read response object.
29
  *
30
  * @since 2.1.0
31
  *
32
+ * @return \WooCommerce\Facebook\API\Orders\Order
33
  */
34
  public function get_order() {
35
 
includes/API/Orders/Refund/Request.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders\Refund;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API refund request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders\Refund;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API refund request object.
includes/API/Orders/Request.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/API/Orders/Response.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API list response object.
@@ -31,7 +31,7 @@ class Response extends API\Response {
31
  *
32
  * @since 2.1.0
33
  *
34
- * @return \SkyVerge\WooCommerce\Facebook\API\Orders\Order[]
35
  */
36
  public function get_orders() {
37
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Orders;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Orders API list response object.
31
  *
32
  * @since 2.1.0
33
  *
34
+ * @return \WooCommerce\Facebook\API\Orders\Order[]
35
  */
36
  public function get_orders() {
37
 
includes/API/Pages/Read/Request.php CHANGED
@@ -1,19 +1,11 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Pages\Read;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Page API request object.
@@ -21,32 +13,12 @@ use SkyVerge\WooCommerce\Facebook\API;
21
  * @since 2.0.0
22
  */
23
  class Request extends API\Request {
24
-
25
-
26
  /**
27
  * API request constructor.
28
  *
29
- * @since 2.0.0
30
- *
31
- * @param string $page_id page ID
32
  */
33
  public function __construct( $page_id ) {
34
-
35
- parent::__construct( "/{$page_id}", 'GET' );
36
- }
37
-
38
-
39
- /**
40
- * Gets the request parameters.
41
- *
42
- * @since 2.0.0
43
- *
44
- * @return array
45
- */
46
- public function get_params() {
47
-
48
- return array( 'fields' => 'name,link' );
49
  }
50
-
51
-
52
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\Pages\Read;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * Page API request object.
13
  * @since 2.0.0
14
  */
15
  class Request extends API\Request {
 
 
16
  /**
17
  * API request constructor.
18
  *
19
+ * @param string $page_id Facebook Page ID.
 
 
20
  */
21
  public function __construct( $page_id ) {
22
+ parent::__construct( "/{$page_id}/?fields=name,link", 'GET' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
 
 
24
  }
includes/API/Pages/Read/Response.php CHANGED
@@ -1,52 +1,17 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Pages\Read;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Page API response object.
20
  *
21
  * @since 2.0.0
 
 
22
  */
23
- class Response extends API\Response {
24
-
25
-
26
- /**
27
- * Gets the page name.
28
- *
29
- * @since 2.0.0
30
- *
31
- * @return string|null
32
- */
33
- public function get_name() {
34
-
35
- return $this->name;
36
- }
37
-
38
-
39
- /**
40
- * Gets the page URL.
41
- *
42
- * @since 2.0.0
43
- *
44
- * @return string|null
45
- */
46
- public function get_url() {
47
-
48
- return $this->link;
49
- }
50
-
51
-
52
- }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\Pages\Read;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * Page API response object.
12
  *
13
  * @since 2.0.0
14
+ * @property-read string $name Facebook Page Name.
15
+ * @property-read string $link Facebook Page URL.
16
  */
17
+ class Response extends API\Response {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/API/Pixel/Events/Request.php CHANGED
@@ -9,12 +9,12 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Pixel\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
- use SkyVerge\WooCommerce\Facebook\Events\Event;
18
 
19
  /**
20
  * Base S2S API request object.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Pixel\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\API;
17
+ use WooCommerce\Facebook\Events\Event;
18
 
19
  /**
20
  * Base S2S API request object.
includes/API/ProductCatalog/ItemsBatch/Create/Request.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ItemsBatch\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Items Batch > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/items_batch/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_catalog_id Facebook Product Catalog ID.
19
+ * @param array $requests Array of JSON objects containing batch requests. Each batch request consists of method and data fields.
20
+ */
21
+ public function __construct( string $product_catalog_id, array $requests ) {
22
+ parent::__construct( "/{$product_catalog_id}/items_batch", 'POST' );
23
+ $data = [
24
+ 'allow_upsert' => true,
25
+ 'requests' => $requests,
26
+ 'item_type' => 'PRODUCT_ITEM',
27
+ ];
28
+ parent::set_data( $data );
29
+ }
30
+ }
includes/API/ProductCatalog/ItemsBatch/Create/Response.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ItemsBatch\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Items Batch > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/items_batch/
14
+ * @property-read string[] handles Either request was successful or not.
15
+ * @property-read array validation_status
16
+ */
17
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductFeedUploads/Read/Request.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeedUploads\Read;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed-upload/#read_examples
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_feed_upload_id Facebook Product Feed Upload ID.
19
+ */
20
+ public function __construct( string $product_feed_upload_id ) {
21
+ parent::__construct( "/{$product_feed_upload_id}/?fields=error_count,warning_count,num_detected_items,num_persisted_items,url,end_time", 'GET' );
22
+ }
23
+ }
includes/API/ProductCatalog/ProductFeedUploads/Read/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeedUploads\Read;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed-upload/#read_examples
14
+ * @property-read array $data Facebook Product Feeds.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductFeeds/Create/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Feeds > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_feeds/v13.0#Creating
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_catalog_id Facebook Product Catalog ID.
19
+ * @param array $data Facebook Product Feed Data.
20
+ */
21
+ public function __construct( string $product_catalog_id, array $data ) {
22
+ parent::__construct( "/{$product_catalog_id}/product_feeds", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/ProductFeeds/Create/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Feeds > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_feeds/v13.0#Creating
14
+ * @property-read string id Facebook Product Feed ID.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductFeeds/Read/Request.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\Read;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed/#Reading
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_feed_id Facebook Product Feed ID.
19
+ */
20
+ public function __construct( string $product_feed_id ) {
21
+ parent::__construct( "/{$product_feed_id}/?fields=created_time,latest_upload,product_count,schedule,update_schedule", 'GET' );
22
+ }
23
+ }
includes/API/ProductCatalog/ProductFeeds/Read/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\Read;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed/#Reading
14
+ * @property-read array $data Facebook Product Feeds.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductFeeds/ReadAll/Request.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\ReadAll;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_feeds/v13.0#Reading
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_catalog_id Facebook Product Catalog ID.
19
+ */
20
+ public function __construct( string $product_catalog_id ) {
21
+ parent::__construct( "/{$product_catalog_id}/product_feeds", 'GET' );
22
+ }
23
+ }
includes/API/ProductCatalog/ProductFeeds/ReadAll/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeeds\ReadAll;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Feeds > Read Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_feeds/v13.0#Reading
14
+ * @property-read array data Facebook Product Feeds.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductGroups/Create/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_groups/#Creating
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_catalog_id Facebook Product Catalog ID.
19
+ * @param array $data Facebook Product Group Data.
20
+ */
21
+ public function __construct( string $product_catalog_id, array $data ) {
22
+ parent::__construct( "/{$product_catalog_id}/product_groups", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/ProductGroups/Create/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_groups/#Creating
14
+ * @property-read string id Facebook Product Group ID.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductGroups/Delete/Request.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_groups/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_group_id Facebook Product Group ID.
19
+ */
20
+ public function __construct( string $product_group_id ) {
21
+ parent::__construct( "/{$product_group_id}?deletion_method=delete_items", 'DELETE' );
22
+ }
23
+ }
includes/API/ProductCatalog/ProductGroups/Delete/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Deleting
14
+ * @property-read bool $success
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductGroups/Read/Request.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Read;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_groups/#Reading
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_group_id Facebook Product Group ID.
19
+ * @param int $limit Limit.
20
+ */
21
+ public function __construct( string $product_group_id, int $limit ) {
22
+ parent::__construct( "/{$product_group_id}/products?fields=id,retailer_id&limit={$limit}", 'GET' );
23
+ }
24
+ }
includes/API/ProductCatalog/ProductGroups/Read/Response.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Read;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+ use WooCommerce\Facebook\API\Traits\Paginated_Response;
8
+
9
+ defined( 'ABSPATH' ) || exit;
10
+
11
+ /**
12
+ * Response object for Product Catalog > Product Groups > Update Graph Api.
13
+ *
14
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Updating
15
+ * @property-read array data A list of ProductGroup nodes.
16
+ * @property-read array paging Paging cursors and next data link.
17
+ * @property-read array summary Aggregated information about the edge, such as counts. Specify the fields to fetch in the summary param (like summary=total_count).
18
+ */
19
+ class Response extends ApiResponse {
20
+
21
+ use Paginated_Response;
22
+
23
+ /**
24
+ * Gets the Product Item IDs indexed by the retailer ID.
25
+ *
26
+ * @return string[]
27
+ */
28
+ public function get_ids(): array {
29
+ $product_item_ids = [];
30
+ foreach ( $this->data as $entry ) {
31
+ $product_item_ids[ $entry['retailer_id'] ] = $entry['id'];
32
+ }
33
+ return $product_item_ids;
34
+ }
35
+ }
includes/API/ProductCatalog/ProductGroups/Update/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Update;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/v13.0
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_group_id Facebook Product Group ID.
19
+ * @param array $data Facebook Product Group Data.
20
+ */
21
+ public function __construct( string $product_group_id, array $data ) {
22
+ parent::__construct( "/{$product_group_id}", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/ProductGroups/Update/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductGroups\Update;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Updating
14
+ * @property-read bool $success Either request was successful or not.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductSets/Create/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Sets > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_catalog_id Facebook Product Catalog ID.
19
+ * @param array $data Facebook Product Set Data.
20
+ */
21
+ public function __construct( string $product_catalog_id, array $data ) {
22
+ parent::__construct( "/{$product_catalog_id}/product_sets", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/ProductSets/Create/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ * @property-read string id Facebook Product Set ID.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductSets/Delete/Request.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Sets > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_set_id Facebook Product Set ID.
19
+ * @param bool $allow_live_deletion Allow live Facebook Product Set Deletion.
20
+ */
21
+ public function __construct( string $product_set_id, bool $allow_live_deletion = false ) {
22
+ $path = "/{$product_set_id}";
23
+ $path .= $allow_live_deletion ? '?allow_live_product_set_deletion=true' : '';
24
+ parent::__construct( $path, 'DELETE' );
25
+ }
26
+ }
includes/API/ProductCatalog/ProductSets/Delete/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Sets > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ * @property-read bool $success
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/ProductSets/Update/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Update;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Sets > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_set_id Facebook Product Set ID.
19
+ * @param array $data Facebook Product Set Data.
20
+ */
21
+ public function __construct( string $product_set_id, array $data ) {
22
+ parent::__construct( "/{$product_set_id}", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/ProductSets/Update/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\ProductSets\Update;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Sets > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/product_sets/
14
+ * @property-read bool $success Either request was successful or not.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/Products/Create/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Create;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Products > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/#Creating
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $product_group_id Facebook Product Group ID.
19
+ * @param array $data Facebook Product Data.
20
+ */
21
+ public function __construct( string $product_group_id, array $data ) {
22
+ parent::__construct( "/{$product_group_id}/products", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/Products/Create/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Create;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Products > Create Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/#Creating
14
+ * @property-read string id Facebook Product ID.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/Products/Delete/Request.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Products > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-item/#Deleting
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $facebook_product_id Facebook Product Group ID.
19
+ */
20
+ public function __construct( string $facebook_product_id ) {
21
+ parent::__construct( "/{$facebook_product_id}", 'DELETE' );
22
+ }
23
+ }
includes/API/ProductCatalog/Products/Delete/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Delete;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Delete Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/#Deleting
14
+ * @property-read bool $success
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/ProductCatalog/Products/Id/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Id;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Products > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $facebook_product_catalog_id Facebook Product Catalog ID.
19
+ * @param string $facebook_product_retailer_id Facebook Product Retailer ID.
20
+ */
21
+ public function __construct( string $facebook_product_catalog_id, string $facebook_product_retailer_id ) {
22
+ $path = "catalog:{$facebook_product_catalog_id}:" . base64_encode( $facebook_product_retailer_id );
23
+ parent::__construct( "/{$path}/?fields=id,product_group{id}", 'GET' );
24
+ }
25
+ }
includes/API/ProductCatalog/Products/Id/Response.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Id;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
14
+ * @property-read string id Either request was successful or not.
15
+ * @property-read array product_group Product group data container containing facebook product group id
16
+ * e.g. product_group => [ id => <facebook product group id>]
17
+ */
18
+ class Response extends ApiResponse {
19
+ /**
20
+ * Returns product's Facebook product group id.
21
+ *
22
+ * @return string
23
+ */
24
+ public function get_facebook_product_group_id(): string {
25
+ return $this->product_group['id'] ?? '';
26
+ }
27
+ }
includes/API/ProductCatalog/Products/Update/Request.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Update;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Product Catalog > Product Groups > Products > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
14
+ */
15
+ class Request extends ApiRequest {
16
+
17
+ /**
18
+ * @param string $facebook_product_id Facebook Product ID.
19
+ * @param array $data Facebook Product Data.
20
+ */
21
+ public function __construct( string $facebook_product_id, array $data ) {
22
+ parent::__construct( "/{$facebook_product_id}", 'POST' );
23
+ parent::set_data( $data );
24
+ }
25
+ }
includes/API/ProductCatalog/Products/Update/Response.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\ProductCatalog\Products\Update;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Product Catalog > Product Groups > Update Graph Api.
12
+ *
13
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-group/products/
14
+ * @property-read bool $success Either request was successful or not.
15
+ */
16
+ class Response extends ApiResponse {}
includes/API/Request.php CHANGED
@@ -1,31 +1,21 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
 
18
  /**
19
  * Base API request object.
20
  *
21
  * @since 2.0.0
22
  */
23
- class Request extends Framework\SV_WC_API_JSON_Request {
24
-
25
 
26
  use Traits\Rate_Limited_Request;
27
 
28
-
29
  /** @var int maximum number of retries to attempt if told to do so by Facebook */
30
  protected $retry_limit = 5;
31
 
@@ -33,8 +23,7 @@ class Request extends Framework\SV_WC_API_JSON_Request {
33
  protected $retry_count = 0;
34
 
35
  /** @var int[] the response codes that should trigger a retry */
36
- protected $retry_codes = array();
37
-
38
 
39
  /**
40
  * API request constructor.
@@ -45,7 +34,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
45
  * @param string $method HTTP method
46
  */
47
  public function __construct( $path, $method ) {
48
-
49
  $this->method = $method;
50
  $this->path = $path;
51
  }
@@ -59,7 +47,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
59
  * @param array $params request parameters
60
  */
61
  public function set_params( $params ) {
62
-
63
  $this->params = $params;
64
  }
65
 
@@ -72,7 +59,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
72
  * @param array $data request data
73
  */
74
  public function set_data( $data ) {
75
-
76
  $this->data = $data;
77
  }
78
 
@@ -85,7 +71,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
85
  * @return int
86
  */
87
  public function get_retry_count() {
88
-
89
  return $this->retry_count;
90
  }
91
 
@@ -96,7 +81,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
96
  * @since 2.1.0
97
  */
98
  public function mark_retry() {
99
-
100
  $this->retry_count++;
101
  }
102
 
@@ -109,7 +93,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
109
  * @return int
110
  */
111
  public function get_retry_limit() {
112
-
113
  /**
114
  * Filters the maximum number of retries allowed for the request.
115
  *
@@ -130,9 +113,6 @@ class Request extends Framework\SV_WC_API_JSON_Request {
130
  * @return int[]
131
  */
132
  public function get_retry_codes() {
133
-
134
  return $this->retry_codes;
135
  }
136
-
137
-
138
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API;
5
 
6
+ use WooCommerce\Facebook\Framework\Api\JSONRequest;
7
 
8
+ defined( 'ABSPATH' ) || exit;
9
 
10
  /**
11
  * Base API request object.
12
  *
13
  * @since 2.0.0
14
  */
15
+ class Request extends JSONRequest {
 
16
 
17
  use Traits\Rate_Limited_Request;
18
 
 
19
  /** @var int maximum number of retries to attempt if told to do so by Facebook */
20
  protected $retry_limit = 5;
21
 
23
  protected $retry_count = 0;
24
 
25
  /** @var int[] the response codes that should trigger a retry */
26
+ protected $retry_codes = [];
 
27
 
28
  /**
29
  * API request constructor.
34
  * @param string $method HTTP method
35
  */
36
  public function __construct( $path, $method ) {
 
37
  $this->method = $method;
38
  $this->path = $path;
39
  }
47
  * @param array $params request parameters
48
  */
49
  public function set_params( $params ) {
 
50
  $this->params = $params;
51
  }
52
 
59
  * @param array $data request data
60
  */
61
  public function set_data( $data ) {
 
62
  $this->data = $data;
63
  }
64
 
71
  * @return int
72
  */
73
  public function get_retry_count() {
 
74
  return $this->retry_count;
75
  }
76
 
81
  * @since 2.1.0
82
  */
83
  public function mark_retry() {
 
84
  $this->retry_count++;
85
  }
86
 
93
  * @return int
94
  */
95
  public function get_retry_limit() {
 
96
  /**
97
  * Filters the maximum number of retries allowed for the request.
98
  *
113
  * @return int[]
114
  */
115
  public function get_retry_codes() {
 
116
  return $this->retry_codes;
117
  }
 
 
118
  }
includes/API/Response.php CHANGED
@@ -9,23 +9,21 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
 
18
  /**
19
  * Base API response object
20
  *
21
  * @since 2.0.0
22
  */
23
- class Response extends Framework\SV_WC_API_JSON_Response {
24
-
25
 
26
  use Traits\Rate_Limited_Response;
27
 
28
-
29
  /**
30
  * Gets the response ID.
31
  *
@@ -34,7 +32,6 @@ class Response extends Framework\SV_WC_API_JSON_Response {
34
  * @return string
35
  */
36
  public function get_id() {
37
-
38
  return $this->id;
39
  }
40
 
@@ -49,7 +46,6 @@ class Response extends Framework\SV_WC_API_JSON_Response {
49
  * @return boolean
50
  */
51
  public function has_api_error() {
52
-
53
  return (bool) $this->error;
54
  }
55
 
@@ -62,8 +58,7 @@ class Response extends Framework\SV_WC_API_JSON_Response {
62
  * @return string|null
63
  */
64
  public function get_api_error_type() {
65
-
66
- return isset( $this->error->type ) ? $this->error->type : null;
67
  }
68
 
69
 
@@ -75,8 +70,7 @@ class Response extends Framework\SV_WC_API_JSON_Response {
75
  * @return string|null
76
  */
77
  public function get_api_error_message() {
78
-
79
- return isset( $this->error->message ) ? $this->error->message : null;
80
  }
81
 
82
 
@@ -88,8 +82,7 @@ class Response extends Framework\SV_WC_API_JSON_Response {
88
  * @return int|null
89
  */
90
  public function get_api_error_code() {
91
-
92
- return isset( $this->error->code ) ? (int) $this->error->code : null;
93
  }
94
 
95
 
@@ -101,9 +94,6 @@ class Response extends Framework\SV_WC_API_JSON_Response {
101
  * @return string|null
102
  */
103
  public function get_user_error_message() {
104
-
105
- return isset( $this->error->error_user_msg ) ? $this->error->error_user_msg : null;
106
  }
107
-
108
-
109
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API;
13
 
14
+ use WooCommerce\Facebook\Framework\Api\JSONResponse;
15
 
16
+ defined( 'ABSPATH' ) or exit;
17
 
18
  /**
19
  * Base API response object
20
  *
21
  * @since 2.0.0
22
  */
23
+ class Response extends JSONResponse {
 
24
 
25
  use Traits\Rate_Limited_Response;
26
 
 
27
  /**
28
  * Gets the response ID.
29
  *
32
  * @return string
33
  */
34
  public function get_id() {
 
35
  return $this->id;
36
  }
37
 
46
  * @return boolean
47
  */
48
  public function has_api_error() {
 
49
  return (bool) $this->error;
50
  }
51
 
58
  * @return string|null
59
  */
60
  public function get_api_error_type() {
61
+ return $this->error['type'] ?? null;
 
62
  }
63
 
64
 
70
  * @return string|null
71
  */
72
  public function get_api_error_message() {
73
+ return $this->error['message'] ?? null;
 
74
  }
75
 
76
 
82
  * @return int|null
83
  */
84
  public function get_api_error_code() {
85
+ return $this->error['code'] ?? null;
 
86
  }
87
 
88
 
94
  * @return string|null
95
  */
96
  public function get_user_error_message() {
97
+ return $this->error['error_user_msg'] ?? null;
 
98
  }
 
 
99
  }
includes/API/Tip/Log/Request.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Tip\Log;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Tip > Log Graph Api.
12
+ */
13
+ class Request extends ApiRequest {
14
+
15
+ /**
16
+ * @param string $tip_id Tip ID.
17
+ * @param string $channel_id Channel ID.
18
+ * @param string $event Event Data.
19
+ */
20
+ public function __construct( $tip_id, $channel_id, $event ) {
21
+ parent::__construct( '/log_tip_events', 'POST' );
22
+ $data = [
23
+ 'tip_id' => $tip_id,
24
+ 'channel_id' => $channel_id,
25
+ 'event' => $event,
26
+ ];
27
+ parent::set_data( $data );
28
+ }
29
+ }
includes/API/Tip/Log/Response.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Tip\Log;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Tip > Log Graph Api.
12
+ */
13
+ class Response extends ApiResponse {}
includes/API/Tip/Read/Request.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Tip\Read;
5
+
6
+ use WooCommerce\Facebook\API\Request as ApiRequest;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Request object for Tip > Read Graph Api.
12
+ */
13
+ class Request extends ApiRequest {
14
+
15
+ /**
16
+ * @param string $external_merchant_settings_id External Merchant e.g. Shopify, etc.
17
+ */
18
+ public function __construct( string $external_merchant_settings_id ) {
19
+ parent::__construct( "/{$external_merchant_settings_id}/?fields=connect_woo", 'GET' );
20
+ }
21
+ }
includes/API/Tip/Read/Response.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\Tip\Read;
5
+
6
+ use WooCommerce\Facebook\API\Response as ApiResponse;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Response object for Tip > Read Graph Api.
12
+ *
13
+ * @property-read array tip_title
14
+ * @property-read array tip_body
15
+ * @property-read string tip_action_link
16
+ * @property-read array tip_action
17
+ * @property-read string tip_img_url
18
+ */
19
+ class Response extends ApiResponse {
20
+ /**
21
+ * Returns tip title html content.
22
+ *
23
+ * @return string
24
+ */
25
+ public function get_tip_title_html(): string {
26
+ return $this->tip_title['__html'] ?? '';
27
+ }
28
+
29
+ /**
30
+ * Returns tip body html content.
31
+ *
32
+ * @return string
33
+ */
34
+ public function get_tip_body_html(): string {
35
+ return $this->tip_body['__html'] ?? '';
36
+ }
37
+
38
+ /**
39
+ * Returns tip action html content.
40
+ *
41
+ * @return string
42
+ */
43
+ public function get_tip_action_html(): string {
44
+ return $this->tip_action['_html'] ?? '';
45
+ }
46
+ }
includes/API/Traits/Idempotent_Request.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/API/Traits/Paginated_Response.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/API/Traits/Rate_Limited_API.php CHANGED
@@ -9,9 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Traits;
13
-
14
- use SkyVerge\WooCommerce\Facebook\API\Response;
15
 
16
  defined( 'ABSPATH' ) or exit;
17
 
@@ -34,15 +32,10 @@ trait Rate_Limited_API {
34
  * @param int $delay delay in seconds
35
  */
36
  public function set_rate_limit_delay( $rate_limit_id, $delay ) {
37
-
38
  if ( ! empty( $delay ) ) {
39
-
40
  $expiration = min( $delay, 24 * HOUR_IN_SECONDS );
41
-
42
  set_transient( "wc_facebook_rate_limit_${rate_limit_id}", $delay, $expiration );
43
-
44
  } else {
45
-
46
  delete_transient( "wc_facebook_rate_limit_${rate_limit_id}" );
47
  }
48
  }
@@ -57,7 +50,6 @@ trait Rate_Limited_API {
57
  * @return int
58
  */
59
  public function get_rate_limit_delay( $rate_limit_id ) {
60
-
61
  return (int) get_transient( "wc_facebook_rate_limit_${rate_limit_id}" );
62
  }
63
 
@@ -73,9 +65,6 @@ trait Rate_Limited_API {
73
  * @return int delay in seconds
74
  */
75
  protected function calculate_rate_limit_delay( $response, $headers ) {
76
-
77
  return $response->get_rate_limit_estimated_time_to_regain_access( $headers ) * MINUTE_IN_SECONDS;
78
  }
79
-
80
-
81
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Traits;
 
 
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
32
  * @param int $delay delay in seconds
33
  */
34
  public function set_rate_limit_delay( $rate_limit_id, $delay ) {
 
35
  if ( ! empty( $delay ) ) {
 
36
  $expiration = min( $delay, 24 * HOUR_IN_SECONDS );
 
37
  set_transient( "wc_facebook_rate_limit_${rate_limit_id}", $delay, $expiration );
 
38
  } else {
 
39
  delete_transient( "wc_facebook_rate_limit_${rate_limit_id}" );
40
  }
41
  }
50
  * @return int
51
  */
52
  public function get_rate_limit_delay( $rate_limit_id ) {
 
53
  return (int) get_transient( "wc_facebook_rate_limit_${rate_limit_id}" );
54
  }
55
 
65
  * @return int delay in seconds
66
  */
67
  protected function calculate_rate_limit_delay( $response, $headers ) {
 
68
  return $response->get_rate_limit_estimated_time_to_regain_access( $headers ) * MINUTE_IN_SECONDS;
69
  }
 
 
70
  }
includes/API/Traits/Rate_Limited_Request.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
@@ -20,7 +20,6 @@ defined( 'ABSPATH' ) or exit;
20
  */
21
  trait Rate_Limited_Request {
22
 
23
-
24
  /**
25
  * Gets the ID of this request for rate limiting purposes.
26
  *
@@ -29,9 +28,6 @@ trait Rate_Limited_Request {
29
  * @return string
30
  */
31
  public static function get_rate_limit_id() {
32
-
33
  return 'graph_api_request';
34
  }
35
-
36
-
37
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
20
  */
21
  trait Rate_Limited_Request {
22
 
 
23
  /**
24
  * Gets the ID of this request for rate limiting purposes.
25
  *
28
  * @return string
29
  */
30
  public static function get_rate_limit_id() {
 
31
  return 'graph_api_request';
32
  }
 
 
33
  }
includes/API/Traits/Rate_Limited_Response.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\API\Traits;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/API/User/Permissions/Delete/Request.php CHANGED
@@ -1,19 +1,11 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\User\Permissions\Delete;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the Business Manager API.
@@ -22,7 +14,6 @@ use SkyVerge\WooCommerce\Facebook\API;
22
  */
23
  class Request extends API\Request {
24
 
25
-
26
  /**
27
  * API request constructor.
28
  *
@@ -32,9 +23,6 @@ class Request extends API\Request {
32
  * @param string $permission permission to revoke
33
  */
34
  public function __construct( $user_id, $permission ) {
35
-
36
  parent::__construct( "/{$user_id}/permissions/{$permission}", 'DELETE' );
37
  }
38
-
39
-
40
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\User\Permissions\Delete;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * Request object for the Business Manager API.
14
  */
15
  class Request extends API\Request {
16
 
 
17
  /**
18
  * API request constructor.
19
  *
23
  * @param string $permission permission to revoke
24
  */
25
  public function __construct( $user_id, $permission ) {
 
26
  parent::__construct( "/{$user_id}/permissions/{$permission}", 'DELETE' );
27
  }
 
 
28
  }
includes/API/User/Permissions/Delete/Response.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace WooCommerce\Facebook\API\User\Permissions\Delete;
5
+
6
+ defined( 'ABSPATH' ) || exit;
7
+
8
+ use WooCommerce\Facebook\API;
9
+
10
+ /**
11
+ * User API response object
12
+ *
13
+ * @property-read bool success Facebook User ID.
14
+ */
15
+ class Response extends API\Response {}
includes/API/User/Request.php CHANGED
@@ -1,45 +1,23 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\User;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\API;
17
 
18
  /**
19
  * Request object for the User API.
20
- *
21
- * @since 2.0.0
22
  */
23
  class Request extends API\Request {
24
-
25
-
26
  /**
27
  * API request constructor.
28
  *
29
- * @since 2.0.0
30
- *
31
- * @param string $user_id user ID
32
  */
33
- public function __construct( $user_id = '' ) {
34
-
35
- if ( $user_id ) {
36
- $path = "/{$user_id}";
37
- } else {
38
- $path = '/me';
39
- }
40
-
41
  parent::__construct( $path, 'GET' );
42
  }
43
-
44
-
45
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\User;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * Request object for the User API.
 
 
12
  */
13
  class Request extends API\Request {
 
 
14
  /**
15
  * API request constructor.
16
  *
17
+ * @param string $user_id Facebook User ID.
 
 
18
  */
19
+ public function __construct( string $user_id = '' ) {
20
+ $path = $user_id ? "/{$user_id}" : '/me';
 
 
 
 
 
 
21
  parent::__construct( $path, 'GET' );
22
  }
 
 
23
  }
includes/API/User/Response.php CHANGED
@@ -1,24 +1,16 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\API\User;
13
 
14
- defined( 'ABSPATH' ) or exit;
 
 
15
 
16
  /**
17
  * User API response object
18
  *
19
- * @since 2.0.0
 
20
  */
21
- class Response extends \SkyVerge\WooCommerce\Facebook\API\Response {
22
-
23
- }
24
-
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\API\User;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
+
8
+ use WooCommerce\Facebook\API;
9
 
10
  /**
11
  * User API response object
12
  *
13
+ * @property-read string id Facebook User ID.
14
+ * @property-read string name Facebook User Name.
15
  */
16
+ class Response extends API\Response {}
 
 
 
includes/Admin.php CHANGED
@@ -9,9 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Helper;
 
 
15
  use Automattic\WooCommerce\Utilities\OrderUtil;
16
 
17
  defined( 'ABSPATH' ) or exit;
@@ -23,7 +25,6 @@ defined( 'ABSPATH' ) or exit;
23
  */
24
  class Admin {
25
 
26
-
27
  /** @var string the "sync and show" sync mode slug */
28
  const SYNC_MODE_SYNC_AND_SHOW = 'sync_and_show';
29
 
@@ -33,7 +34,7 @@ class Admin {
33
  /** @var string the "sync disabled" sync mode slug */
34
  const SYNC_MODE_SYNC_DISABLED = 'sync_disabled';
35
 
36
- /** @var \Admin\Product_Categories the product category admin handler */
37
  protected $product_categories;
38
 
39
  /** @var array screens ids where to include scripts */
@@ -62,19 +63,13 @@ class Admin {
62
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
63
 
64
  $plugin = facebook_for_woocommerce();
65
-
66
  // only alter the admin UI if the plugin is connected to Facebook and ready to sync products
67
  if ( ! $plugin->get_connection_handler()->is_connected() || ! $plugin->get_integration()->get_product_catalog_id() ) {
68
  return;
69
  }
70
 
71
- require_once __DIR__ . '/Admin/Products.php';
72
- require_once __DIR__ . '/Admin/Product_Categories.php';
73
- require_once __DIR__ . '/Admin/Product_Sets.php';
74
-
75
  $this->product_categories = new Admin\Product_Categories();
76
  $this->product_sets = new Admin\Product_Sets();
77
-
78
  // add a modal in admin product pages
79
  add_action( 'admin_footer', array( $this, 'render_modal_template' ) );
80
  // may trigger the modal to open to warn the merchant about a conflict with the current product terms
@@ -125,13 +120,10 @@ class Admin {
125
  * @return string
126
  */
127
  public function change_custom_taxonomy_tip( $translation, $text ) {
128
-
129
  global $current_screen;
130
-
131
  if ( isset( $current_screen->id ) && 'edit-fb_product_set' === $current_screen->id && 'The name is how it appears on your site.' === $text ) {
132
  $translation = esc_html__( 'The name is how it appears on Facebook Catalog.', 'facebook-for-woocommerce' );
133
  }
134
-
135
  return $translation;
136
  }
137
 
@@ -179,25 +171,24 @@ class Admin {
179
  }
180
 
181
  if ( 'edit-fb_product_set' === $current_screen->id ) {
182
-
183
  // enqueue WooCommerce Admin Styles because of Select2
184
  wp_enqueue_style(
185
  'woocommerce_admin_styles',
186
  WC()->plugin_url() . '/assets/css/admin.css',
187
- array(),
188
  \WC_Facebookcommerce::PLUGIN_VERSION
189
  );
190
  wp_enqueue_style(
191
  'facebook-for-woocommerce-product-sets-admin',
192
  facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-product-sets-admin.css',
193
- array(),
194
  \WC_Facebookcommerce::PLUGIN_VERSION
195
  );
196
 
197
  wp_enqueue_script(
198
  'facebook-for-woocommerce-product-sets',
199
  facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/product-sets-admin.js',
200
- array( 'jquery', 'select2' ),
201
  \WC_Facebookcommerce::PLUGIN_VERSION,
202
  true
203
  );
@@ -214,46 +205,42 @@ class Admin {
214
  }
215
 
216
  if ( 'product' === $current_screen->id || 'edit-product' === $current_screen->id ) {
217
-
218
  wp_enqueue_style(
219
  'facebook-for-woocommerce-products-admin',
220
  facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-products-admin.css',
221
- array(),
222
  \WC_Facebookcommerce::PLUGIN_VERSION
223
  );
224
-
225
  wp_enqueue_script(
226
  'facebook-for-woocommerce-products-admin',
227
  facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/products-admin.js',
228
- array( 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'facebook-for-woocommerce-modal' ),
229
  \WC_Facebookcommerce::PLUGIN_VERSION
230
  );
231
-
232
  wp_localize_script(
233
  'facebook-for-woocommerce-products-admin',
234
  'facebook_for_woocommerce_products_admin',
235
- array(
236
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
237
- 'enhanced_attribute_optional_selector' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY,
238
- 'enhanced_attribute_page_type_edit_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
239
- 'enhanced_attribute_page_type_add_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
240
- 'enhanced_attribute_page_type_edit_product' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
241
- 'is_product_published' => $this->is_current_product_published(),
242
- 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(),
243
- 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ),
244
- 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ),
245
- 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ),
246
- 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(),
247
- 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(),
248
  'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(),
249
  'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(),
250
- 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
251
- 'i18n' => array(
252
  'missing_google_product_category_message' => __( 'Please enter a Google product category and at least one sub-category to sell this product on Instagram.', 'facebook-for-woocommerce' ),
253
- ),
254
- )
255
  );
256
-
257
  }//end if
258
 
259
  if ( facebook_for_woocommerce()->is_plugin_settings() ) {
@@ -264,7 +251,6 @@ class Admin {
264
 
265
  }
266
 
267
-
268
  /**
269
  * Determines whether sync is enabled for the current product.
270
  *
@@ -274,13 +260,10 @@ class Admin {
274
  */
275
  private function is_sync_enabled_for_current_product() {
276
  global $post;
277
-
278
  $product = wc_get_product( $post );
279
-
280
  if ( ! $product instanceof \WC_Product ) {
281
  return false;
282
  }
283
-
284
  return Products::is_sync_enabled_for_product( $product );
285
  }
286
 
@@ -293,13 +276,10 @@ class Admin {
293
  */
294
  private function is_current_product_published() {
295
  global $post;
296
-
297
  $product = wc_get_product( $post );
298
-
299
  if ( ! $product instanceof \WC_Product ) {
300
  return false;
301
  }
302
-
303
  return 'publish' === $product->get_status();
304
  }
305
 
@@ -311,12 +291,9 @@ class Admin {
311
  * @return string
312
  */
313
  private function get_product_not_ready_modal_message() {
314
-
315
  ob_start();
316
-
317
  ?>
318
  <p><?php esc_html_e( 'To sell this product on Instagram, please ensure it meets the following requirements:', 'facebook-for-woocommerce' ); ?></p>
319
-
320
  <ul class="ul-disc">
321
  <li><?php esc_html_e( 'Has a price defined', 'facebook-for-woocommerce' ); ?></li>
322
  <li>
@@ -345,11 +322,9 @@ class Admin {
345
  </li>
346
  </ul>
347
  <?php
348
-
349
  return ob_get_clean();
350
  }
351
 
352
-
353
  /**
354
  * Gets the markup for the buttons used in the product not ready modal.
355
  *
@@ -358,20 +333,16 @@ class Admin {
358
  * @return string
359
  */
360
  private function get_product_not_ready_modal_buttons() {
361
-
362
  ob_start();
363
-
364
  ?>
365
  <button
366
  id="btn-ok"
367
  class="button button-large button-primary"
368
  ><?php esc_html_e( 'Close', 'facebook-for-woocomerce' ); ?></button>
369
  <?php
370
-
371
  return ob_get_clean();
372
  }
373
 
374
-
375
  /**
376
  * Gets the markup for the message used in the product removed from sync confirm modal.
377
  *
@@ -382,9 +353,7 @@ class Admin {
382
  * @return string
383
  */
384
  private function get_product_removed_from_sync_confirm_modal_message() {
385
-
386
  ob_start();
387
-
388
  ?>
389
  <p>
390
  <?php
@@ -397,11 +366,9 @@ class Admin {
397
  ?>
398
  </p>
399
  <?php
400
-
401
  return ob_get_clean();
402
  }
403
 
404
-
405
  /**
406
  * Gets the markup for the buttons used in the product removed from sync confirm modal.
407
  *
@@ -412,9 +379,7 @@ class Admin {
412
  * @return string
413
  */
414
  private function get_product_removed_from_sync_confirm_modal_buttons() {
415
-
416
  ob_start();
417
-
418
  ?>
419
  <button
420
  id="btn-ok"
@@ -429,24 +394,20 @@ class Admin {
429
  class="button button-large button-product-removed-from-sync-cancel"
430
  ><?php esc_html_e( 'Cancel', 'facebook-for-woocomerce' ); ?></button>
431
  <?php
432
-
433
  return ob_get_clean();
434
  }
435
 
436
-
437
  /**
438
  * Gets the product category admin handler instance.
439
  *
440
  * @since 2.1.0
441
  *
442
- * @return \SkyVerge\WooCommerce\Facebook\Admin\Product_Categories
443
  */
444
  public function get_product_categories_handler() {
445
-
446
  return $this->product_categories;
447
  }
448
 
449
-
450
  /**
451
  * Adds Facebook-related columns in the products edit screen.
452
  *
@@ -458,13 +419,10 @@ class Admin {
458
  * @return array
459
  */
460
  public function add_product_list_table_columns( $columns ) {
461
-
462
  $columns['facebook_sync'] = __( 'Facebook sync', 'facebook-for-woocommerce' );
463
-
464
  return $columns;
465
  }
466
 
467
-
468
  /**
469
  * Outputs sync information for products in the edit screen.
470
  *
@@ -482,21 +440,17 @@ class Admin {
482
  }
483
 
484
  $product = wc_get_product( $post );
485
-
486
  if ( $product && Products::product_should_be_synced( $product ) ) {
487
-
488
  if ( Products::is_product_visible( $product ) ) {
489
  esc_html_e( 'Sync and show', 'facebook-for-woocommerce' );
490
  } else {
491
  esc_html_e( 'Sync and hide', 'facebook-for-woocommerce' );
492
  }
493
  } else {
494
-
495
  esc_html_e( 'Do not sync', 'facebook-for-woocommerce' );
496
  }
497
  }
498
 
499
-
500
  /**
501
  * Adds a dropdown input to let shop managers filter products by sync setting.
502
  *
@@ -513,7 +467,6 @@ class Admin {
513
 
514
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
515
  $choice = isset( $_GET['fb_sync_enabled'] ) ? (string) sanitize_text_field( wp_unslash( $_GET['fb_sync_enabled'] ) ) : '';
516
-
517
  ?>
518
  <select name="fb_sync_enabled">
519
  <option value="" <?php selected( $choice, '' ); ?>><?php esc_html_e( 'Filter by Facebook sync setting', 'facebook-for-woocommerce' ); ?></option>
@@ -524,7 +477,6 @@ class Admin {
524
  <?php
525
  }
526
 
527
-
528
  /**
529
  * Filters products by Facebook sync setting.
530
  *
@@ -536,7 +488,6 @@ class Admin {
536
  * @return array
537
  */
538
  public function filter_products_by_sync_enabled( $query_vars ) {
539
-
540
  $valid_values = array(
541
  self::SYNC_MODE_SYNC_AND_SHOW,
542
  self::SYNC_MODE_SYNC_AND_HIDE,
@@ -545,45 +496,36 @@ class Admin {
545
 
546
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
547
  if ( isset( $_REQUEST['fb_sync_enabled'] ) && in_array( $_REQUEST['fb_sync_enabled'], $valid_values, true ) ) {
548
-
549
  // store original meta query
550
- $original_meta_query = ! empty( $query_vars['meta_query'] ) ? $query_vars['meta_query'] : array();
551
-
552
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
553
  $filter_value = $_REQUEST['fb_sync_enabled'];
554
-
555
  // by default use an "AND" clause if multiple conditions exist for a meta query
556
  if ( ! empty( $query_vars['meta_query'] ) ) {
557
  $query_vars['meta_query']['relation'] = 'AND';
558
  } else {
559
- $query_vars['meta_query'] = array();
560
  }
561
 
562
  if ( self::SYNC_MODE_SYNC_AND_SHOW === $filter_value ) {
563
-
564
  // when checking for products with sync enabled we need to check both "yes" and meta not set, this requires adding an "OR" clause
565
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
566
-
567
  // only get visible products (both "yes" and meta not set)
568
  $query_vars = $this->add_query_vars_to_find_visible_products( $query_vars );
569
-
570
  // since we record enabled status and visibility on child variations, we need to query variable products found for their children to exclude them from query results
571
- $exclude_products = array();
572
  $found_ids = get_posts( array_merge( $query_vars, array( 'fields' => 'ids' ) ) );
573
- $found_products = empty( $found_ids ) ? array() : wc_get_products(
574
  array(
575
  'limit' => -1,
576
  'type' => 'variable',
577
  'include' => $found_ids,
578
  )
579
  );
580
-
581
  /** @var \WC_Product[] $found_products */
582
  foreach ( $found_products as $product ) {
583
-
584
  if ( ! Products::is_sync_enabled_for_product( $product )
585
  || ! Products::is_product_visible( $product ) ) {
586
-
587
  $exclude_products[] = $product->get_id();
588
  }
589
  }
@@ -596,30 +538,24 @@ class Admin {
596
  }
597
  }
598
  } elseif ( self::SYNC_MODE_SYNC_AND_HIDE === $filter_value ) {
599
-
600
  // when checking for products with sync enabled we need to check both "yes" and meta not set, this requires adding an "OR" clause
601
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
602
-
603
  // only get hidden products
604
  $query_vars = $this->add_query_vars_to_find_hidden_products( $query_vars );
605
-
606
  // since we record enabled status and visibility on child variations, we need to query variable products found for their children to exclude them from query results
607
- $exclude_products = array();
608
  $found_ids = get_posts( array_merge( $query_vars, array( 'fields' => 'ids' ) ) );
609
- $found_products = empty( $found_ids ) ? array() : wc_get_products(
610
  array(
611
  'limit' => -1,
612
  'type' => 'variable',
613
  'include' => $found_ids,
614
  )
615
  );
616
-
617
  /** @var \WC_Product[] $found_products */
618
  foreach ( $found_products as $product ) {
619
-
620
  if ( ! Products::is_sync_enabled_for_product( $product )
621
  || Products::is_product_visible( $product ) ) {
622
-
623
  $exclude_products[] = $product->get_id();
624
  }
625
  }
@@ -633,7 +569,7 @@ class Admin {
633
  }
634
 
635
  // for the same reason, we also need to include variable products with hidden children
636
- $include_products = array();
637
  $hidden_variations = get_posts(
638
  array(
639
  'limit' => -1,
@@ -647,35 +583,26 @@ class Admin {
647
 
648
  /** @var \WP_Post[] $hidden_variations */
649
  foreach ( $hidden_variations as $variation_post ) {
650
-
651
  $variable_product = wc_get_product( $variation_post->post_parent );
652
-
653
  // we need this check because we only want products with ALL variations hidden
654
  if ( $variable_product instanceof \WC_Product && Products::is_sync_enabled_for_product( $variable_product )
655
  && ! Products::is_product_visible( $variable_product ) ) {
656
-
657
  $include_products[] = $variable_product->get_id();
658
  }
659
  }
660
  } else {
661
-
662
  // self::SYNC_MODE_SYNC_DISABLED
663
-
664
  // products to be included in the QUERY, not in the sync
665
- $include_products = array();
666
- $found_ids = array();
667
-
668
  $integration = facebook_for_woocommerce()->get_integration();
669
- $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : array();
670
- $excluded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : array();
671
-
672
  // get the product IDs from all products in excluded taxonomies
673
  if ( $excluded_categories_ids || $excluded_tags_ids ) {
674
-
675
  $tax_query_vars = $this->maybe_add_tax_query_for_excluded_taxonomies( $query_vars, true );
676
  $include_products = array_merge( $include_products, get_posts( array_merge( $tax_query_vars, array( 'fields' => 'ids' ) ) ) );
677
  }
678
-
679
  $excluded_products = get_posts(
680
  array(
681
  'fields' => 'ids',
@@ -689,9 +616,7 @@ class Admin {
689
  ),
690
  )
691
  );
692
-
693
  $include_products = array_unique( array_merge( $include_products, $excluded_products ) );
694
-
695
  // since we record enabled status and visibility on child variations,
696
  // we need to include variable products with excluded children
697
  $excluded_variations = get_posts(
@@ -706,25 +631,19 @@ class Admin {
706
  ),
707
  )
708
  );
709
-
710
  /** @var \WP_Post[] $excluded_variations */
711
  foreach ( $excluded_variations as $variation_post ) {
712
-
713
  $variable_product = wc_get_product( $variation_post->post_parent );
714
-
715
  // we need this check because we only want products with ALL variations excluded
716
  if ( ! Products::is_sync_enabled_for_product( $variable_product ) ) {
717
-
718
  $include_products[] = $variable_product->get_id();
719
  }
720
  }
721
  }//end if
722
 
723
  if ( ! empty( $include_products ) ) {
724
-
725
  // we are going to query by ID, so we want to include all the IDs from before
726
  $include_products = array_unique( array_merge( $found_ids, $include_products ) );
727
-
728
  if ( ! empty( $query_vars['post__in'] ) ) {
729
  $query_vars['post__in'] = array_merge( $query_vars['post__in'], $include_products );
730
  } else {
@@ -757,7 +676,6 @@ class Admin {
757
  * @return array
758
  */
759
  private function add_query_vars_to_find_products_with_sync_enabled( array $query_vars ) {
760
-
761
  $meta_query = array(
762
  'relation' => 'OR',
763
  array(
@@ -771,11 +689,8 @@ class Admin {
771
  );
772
 
773
  if ( empty( $query_vars['meta_query'] ) ) {
774
-
775
  $query_vars['meta_query'] = $meta_query;
776
-
777
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
778
-
779
  $original_meta_query = $query_vars['meta_query'];
780
  $query_vars['meta_query'] = array(
781
  'relation' => 'AND',
@@ -786,7 +701,6 @@ class Admin {
786
 
787
  // check whether the product belongs to an excluded product category or tag
788
  $query_vars = $this->maybe_add_tax_query_for_excluded_taxonomies( $query_vars );
789
-
790
  return $query_vars;
791
  }
792
 
@@ -801,14 +715,10 @@ class Admin {
801
  * @return array
802
  */
803
  private function maybe_add_tax_query_for_excluded_taxonomies( $query_vars, $in = false ) {
804
-
805
  $integration = facebook_for_woocommerce()->get_integration();
806
-
807
  if ( $integration ) {
808
-
809
- $tax_query = array();
810
  $excluded_categories_ids = $integration->get_excluded_product_category_ids();
811
-
812
  if ( $excluded_categories_ids ) {
813
  $tax_query[] = array(
814
  'taxonomy' => 'product_cat',
@@ -817,9 +727,7 @@ class Admin {
817
  'operator' => $in ? 'IN' : 'NOT IN',
818
  );
819
  }
820
-
821
  $excluded_tags_ids = $integration->get_excluded_product_tag_ids();
822
-
823
  if ( $excluded_tags_ids ) {
824
  $tax_query[] = array(
825
  'taxonomy' => 'product_tag',
@@ -853,7 +761,6 @@ class Admin {
853
  * @return array
854
  */
855
  private function add_query_vars_to_find_visible_products( array $query_vars ) {
856
-
857
  $visibility_meta_query = array(
858
  'relation' => 'OR',
859
  array(
@@ -867,11 +774,8 @@ class Admin {
867
  );
868
 
869
  if ( empty( $query_vars['meta_query'] ) ) {
870
-
871
  $query_vars['meta_query'] = $visibility_meta_query;
872
-
873
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
874
-
875
  $enabled_meta_query = $query_vars['meta_query'];
876
  $query_vars['meta_query'] = array(
877
  'relation' => 'AND',
@@ -893,18 +797,14 @@ class Admin {
893
  * @return array
894
  */
895
  private function add_query_vars_to_find_hidden_products( array $query_vars ) {
896
-
897
  $visibility_meta_query = array(
898
  'key' => Products::VISIBILITY_META_KEY,
899
  'value' => 'no',
900
  );
901
 
902
  if ( empty( $query_vars['meta_query'] ) ) {
903
-
904
  $query_vars['meta_query'] = $visibility_meta_query;
905
-
906
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
907
-
908
  $enabled_meta_query = $query_vars['meta_query'];
909
  $query_vars['meta_query'] = array(
910
  'relation' => 'AND',
@@ -928,10 +828,8 @@ class Admin {
928
  * @return array
929
  */
930
  public function add_products_sync_bulk_actions( $bulk_actions ) {
931
-
932
  $bulk_actions['facebook_include'] = __( 'Include in Facebook sync', 'facebook-for-woocommerce' );
933
  $bulk_actions['facebook_exclude'] = __( 'Exclude from Facebook sync', 'facebook-for-woocommerce' );
934
-
935
  return $bulk_actions;
936
  }
937
 
@@ -959,42 +857,26 @@ class Admin {
959
  }
960
 
961
  if ( $action && in_array( $action, array( 'facebook_include', 'facebook_exclude' ), true ) ) {
962
-
963
- $products = array();
964
-
965
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
966
- $product_ids = isset( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ? array_map( 'absint', $_REQUEST['post'] ) : array();
967
-
968
  if ( ! empty( $product_ids ) ) {
969
-
970
  /** @var \WC_Product[] $enabling_sync_virtual_products virtual products that are being included */
971
- $enabling_sync_virtual_products = array();
972
  /** @var \WC_Product_Variation[] $enabling_sync_virtual_variations virtual variations that are being included */
973
- $enabling_sync_virtual_variations = array();
974
-
975
  foreach ( $product_ids as $product_id ) {
976
-
977
  if ( $product = wc_get_product( $product_id ) ) {
978
-
979
  $products[] = $product;
980
-
981
  if ( 'facebook_include' === $action ) {
982
-
983
  if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) {
984
-
985
  $enabling_sync_virtual_products[ $product->get_id() ] = $product;
986
-
987
  } else {
988
-
989
  if ( $product->is_type( 'variable' ) ) {
990
-
991
  // collect the virtual variations
992
  foreach ( $product->get_children() as $variation_id ) {
993
-
994
  $variation = wc_get_product( $variation_id );
995
-
996
  if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) {
997
-
998
  $enabling_sync_virtual_products[ $product->get_id() ] = $product;
999
  $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation;
1000
  }
@@ -1006,7 +888,6 @@ class Admin {
1006
  }//end foreach
1007
 
1008
  if ( ! empty( $enabling_sync_virtual_products ) || ! empty( $enabling_sync_virtual_variations ) ) {
1009
-
1010
  // display notice if enabling sync for virtual products or variations
1011
  set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id(), true, 15 * MINUTE_IN_SECONDS );
1012
  set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id(), array_keys( $enabling_sync_virtual_products ), 15 * MINUTE_IN_SECONDS );
@@ -1078,22 +959,6 @@ class Admin {
1078
  }
1079
 
1080
 
1081
- /**
1082
- * Prints a notice on products page in case the current cart URL is not the original sync URL.
1083
- *
1084
- * TODO remove this deprecated method by version 3.0.0 or by June 2021 {FN 2020-06-09}
1085
- *
1086
- * @internal
1087
- *
1088
- * @since 1.10.0
1089
- * @deprecated 2.0.0
1090
- */
1091
- public function validate_cart_url() {
1092
-
1093
- wc_deprecated_function( __METHOD__, '2.0.0' );
1094
- }
1095
-
1096
-
1097
  /**
1098
  * Adds a transient so an informational notice is displayed on the next page load.
1099
  *
@@ -1119,7 +984,7 @@ class Admin {
1119
  $transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_show_product_disabled_sync_notice_' . get_current_user_id();
1120
  $message_id = 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-product-disabled-sync';
1121
 
1122
- if ( ( $count = get_transient( $transient_name ) ) && ( SV_WC_Helper::is_current_screen( 'edit-product' ) || SV_WC_Helper::is_current_screen( 'product' ) ) ) {
1123
 
1124
  $message = sprintf(
1125
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - <a> tag */
@@ -1159,7 +1024,7 @@ class Admin {
1159
  $show_notice_transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id();
1160
  $affected_products_transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id();
1161
 
1162
- if ( SV_WC_Helper::is_current_screen( 'edit-product' ) && get_transient( $show_notice_transient_name ) && ( $affected_products = get_transient( $affected_products_transient_name ) ) ) {
1163
 
1164
  $message = sprintf(
1165
  esc_html(
@@ -1206,7 +1071,7 @@ class Admin {
1206
 
1207
  $transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id();
1208
 
1209
- if ( isset( $_GET['facebook_show_affected_products'] ) && SV_WC_Helper::is_current_screen( 'edit-product' ) && $affected_products = get_transient( $transient_name ) ) {
1210
 
1211
  $query_vars['post__in'] = $affected_products;
1212
  }
@@ -1373,29 +1238,24 @@ class Admin {
1373
  'value' => '',
1374
  )
1375
  );
1376
-
1377
  ?>
1378
  </div>
1379
-
1380
  <?php
1381
  $product = wc_get_product( $post );
1382
  $commerce_handler = facebook_for_woocommerce()->get_commerce_handler();
1383
  ?>
1384
-
1385
  <?php if ( $commerce_handler->is_connected() && $commerce_handler->is_available() ) : ?>
1386
  <div class='wc-facebook-commerce-options-group options_group'>
1387
  <?php
1388
  if ( $product instanceof \WC_Product ) {
1389
- \SkyVerge\WooCommerce\Facebook\Admin\Products::render_commerce_fields( $product );
1390
  }
1391
  ?>
1392
  </div>
1393
  <?php endif; ?>
1394
-
1395
  <div class='wc-facebook-commerce-options-group options_group'>
1396
- <?php \SkyVerge\WooCommerce\Facebook\Admin\Products::render_google_product_category_fields_and_enhanced_attributes( $product ); ?>
1397
  </div>
1398
-
1399
  </div>
1400
  <?php
1401
  }
@@ -1479,9 +1339,9 @@ class Admin {
1479
  'desc_tip' => true,
1480
  'description' => __( 'Choose the product image that should be synced to the Facebook catalog for this product. If using a custom image, please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ),
1481
  'options' => array(
1482
- Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use variation image', 'facebook-for-woocommerce' ),
1483
  Products::PRODUCT_IMAGE_SOURCE_PARENT_PRODUCT => __( 'Use parent image', 'facebook-for-woocommerce' ),
1484
- Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ),
1485
  ),
1486
  'value' => $image_source ?: Products::PRODUCT_IMAGE_SOURCE_PRODUCT,
1487
  'class' => 'enable-if-sync-enabled js-fb-product-image-source',
@@ -1532,13 +1392,10 @@ class Admin {
1532
  * @return mixed
1533
  */
1534
  private function get_product_variation_meta( $variation, $key, $parent ) {
1535
-
1536
  $value = $variation->get_meta( $key );
1537
-
1538
  if ( '' === $value && $parent instanceof \WC_Product ) {
1539
  $value = $parent->get_meta( $key );
1540
  }
1541
-
1542
  return $value;
1543
  }
1544
 
@@ -1554,49 +1411,35 @@ class Admin {
1554
  * @param int $index the index of the current variation
1555
  */
1556
  public function save_product_variation_edit_fields( $variation_id, $index ) {
1557
-
1558
  $variation = wc_get_product( $variation_id );
1559
-
1560
  if ( ! $variation instanceof \WC_Product_Variation ) {
1561
  return;
1562
  }
1563
-
1564
  $sync_mode = isset( $_POST['variable_facebook_sync_mode'][ $index ] ) ? $_POST['variable_facebook_sync_mode'][ $index ] : self::SYNC_MODE_SYNC_DISABLED;
1565
  $sync_enabled = self::SYNC_MODE_SYNC_DISABLED !== $sync_mode;
1566
-
1567
  if ( self::SYNC_MODE_SYNC_AND_SHOW === $sync_mode && $variation->is_virtual() ) {
1568
  // force to Sync and hide
1569
  $sync_mode = self::SYNC_MODE_SYNC_AND_HIDE;
1570
  }
1571
-
1572
  // phpcs:disable WordPress.Security.NonceVerification.Missing
1573
  if ( $sync_enabled ) {
1574
-
1575
  Products::enable_sync_for_products( array( $variation ) );
1576
  Products::set_product_visibility( $variation, self::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
1577
-
1578
  $posted_param = 'variable_' . \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION;
1579
  $description = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
1580
-
1581
  $posted_param = 'variable_fb_product_image_source';
1582
  $image_source = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_key( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : '';
1583
-
1584
  $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_IMAGE;
1585
  $image_url = isset( $_POST[ $posted_param ][ $index ] ) ? esc_url_raw( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
1586
-
1587
  $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_PRICE;
1588
  $price = isset( $_POST[ $posted_param ][ $index ] ) ? wc_format_decimal( $_POST[ $posted_param ][ $index ] ) : '';
1589
-
1590
  $variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $description );
1591
  $variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source );
1592
  $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_IMAGE, $image_url );
1593
  $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_PRICE, $price );
1594
  $variation->save_meta_data();
1595
-
1596
  } else {
1597
-
1598
  Products::disable_sync_for_products( array( $variation ) );
1599
-
1600
  }//end if
1601
  // phpcs:enable WordPress.Security.NonceVerification.Missing
1602
  }
@@ -1616,7 +1459,6 @@ class Admin {
1616
  if ( ! $current_screen || ! in_array( $current_screen->id, $this->screen_ids, true ) ) {
1617
  return;
1618
  }
1619
-
1620
  ?>
1621
  <script type="text/template" id="tmpl-facebook-for-woocommerce-modal">
1622
  <div class="wc-backbone-modal facebook-for-woocommerce-modal">
@@ -1652,16 +1494,12 @@ class Admin {
1652
  */
1653
  public function validate_product_excluded_terms() {
1654
  global $current_screen, $post;
1655
-
1656
  if ( $post && $current_screen && $current_screen->id === 'product' ) :
1657
-
1658
  $product = wc_get_product( $post );
1659
-
1660
  if ( $product instanceof \WC_Product
1661
  && Products::is_sync_enabled_for_product( $product )
1662
  && Products::is_sync_excluded_for_product_terms( $product )
1663
  ) :
1664
-
1665
  ?>
1666
  <script type="text/javascript">
1667
  jQuery( document ).ready( function( $ ) {
@@ -1696,59 +1534,7 @@ class Admin {
1696
  } );
1697
  </script>
1698
  <?php
1699
-
1700
  endif;
1701
-
1702
  endif;
1703
  }
1704
-
1705
-
1706
- /** Deprecated methods ********************************************************************************************/
1707
-
1708
-
1709
- /**
1710
- * No-op: Prints a notice on products page to inform users about changes in product sync.
1711
- *
1712
- * @internal
1713
- *
1714
- * @since 1.11.0
1715
- * @deprecated 2.0.0
1716
- */
1717
- public function add_product_sync_delay_notice() {
1718
-
1719
- wc_deprecated_function( __METHOD__, '2.0.0' );
1720
- }
1721
-
1722
-
1723
- /**
1724
- * No-op: Handles dismissed notices.
1725
- *
1726
- * @internal
1727
- *
1728
- * @since 1.11.0
1729
- * @deprecated 2.0.0
1730
- *
1731
- * @param string $message_id the dismissed notice ID
1732
- * @param int $user_id the ID of the user the noticed was dismissed for
1733
- */
1734
- public function handle_dismiss_notice( $message_id, $user_id = null ) {
1735
-
1736
- wc_deprecated_function( __METHOD__, '2.0.0' );
1737
- }
1738
-
1739
-
1740
- /**
1741
- * No-op: Prints a notice on products page to inform users that catalog visibility settings were removed.
1742
- *
1743
- * @internal
1744
- *
1745
- * @since 1.11.0
1746
- * @deprecated 2.0.0
1747
- */
1748
- public function add_catalog_visibility_settings_removed_notice() {
1749
-
1750
- wc_deprecated_function( __METHOD__, '2.0.0' );
1751
- }
1752
-
1753
-
1754
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
+ use WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields;
15
+ use WooCommerce\Facebook\Framework\Helper;
16
+ use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
17
  use Automattic\WooCommerce\Utilities\OrderUtil;
18
 
19
  defined( 'ABSPATH' ) or exit;
25
  */
26
  class Admin {
27
 
 
28
  /** @var string the "sync and show" sync mode slug */
29
  const SYNC_MODE_SYNC_AND_SHOW = 'sync_and_show';
30
 
34
  /** @var string the "sync disabled" sync mode slug */
35
  const SYNC_MODE_SYNC_DISABLED = 'sync_disabled';
36
 
37
+ /** @var Product_Categories the product category admin handler */
38
  protected $product_categories;
39
 
40
  /** @var array screens ids where to include scripts */
63
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
64
 
65
  $plugin = facebook_for_woocommerce();
 
66
  // only alter the admin UI if the plugin is connected to Facebook and ready to sync products
67
  if ( ! $plugin->get_connection_handler()->is_connected() || ! $plugin->get_integration()->get_product_catalog_id() ) {
68
  return;
69
  }
70
 
 
 
 
 
71
  $this->product_categories = new Admin\Product_Categories();
72
  $this->product_sets = new Admin\Product_Sets();
 
73
  // add a modal in admin product pages
74
  add_action( 'admin_footer', array( $this, 'render_modal_template' ) );
75
  // may trigger the modal to open to warn the merchant about a conflict with the current product terms
120
  * @return string
121
  */
122
  public function change_custom_taxonomy_tip( $translation, $text ) {
 
123
  global $current_screen;
 
124
  if ( isset( $current_screen->id ) && 'edit-fb_product_set' === $current_screen->id && 'The name is how it appears on your site.' === $text ) {
125
  $translation = esc_html__( 'The name is how it appears on Facebook Catalog.', 'facebook-for-woocommerce' );
126
  }
 
127
  return $translation;
128
  }
129
 
171
  }
172
 
173
  if ( 'edit-fb_product_set' === $current_screen->id ) {
 
174
  // enqueue WooCommerce Admin Styles because of Select2
175
  wp_enqueue_style(
176
  'woocommerce_admin_styles',
177
  WC()->plugin_url() . '/assets/css/admin.css',
178
+ [],
179
  \WC_Facebookcommerce::PLUGIN_VERSION
180
  );
181
  wp_enqueue_style(
182
  'facebook-for-woocommerce-product-sets-admin',
183
  facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-product-sets-admin.css',
184
+ [],
185
  \WC_Facebookcommerce::PLUGIN_VERSION
186
  );
187
 
188
  wp_enqueue_script(
189
  'facebook-for-woocommerce-product-sets',
190
  facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/product-sets-admin.js',
191
+ [ 'jquery', 'select2' ],
192
  \WC_Facebookcommerce::PLUGIN_VERSION,
193
  true
194
  );
205
  }
206
 
207
  if ( 'product' === $current_screen->id || 'edit-product' === $current_screen->id ) {
 
208
  wp_enqueue_style(
209
  'facebook-for-woocommerce-products-admin',
210
  facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-products-admin.css',
211
+ [],
212
  \WC_Facebookcommerce::PLUGIN_VERSION
213
  );
 
214
  wp_enqueue_script(
215
  'facebook-for-woocommerce-products-admin',
216
  facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/products-admin.js',
217
+ [ 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'facebook-for-woocommerce-modal' ],
218
  \WC_Facebookcommerce::PLUGIN_VERSION
219
  );
 
220
  wp_localize_script(
221
  'facebook-for-woocommerce-products-admin',
222
  'facebook_for_woocommerce_products_admin',
223
+ [
224
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
225
+ 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY,
226
+ 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
227
+ 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
228
+ 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
229
+ 'is_product_published' => $this->is_current_product_published(),
230
+ 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(),
231
+ 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ),
232
+ 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ),
233
+ 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ),
234
+ 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(),
235
+ 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(),
236
  'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(),
237
  'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(),
238
+ 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
239
+ 'i18n' => [
240
  'missing_google_product_category_message' => __( 'Please enter a Google product category and at least one sub-category to sell this product on Instagram.', 'facebook-for-woocommerce' ),
241
+ ],
242
+ ]
243
  );
 
244
  }//end if
245
 
246
  if ( facebook_for_woocommerce()->is_plugin_settings() ) {
251
 
252
  }
253
 
 
254
  /**
255
  * Determines whether sync is enabled for the current product.
256
  *
260
  */
261
  private function is_sync_enabled_for_current_product() {
262
  global $post;
 
263
  $product = wc_get_product( $post );
 
264
  if ( ! $product instanceof \WC_Product ) {
265
  return false;
266
  }
 
267
  return Products::is_sync_enabled_for_product( $product );
268
  }
269
 
276
  */
277
  private function is_current_product_published() {
278
  global $post;
 
279
  $product = wc_get_product( $post );
 
280
  if ( ! $product instanceof \WC_Product ) {
281
  return false;
282
  }
 
283
  return 'publish' === $product->get_status();
284
  }
285
 
291
  * @return string
292
  */
293
  private function get_product_not_ready_modal_message() {
 
294
  ob_start();
 
295
  ?>
296
  <p><?php esc_html_e( 'To sell this product on Instagram, please ensure it meets the following requirements:', 'facebook-for-woocommerce' ); ?></p>
 
297
  <ul class="ul-disc">
298
  <li><?php esc_html_e( 'Has a price defined', 'facebook-for-woocommerce' ); ?></li>
299
  <li>
322
  </li>
323
  </ul>
324
  <?php
 
325
  return ob_get_clean();
326
  }
327
 
 
328
  /**
329
  * Gets the markup for the buttons used in the product not ready modal.
330
  *
333
  * @return string
334
  */
335
  private function get_product_not_ready_modal_buttons() {
 
336
  ob_start();
 
337
  ?>
338
  <button
339
  id="btn-ok"
340
  class="button button-large button-primary"
341
  ><?php esc_html_e( 'Close', 'facebook-for-woocomerce' ); ?></button>
342
  <?php
 
343
  return ob_get_clean();
344
  }
345
 
 
346
  /**
347
  * Gets the markup for the message used in the product removed from sync confirm modal.
348
  *
353
  * @return string
354
  */
355
  private function get_product_removed_from_sync_confirm_modal_message() {
 
356
  ob_start();
 
357
  ?>
358
  <p>
359
  <?php
366
  ?>
367
  </p>
368
  <?php
 
369
  return ob_get_clean();
370
  }
371
 
 
372
  /**
373
  * Gets the markup for the buttons used in the product removed from sync confirm modal.
374
  *
379
  * @return string
380
  */
381
  private function get_product_removed_from_sync_confirm_modal_buttons() {
 
382
  ob_start();
 
383
  ?>
384
  <button
385
  id="btn-ok"
394
  class="button button-large button-product-removed-from-sync-cancel"
395
  ><?php esc_html_e( 'Cancel', 'facebook-for-woocomerce' ); ?></button>
396
  <?php
 
397
  return ob_get_clean();
398
  }
399
 
 
400
  /**
401
  * Gets the product category admin handler instance.
402
  *
403
  * @since 2.1.0
404
  *
405
+ * @return Product_Categories
406
  */
407
  public function get_product_categories_handler() {
 
408
  return $this->product_categories;
409
  }
410
 
 
411
  /**
412
  * Adds Facebook-related columns in the products edit screen.
413
  *
419
  * @return array
420
  */
421
  public function add_product_list_table_columns( $columns ) {
 
422
  $columns['facebook_sync'] = __( 'Facebook sync', 'facebook-for-woocommerce' );
 
423
  return $columns;
424
  }
425
 
 
426
  /**
427
  * Outputs sync information for products in the edit screen.
428
  *
440
  }
441
 
442
  $product = wc_get_product( $post );
 
443
  if ( $product && Products::product_should_be_synced( $product ) ) {
 
444
  if ( Products::is_product_visible( $product ) ) {
445
  esc_html_e( 'Sync and show', 'facebook-for-woocommerce' );
446
  } else {
447
  esc_html_e( 'Sync and hide', 'facebook-for-woocommerce' );
448
  }
449
  } else {
 
450
  esc_html_e( 'Do not sync', 'facebook-for-woocommerce' );
451
  }
452
  }
453
 
 
454
  /**
455
  * Adds a dropdown input to let shop managers filter products by sync setting.
456
  *
467
 
468
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
469
  $choice = isset( $_GET['fb_sync_enabled'] ) ? (string) sanitize_text_field( wp_unslash( $_GET['fb_sync_enabled'] ) ) : '';
 
470
  ?>
471
  <select name="fb_sync_enabled">
472
  <option value="" <?php selected( $choice, '' ); ?>><?php esc_html_e( 'Filter by Facebook sync setting', 'facebook-for-woocommerce' ); ?></option>
477
  <?php
478
  }
479
 
 
480
  /**
481
  * Filters products by Facebook sync setting.
482
  *
488
  * @return array
489
  */
490
  public function filter_products_by_sync_enabled( $query_vars ) {
 
491
  $valid_values = array(
492
  self::SYNC_MODE_SYNC_AND_SHOW,
493
  self::SYNC_MODE_SYNC_AND_HIDE,
496
 
497
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
498
  if ( isset( $_REQUEST['fb_sync_enabled'] ) && in_array( $_REQUEST['fb_sync_enabled'], $valid_values, true ) ) {
 
499
  // store original meta query
500
+ $original_meta_query = ! empty( $query_vars['meta_query'] ) ? $query_vars['meta_query'] : [];
 
501
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
502
  $filter_value = $_REQUEST['fb_sync_enabled'];
 
503
  // by default use an "AND" clause if multiple conditions exist for a meta query
504
  if ( ! empty( $query_vars['meta_query'] ) ) {
505
  $query_vars['meta_query']['relation'] = 'AND';
506
  } else {
507
+ $query_vars['meta_query'] = [];
508
  }
509
 
510
  if ( self::SYNC_MODE_SYNC_AND_SHOW === $filter_value ) {
 
511
  // when checking for products with sync enabled we need to check both "yes" and meta not set, this requires adding an "OR" clause
512
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
 
513
  // only get visible products (both "yes" and meta not set)
514
  $query_vars = $this->add_query_vars_to_find_visible_products( $query_vars );
 
515
  // since we record enabled status and visibility on child variations, we need to query variable products found for their children to exclude them from query results
516
+ $exclude_products = [];
517
  $found_ids = get_posts( array_merge( $query_vars, array( 'fields' => 'ids' ) ) );
518
+ $found_products = empty( $found_ids ) ? [] : wc_get_products(
519
  array(
520
  'limit' => -1,
521
  'type' => 'variable',
522
  'include' => $found_ids,
523
  )
524
  );
 
525
  /** @var \WC_Product[] $found_products */
526
  foreach ( $found_products as $product ) {
 
527
  if ( ! Products::is_sync_enabled_for_product( $product )
528
  || ! Products::is_product_visible( $product ) ) {
 
529
  $exclude_products[] = $product->get_id();
530
  }
531
  }
538
  }
539
  }
540
  } elseif ( self::SYNC_MODE_SYNC_AND_HIDE === $filter_value ) {
 
541
  // when checking for products with sync enabled we need to check both "yes" and meta not set, this requires adding an "OR" clause
542
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
 
543
  // only get hidden products
544
  $query_vars = $this->add_query_vars_to_find_hidden_products( $query_vars );
 
545
  // since we record enabled status and visibility on child variations, we need to query variable products found for their children to exclude them from query results
546
+ $exclude_products = [];
547
  $found_ids = get_posts( array_merge( $query_vars, array( 'fields' => 'ids' ) ) );
548
+ $found_products = empty( $found_ids ) ? [] : wc_get_products(
549
  array(
550
  'limit' => -1,
551
  'type' => 'variable',
552
  'include' => $found_ids,
553
  )
554
  );
 
555
  /** @var \WC_Product[] $found_products */
556
  foreach ( $found_products as $product ) {
 
557
  if ( ! Products::is_sync_enabled_for_product( $product )
558
  || Products::is_product_visible( $product ) ) {
 
559
  $exclude_products[] = $product->get_id();
560
  }
561
  }
569
  }
570
 
571
  // for the same reason, we also need to include variable products with hidden children
572
+ $include_products = [];
573
  $hidden_variations = get_posts(
574
  array(
575
  'limit' => -1,
583
 
584
  /** @var \WP_Post[] $hidden_variations */
585
  foreach ( $hidden_variations as $variation_post ) {
 
586
  $variable_product = wc_get_product( $variation_post->post_parent );
 
587
  // we need this check because we only want products with ALL variations hidden
588
  if ( $variable_product instanceof \WC_Product && Products::is_sync_enabled_for_product( $variable_product )
589
  && ! Products::is_product_visible( $variable_product ) ) {
 
590
  $include_products[] = $variable_product->get_id();
591
  }
592
  }
593
  } else {
 
594
  // self::SYNC_MODE_SYNC_DISABLED
 
595
  // products to be included in the QUERY, not in the sync
596
+ $include_products = [];
597
+ $found_ids = [];
 
598
  $integration = facebook_for_woocommerce()->get_integration();
599
+ $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : [];
600
+ $excluded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : [];
 
601
  // get the product IDs from all products in excluded taxonomies
602
  if ( $excluded_categories_ids || $excluded_tags_ids ) {
 
603
  $tax_query_vars = $this->maybe_add_tax_query_for_excluded_taxonomies( $query_vars, true );
604
  $include_products = array_merge( $include_products, get_posts( array_merge( $tax_query_vars, array( 'fields' => 'ids' ) ) ) );
605
  }
 
606
  $excluded_products = get_posts(
607
  array(
608
  'fields' => 'ids',
616
  ),
617
  )
618
  );
 
619
  $include_products = array_unique( array_merge( $include_products, $excluded_products ) );
 
620
  // since we record enabled status and visibility on child variations,
621
  // we need to include variable products with excluded children
622
  $excluded_variations = get_posts(
631
  ),
632
  )
633
  );
 
634
  /** @var \WP_Post[] $excluded_variations */
635
  foreach ( $excluded_variations as $variation_post ) {
 
636
  $variable_product = wc_get_product( $variation_post->post_parent );
 
637
  // we need this check because we only want products with ALL variations excluded
638
  if ( ! Products::is_sync_enabled_for_product( $variable_product ) ) {
 
639
  $include_products[] = $variable_product->get_id();
640
  }
641
  }
642
  }//end if
643
 
644
  if ( ! empty( $include_products ) ) {
 
645
  // we are going to query by ID, so we want to include all the IDs from before
646
  $include_products = array_unique( array_merge( $found_ids, $include_products ) );
 
647
  if ( ! empty( $query_vars['post__in'] ) ) {
648
  $query_vars['post__in'] = array_merge( $query_vars['post__in'], $include_products );
649
  } else {
676
  * @return array
677
  */
678
  private function add_query_vars_to_find_products_with_sync_enabled( array $query_vars ) {
 
679
  $meta_query = array(
680
  'relation' => 'OR',
681
  array(
689
  );
690
 
691
  if ( empty( $query_vars['meta_query'] ) ) {
 
692
  $query_vars['meta_query'] = $meta_query;
 
693
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
 
694
  $original_meta_query = $query_vars['meta_query'];
695
  $query_vars['meta_query'] = array(
696
  'relation' => 'AND',
701
 
702
  // check whether the product belongs to an excluded product category or tag
703
  $query_vars = $this->maybe_add_tax_query_for_excluded_taxonomies( $query_vars );
 
704
  return $query_vars;
705
  }
706
 
715
  * @return array
716
  */
717
  private function maybe_add_tax_query_for_excluded_taxonomies( $query_vars, $in = false ) {
 
718
  $integration = facebook_for_woocommerce()->get_integration();
 
719
  if ( $integration ) {
720
+ $tax_query = [];
 
721
  $excluded_categories_ids = $integration->get_excluded_product_category_ids();
 
722
  if ( $excluded_categories_ids ) {
723
  $tax_query[] = array(
724
  'taxonomy' => 'product_cat',
727
  'operator' => $in ? 'IN' : 'NOT IN',
728
  );
729
  }
 
730
  $excluded_tags_ids = $integration->get_excluded_product_tag_ids();
 
731
  if ( $excluded_tags_ids ) {
732
  $tax_query[] = array(
733
  'taxonomy' => 'product_tag',
761
  * @return array
762
  */
763
  private function add_query_vars_to_find_visible_products( array $query_vars ) {
 
764
  $visibility_meta_query = array(
765
  'relation' => 'OR',
766
  array(
774
  );
775
 
776
  if ( empty( $query_vars['meta_query'] ) ) {
 
777
  $query_vars['meta_query'] = $visibility_meta_query;
 
778
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
 
779
  $enabled_meta_query = $query_vars['meta_query'];
780
  $query_vars['meta_query'] = array(
781
  'relation' => 'AND',
797
  * @return array
798
  */
799
  private function add_query_vars_to_find_hidden_products( array $query_vars ) {
 
800
  $visibility_meta_query = array(
801
  'key' => Products::VISIBILITY_META_KEY,
802
  'value' => 'no',
803
  );
804
 
805
  if ( empty( $query_vars['meta_query'] ) ) {
 
806
  $query_vars['meta_query'] = $visibility_meta_query;
 
807
  } elseif ( is_array( $query_vars['meta_query'] ) ) {
 
808
  $enabled_meta_query = $query_vars['meta_query'];
809
  $query_vars['meta_query'] = array(
810
  'relation' => 'AND',
828
  * @return array
829
  */
830
  public function add_products_sync_bulk_actions( $bulk_actions ) {
 
831
  $bulk_actions['facebook_include'] = __( 'Include in Facebook sync', 'facebook-for-woocommerce' );
832
  $bulk_actions['facebook_exclude'] = __( 'Exclude from Facebook sync', 'facebook-for-woocommerce' );
 
833
  return $bulk_actions;
834
  }
835
 
857
  }
858
 
859
  if ( $action && in_array( $action, array( 'facebook_include', 'facebook_exclude' ), true ) ) {
860
+ $products = [];
 
 
861
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
862
+ $product_ids = isset( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ? array_map( 'absint', $_REQUEST['post'] ) : [];
 
863
  if ( ! empty( $product_ids ) ) {
 
864
  /** @var \WC_Product[] $enabling_sync_virtual_products virtual products that are being included */
865
+ $enabling_sync_virtual_products = [];
866
  /** @var \WC_Product_Variation[] $enabling_sync_virtual_variations virtual variations that are being included */
867
+ $enabling_sync_virtual_variations = [];
 
868
  foreach ( $product_ids as $product_id ) {
 
869
  if ( $product = wc_get_product( $product_id ) ) {
 
870
  $products[] = $product;
 
871
  if ( 'facebook_include' === $action ) {
 
872
  if ( $product->is_virtual() && ! Products::is_sync_enabled_for_product( $product ) ) {
 
873
  $enabling_sync_virtual_products[ $product->get_id() ] = $product;
 
874
  } else {
 
875
  if ( $product->is_type( 'variable' ) ) {
 
876
  // collect the virtual variations
877
  foreach ( $product->get_children() as $variation_id ) {
 
878
  $variation = wc_get_product( $variation_id );
 
879
  if ( $variation && $variation->is_virtual() && ! Products::is_sync_enabled_for_product( $variation ) ) {
 
880
  $enabling_sync_virtual_products[ $product->get_id() ] = $product;
881
  $enabling_sync_virtual_variations[ $variation->get_id() ] = $variation;
882
  }
888
  }//end foreach
889
 
890
  if ( ! empty( $enabling_sync_virtual_products ) || ! empty( $enabling_sync_virtual_variations ) ) {
 
891
  // display notice if enabling sync for virtual products or variations
892
  set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id(), true, 15 * MINUTE_IN_SECONDS );
893
  set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id(), array_keys( $enabling_sync_virtual_products ), 15 * MINUTE_IN_SECONDS );
959
  }
960
 
961
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
962
  /**
963
  * Adds a transient so an informational notice is displayed on the next page load.
964
  *
984
  $transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_show_product_disabled_sync_notice_' . get_current_user_id();
985
  $message_id = 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-product-disabled-sync';
986
 
987
+ if ( ( $count = get_transient( $transient_name ) ) && ( Helper::is_current_screen( 'edit-product' ) || Helper::is_current_screen( 'product' ) ) ) {
988
 
989
  $message = sprintf(
990
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - <a> tag */
1024
  $show_notice_transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_show_notice_' . get_current_user_id();
1025
  $affected_products_transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id();
1026
 
1027
+ if ( Helper::is_current_screen( 'edit-product' ) && get_transient( $show_notice_transient_name ) && ( $affected_products = get_transient( $affected_products_transient_name ) ) ) {
1028
 
1029
  $message = sprintf(
1030
  esc_html(
1071
 
1072
  $transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_enabling_virtual_products_sync_affected_products_' . get_current_user_id();
1073
 
1074
+ if ( isset( $_GET['facebook_show_affected_products'] ) && Helper::is_current_screen( 'edit-product' ) && $affected_products = get_transient( $transient_name ) ) {
1075
 
1076
  $query_vars['post__in'] = $affected_products;
1077
  }
1238
  'value' => '',
1239
  )
1240
  );
 
1241
  ?>
1242
  </div>
 
1243
  <?php
1244
  $product = wc_get_product( $post );
1245
  $commerce_handler = facebook_for_woocommerce()->get_commerce_handler();
1246
  ?>
 
1247
  <?php if ( $commerce_handler->is_connected() && $commerce_handler->is_available() ) : ?>
1248
  <div class='wc-facebook-commerce-options-group options_group'>
1249
  <?php
1250
  if ( $product instanceof \WC_Product ) {
1251
+ \WooCommerce\Facebook\Admin\Products::render_commerce_fields( $product );
1252
  }
1253
  ?>
1254
  </div>
1255
  <?php endif; ?>
 
1256
  <div class='wc-facebook-commerce-options-group options_group'>
1257
+ <?php \WooCommerce\Facebook\Admin\Products::render_google_product_category_fields_and_enhanced_attributes( $product ); ?>
1258
  </div>
 
1259
  </div>
1260
  <?php
1261
  }
1339
  'desc_tip' => true,
1340
  'description' => __( 'Choose the product image that should be synced to the Facebook catalog for this product. If using a custom image, please enter an absolute URL (e.g. https://domain.com/image.jpg).', 'facebook-for-woocommerce' ),
1341
  'options' => array(
1342
+ Products::PRODUCT_IMAGE_SOURCE_PRODUCT => __( 'Use variation image', 'facebook-for-woocommerce' ),
1343
  Products::PRODUCT_IMAGE_SOURCE_PARENT_PRODUCT => __( 'Use parent image', 'facebook-for-woocommerce' ),
1344
+ Products::PRODUCT_IMAGE_SOURCE_CUSTOM => __( 'Use custom image', 'facebook-for-woocommerce' ),
1345
  ),
1346
  'value' => $image_source ?: Products::PRODUCT_IMAGE_SOURCE_PRODUCT,
1347
  'class' => 'enable-if-sync-enabled js-fb-product-image-source',
1392
  * @return mixed
1393
  */
1394
  private function get_product_variation_meta( $variation, $key, $parent ) {
 
1395
  $value = $variation->get_meta( $key );
 
1396
  if ( '' === $value && $parent instanceof \WC_Product ) {
1397
  $value = $parent->get_meta( $key );
1398
  }
 
1399
  return $value;
1400
  }
1401
 
1411
  * @param int $index the index of the current variation
1412
  */
1413
  public function save_product_variation_edit_fields( $variation_id, $index ) {
 
1414
  $variation = wc_get_product( $variation_id );
 
1415
  if ( ! $variation instanceof \WC_Product_Variation ) {
1416
  return;
1417
  }
 
1418
  $sync_mode = isset( $_POST['variable_facebook_sync_mode'][ $index ] ) ? $_POST['variable_facebook_sync_mode'][ $index ] : self::SYNC_MODE_SYNC_DISABLED;
1419
  $sync_enabled = self::SYNC_MODE_SYNC_DISABLED !== $sync_mode;
 
1420
  if ( self::SYNC_MODE_SYNC_AND_SHOW === $sync_mode && $variation->is_virtual() ) {
1421
  // force to Sync and hide
1422
  $sync_mode = self::SYNC_MODE_SYNC_AND_HIDE;
1423
  }
 
1424
  // phpcs:disable WordPress.Security.NonceVerification.Missing
1425
  if ( $sync_enabled ) {
 
1426
  Products::enable_sync_for_products( array( $variation ) );
1427
  Products::set_product_visibility( $variation, self::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
 
1428
  $posted_param = 'variable_' . \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION;
1429
  $description = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
 
1430
  $posted_param = 'variable_fb_product_image_source';
1431
  $image_source = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_key( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : '';
 
1432
  $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_IMAGE;
1433
  $image_url = isset( $_POST[ $posted_param ][ $index ] ) ? esc_url_raw( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
 
1434
  $posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_PRICE;
1435
  $price = isset( $_POST[ $posted_param ][ $index ] ) ? wc_format_decimal( $_POST[ $posted_param ][ $index ] ) : '';
 
1436
  $variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $description );
1437
  $variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source );
1438
  $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_IMAGE, $image_url );
1439
  $variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_PRICE, $price );
1440
  $variation->save_meta_data();
 
1441
  } else {
 
1442
  Products::disable_sync_for_products( array( $variation ) );
 
1443
  }//end if
1444
  // phpcs:enable WordPress.Security.NonceVerification.Missing
1445
  }
1459
  if ( ! $current_screen || ! in_array( $current_screen->id, $this->screen_ids, true ) ) {
1460
  return;
1461
  }
 
1462
  ?>
1463
  <script type="text/template" id="tmpl-facebook-for-woocommerce-modal">
1464
  <div class="wc-backbone-modal facebook-for-woocommerce-modal">
1494
  */
1495
  public function validate_product_excluded_terms() {
1496
  global $current_screen, $post;
 
1497
  if ( $post && $current_screen && $current_screen->id === 'product' ) :
 
1498
  $product = wc_get_product( $post );
 
1499
  if ( $product instanceof \WC_Product
1500
  && Products::is_sync_enabled_for_product( $product )
1501
  && Products::is_sync_excluded_for_product_terms( $product )
1502
  ) :
 
1503
  ?>
1504
  <script type="text/javascript">
1505
  jQuery( document ).ready( function( $ ) {
1534
  } );
1535
  </script>
1536
  <?php
 
1537
  endif;
 
1538
  endif;
1539
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1540
  }
includes/Admin/Abstract_Settings_Screen.php CHANGED
@@ -9,9 +9,10 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
15
 
16
  defined( 'ABSPATH' ) or exit;
17
 
@@ -84,10 +85,9 @@ abstract class Abstract_Settings_Screen {
84
  *
85
  * @since 2.0.0
86
  *
87
- * @throws Framework\SV_WC_Plugin_Exception
88
  */
89
  public function save() {
90
-
91
  woocommerce_update_options( $this->get_settings() );
92
  }
93
 
@@ -100,14 +100,11 @@ abstract class Abstract_Settings_Screen {
100
  * @return bool
101
  */
102
  protected function is_current_screen_page() {
103
-
104
- if ( Settings::PAGE_ID !== Framework\SV_WC_Helper::get_requested_value( 'page' ) ) {
105
  return false;
106
  }
107
-
108
  // assume we are on the Connection tab by default because the link under Marketing doesn't include the tab query arg
109
- $tab = Framework\SV_WC_Helper::get_requested_value( 'tab', 'connection' );
110
-
111
  return ! empty( $tab ) && $tab === $this->get_id();
112
  }
113
 
@@ -133,7 +130,6 @@ abstract class Abstract_Settings_Screen {
133
  * @return string
134
  */
135
  public function get_disconnected_message() {
136
-
137
  return '';
138
  }
139
 
@@ -146,7 +142,6 @@ abstract class Abstract_Settings_Screen {
146
  * @return string
147
  */
148
  public function get_id() {
149
-
150
  return $this->id;
151
  }
152
 
@@ -159,7 +154,6 @@ abstract class Abstract_Settings_Screen {
159
  * @return string
160
  */
161
  public function get_label() {
162
-
163
  /**
164
  * Filters the screen label.
165
  *
@@ -179,7 +173,6 @@ abstract class Abstract_Settings_Screen {
179
  * @return string
180
  */
181
  public function get_title() {
182
-
183
  /**
184
  * Filters the screen title.
185
  *
@@ -199,7 +192,6 @@ abstract class Abstract_Settings_Screen {
199
  * @return string
200
  */
201
  public function get_description() {
202
-
203
  /**
204
  * Filters the screen description.
205
  *
@@ -209,6 +201,4 @@ abstract class Abstract_Settings_Screen {
209
  */
210
  return (string) apply_filters( 'wc_facebook_admin_settings_' . $this->get_id() . '_screen_description', $this->description, $this );
211
  }
212
-
213
-
214
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
+ use WooCommerce\Facebook\Framework\Helper;
15
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
16
 
17
  defined( 'ABSPATH' ) or exit;
18
 
85
  *
86
  * @since 2.0.0
87
  *
88
+ * @throws PluginException
89
  */
90
  public function save() {
 
91
  woocommerce_update_options( $this->get_settings() );
92
  }
93
 
100
  * @return bool
101
  */
102
  protected function is_current_screen_page() {
103
+ if ( Settings::PAGE_ID !== Helper::get_requested_value( 'page' ) ) {
 
104
  return false;
105
  }
 
106
  // assume we are on the Connection tab by default because the link under Marketing doesn't include the tab query arg
107
+ $tab = Helper::get_requested_value( 'tab', 'connection' );
 
108
  return ! empty( $tab ) && $tab === $this->get_id();
109
  }
110
 
130
  * @return string
131
  */
132
  public function get_disconnected_message() {
 
133
  return '';
134
  }
135
 
142
  * @return string
143
  */
144
  public function get_id() {
 
145
  return $this->id;
146
  }
147
 
154
  * @return string
155
  */
156
  public function get_label() {
 
157
  /**
158
  * Filters the screen label.
159
  *
173
  * @return string
174
  */
175
  public function get_title() {
 
176
  /**
177
  * Filters the screen title.
178
  *
192
  * @return string
193
  */
194
  public function get_description() {
 
195
  /**
196
  * Filters the screen description.
197
  *
201
  */
202
  return (string) apply_filters( 'wc_facebook_admin_settings_' . $this->get_id() . '_screen_description', $this->description, $this );
203
  }
 
 
204
  }
includes/Admin/Enhanced_Catalog_Attribute_Fields.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Products as Products_Handler;
17
 
18
  /**
19
  * Enhanced Catalog attribute fields.
@@ -132,7 +132,7 @@ class Enhanced_Catalog_Attribute_Fields {
132
  if ( ! is_null( $this->product ) ) {
133
  $value = Products_Handler::get_enhanced_catalog_attribute( $attribute_key, $this->product );
134
  } elseif ( ! is_null( $this->term ) ) {
135
- $meta_key = \SkyVerge\WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . $attribute_key;
136
  $value = get_term_meta( $this->term->term_id, $meta_key, true );
137
  }
138
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Products as Products_Handler;
17
 
18
  /**
19
  * Enhanced Catalog attribute fields.
132
  if ( ! is_null( $this->product ) ) {
133
  $value = Products_Handler::get_enhanced_catalog_attribute( $attribute_key, $this->product );
134
  } elseif ( ! is_null( $this->term ) ) {
135
+ $meta_key = \WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . $attribute_key;
136
  $value = get_term_meta( $this->term->term_id, $meta_key, true );
137
  }
138
 
includes/Admin/Google_Product_Category_Field.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
@@ -34,7 +34,6 @@ class Google_Product_Category_Field {
34
  json_encode( $facebook_category_handler->get_categories() ),
35
  esc_js( $input_id )
36
  );
37
-
38
  wc_enqueue_js( $facebook_category_fields );
39
  }
40
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
34
  json_encode( $facebook_category_handler->get_categories() ),
35
  esc_js( $input_id )
36
  );
 
37
  wc_enqueue_js( $facebook_category_fields );
38
  }
39
  }
includes/Admin/Notes/SettingsMoved.php DELETED
@@ -1,88 +0,0 @@
1
- <?php
2
- /**
3
- * Facebook Menu Settings moved note.
4
- *
5
- * Adds a note to merchant's inbox about Facebook Menu being moved to Marketing menu.
6
- *
7
- * @package FacebookCommerce
8
- */
9
-
10
- namespace SkyVerge\WooCommerce\Facebook\Admin\Notes;
11
-
12
- defined( 'ABSPATH' ) || exit;
13
-
14
- use \Automattic\WooCommerce\Admin\Notes\Note;
15
- use \Automattic\WooCommerce\Admin\Notes\NoteTraits;
16
-
17
- /**
18
- * SettingsMoved class.
19
- */
20
- class SettingsMoved {
21
- /**
22
- * Note traits.
23
- */
24
- use NoteTraits;
25
-
26
- /**
27
- * Name of the note for use in the database.
28
- */
29
- const NOTE_NAME = 'facebook-for-woocommerce-settings-moved-to-marketing';
30
-
31
- /**
32
- * Checks if this note should be displayed.
33
- *
34
- * @return bool
35
- */
36
- public static function should_display() {
37
- /**
38
- * The Facebook menu was moved under Marketing menu in v2.2.0. Display this note
39
- * only to users updating from a version prior to v2.2.0.
40
- */
41
- $should_display = false;
42
- $last_event = facebook_for_woocommerce()->get_last_event_from_history();
43
-
44
- if ( isset( $last_event['name'] ) && 'upgrade' === $last_event['name'] ) {
45
- $last_version = $last_event['data']['from_version'];
46
- if ( version_compare( $last_version, '2.2.0', '<' ) ) {
47
- $should_display = true;
48
- }
49
- }
50
-
51
- return $should_display;
52
- }
53
-
54
- /**
55
- * Add or delete note depending on the conditions to display the note.
56
- *
57
- * @throws NotesUnavailableException Throws exception when notes are unavailable.
58
- */
59
- public static function possibly_add_or_delete_note() {
60
- // Verify the conditions to display the note.
61
- if ( self::should_display() ) {
62
- self::possibly_add_note();
63
- } elseif ( self::note_exists() ) {
64
- self::possibly_delete_note();
65
- }
66
- }
67
-
68
- /**
69
- * Get the note.
70
- *
71
- * @return Note
72
- */
73
- public static function get_note() {
74
-
75
- $settings_url = facebook_for_woocommerce()->get_settings_url();
76
- $content = esc_html__( 'Sync your products and reach customers across Facebook, Instagram, Messenger and WhatsApp through your Facebook plugin, which can be found at Marketing > Facebook.', 'facebook-for-woocommerce' );
77
-
78
- $note = new Note();
79
- $note->set_title( esc_html__( 'Facebook is now found under Marketing', 'facebook-for-woocommerce' ) );
80
- $note->set_content( $content );
81
- $note->set_content_data( (object) array() );
82
- $note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
83
- $note->set_name( self::NOTE_NAME );
84
- $note->set_source( 'facebook-for-woocommerce' );
85
- $note->add_action( 'settings', esc_html__( 'Go to Facebook', 'facebook-for-woocommerce' ), $settings_url );
86
- return $note;
87
- }
88
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/Admin/Product_Categories.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
 
18
  /**
19
  * General handler for the product category admin functionality.
@@ -75,11 +75,11 @@ class Product_Categories {
75
  'wc-facebook-product-categories',
76
  'facebook_for_woocommerce_product_categories',
77
  array(
78
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
79
- 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY,
80
- 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
81
- 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
82
- 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
83
  'default_google_product_category_modal_message' => $this->get_default_google_product_category_modal_message(),
84
  'default_google_product_category_modal_buttons' => $this->get_default_google_product_category_modal_buttons(),
85
  )
@@ -96,7 +96,6 @@ class Product_Categories {
96
  * @return string
97
  */
98
  private function get_default_google_product_category_modal_message() {
99
-
100
  return wp_kses_post( __( 'Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?', 'facebook-for-woocommerce' ) );
101
  }
102
 
@@ -109,9 +108,7 @@ class Product_Categories {
109
  * @return string
110
  */
111
  private function get_default_google_product_category_modal_buttons() {
112
-
113
  ob_start();
114
-
115
  ?>
116
  <button
117
  class="button button-large"
@@ -122,7 +119,6 @@ class Product_Categories {
122
  class="button button-large button-primary"
123
  ><?php esc_html_e( 'Update default Google product category', 'facebook-for-woocommerce' ); ?></button>
124
  <?php
125
-
126
  return ob_get_clean();
127
  }
128
 
@@ -135,9 +131,7 @@ class Product_Categories {
135
  * @since 2.1.0
136
  */
137
  public function render_add_google_product_category_field() {
138
-
139
  $category_field = new Google_Product_Category_Field();
140
-
141
  ?>
142
  <div class="form-field term-<?php echo esc_attr( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ); ?>-wrap">
143
  <span><?php echo esc_html( self::get_enhanced_catalog_explanation_text() ); ?></span>
@@ -175,10 +169,8 @@ class Product_Categories {
175
  * @param \WP_Term $term current taxonomy term object.
176
  */
177
  public function render_edit_google_product_category_field( \WP_Term $term ) {
178
-
179
  $category_field = new Google_Product_Category_Field();
180
- $value = get_term_meta( $term->term_id, \SkyVerge\WooCommerce\Facebook\Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
181
-
182
  ?>
183
  <tr class="form-field">
184
  <td colspan="2">
@@ -211,9 +203,7 @@ class Product_Categories {
211
  * @since 2.1.0
212
  */
213
  public function render_google_product_category_tooltip() {
214
-
215
  $tooltip_text = __( 'Choose a default Google product category for products in this category. Products need at least two category levels defined for tax to be correctly applied.', 'facebook-for-woocommerce' );
216
-
217
  ?>
218
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
219
  <?php
@@ -229,7 +219,6 @@ class Product_Categories {
229
  * @return string
230
  */
231
  public function get_google_product_category_field_title() {
232
-
233
  return __( 'Default Google product category', 'facebook-for-woocommerce' );
234
  }
235
 
@@ -241,13 +230,13 @@ class Product_Categories {
241
  * @since 2.1.0
242
  */
243
  public function ajax_render_enhanced_catalog_attributes_field() {
244
- $category_id = wc_clean( Framework\SV_WC_Helper::get_requested_value( 'selected_category' ) );
245
- $page_type = wc_clean( Framework\SV_WC_Helper::get_requested_value( 'page_type' ) );
246
 
247
  switch ( $page_type ) {
248
  case Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY:
249
- $tag_id = intval( wc_clean( Framework\SV_WC_Helper::get_requested_value( 'tag_id' ) ) );
250
- $taxonomy = wc_clean( Framework\SV_WC_Helper::get_requested_value( 'taxonomy' ) );
251
  $term = get_term( $tag_id, $taxonomy );
252
  $this->render_edit_enhanced_catalog_attributes_field( $term, $category_id );
253
  break;
@@ -255,9 +244,9 @@ class Product_Categories {
255
  $this->render_add_enhanced_catalog_attributes_field( $category_id );
256
  break;
257
  case Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT:
258
- $item_id = intval( wc_clean( Framework\SV_WC_Helper::get_requested_value( 'item_id' ) ) );
259
  $product = new \WC_Product( $item_id );
260
- \SkyVerge\WooCommerce\Facebook\Admin\Products::render_enhanced_catalog_attributes_fields( $category_id, $product );
261
  break;
262
  }
263
  }
@@ -274,7 +263,7 @@ class Product_Categories {
274
  */
275
  public function render_edit_enhanced_catalog_attributes_field( \WP_Term $term, $category_id = null ) {
276
  if ( empty( $category_id ) ) {
277
- $category_id = get_term_meta( $term->term_id, \SkyVerge\WooCommerce\Facebook\Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
278
  }
279
 
280
  $enhanced_attribute_fields = new Enhanced_Catalog_Attribute_Fields( Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, $term );
@@ -354,9 +343,7 @@ class Product_Categories {
354
  * @since 2.1.0
355
  */
356
  public function render_enhanced_catalog_attributes_tooltip() {
357
-
358
  $tooltip_text = __( 'Select default values for enhanced attributes within this category', 'facebook-for-woocommerce' );
359
-
360
  ?>
361
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
362
  <?php
@@ -372,7 +359,6 @@ class Product_Categories {
372
  * @return string
373
  */
374
  public function render_enhanced_catalog_attributes_title() {
375
-
376
  return __( 'Category Specific Attributes', 'facebook-for-woocommerce' );
377
  }
378
 
@@ -389,9 +375,9 @@ class Product_Categories {
389
  */
390
  public function save_google_product_category_and_enhanced_attributes( $term_id, $tt_id, $taxonomy ) {
391
 
392
- $google_product_category_id = wc_clean( Framework\SV_WC_Helper::get_posted_value( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ) );
393
 
394
- \SkyVerge\WooCommerce\Facebook\Product_Categories::update_google_product_category_id( $term_id, $google_product_category_id );
395
  $this->save_enhanced_catalog_attributes( $term_id, $tt_id, $taxonomy );
396
 
397
  $term = get_term( $term_id, $taxonomy );
@@ -443,17 +429,17 @@ class Product_Categories {
443
  * @param string $taxonomy Taxonomy slug.
444
  */
445
  public function save_enhanced_catalog_attributes( $term_id, $tt_id, $taxonomy ) {
446
- $enhanced_catalog_attributes = \SkyVerge\WooCommerce\Facebook\Products::get_enhanced_catalog_attributes_from_request();
447
 
448
  foreach ( $enhanced_catalog_attributes as $key => $value ) {
449
- $meta_key = \SkyVerge\WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . $key;
450
  update_term_meta( $term_id, $meta_key, $value );
451
  }
452
 
453
  if ( ! isset( $enhanced_catalog_attributes[ Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY ] ) ) {
454
  // This is a checkbox so won't show in the post data if it's been unchecked,
455
  // hence if it's unset we should clear the term meta for it.
456
- $meta_key = \SkyVerge\WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY;
457
  update_term_meta( $term_id, $meta_key, null );
458
  }
459
  }
@@ -469,9 +455,6 @@ class Product_Categories {
469
  * @return bool
470
  */
471
  public function is_categories_screen() {
472
-
473
- return Framework\SV_WC_Helper::is_current_screen( 'edit-product_cat' );
474
  }
475
-
476
-
477
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Helper;
17
 
18
  /**
19
  * General handler for the product category admin functionality.
75
  'wc-facebook-product-categories',
76
  'facebook_for_woocommerce_product_categories',
77
  array(
78
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
79
+ 'enhanced_attribute_optional_selector' => Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTE_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY,
80
+ 'enhanced_attribute_page_type_edit_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
81
+ 'enhanced_attribute_page_type_add_category' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
82
+ 'enhanced_attribute_page_type_edit_product' => Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
83
  'default_google_product_category_modal_message' => $this->get_default_google_product_category_modal_message(),
84
  'default_google_product_category_modal_buttons' => $this->get_default_google_product_category_modal_buttons(),
85
  )
96
  * @return string
97
  */
98
  private function get_default_google_product_category_modal_message() {
 
99
  return wp_kses_post( __( 'Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?', 'facebook-for-woocommerce' ) );
100
  }
101
 
108
  * @return string
109
  */
110
  private function get_default_google_product_category_modal_buttons() {
 
111
  ob_start();
 
112
  ?>
113
  <button
114
  class="button button-large"
119
  class="button button-large button-primary"
120
  ><?php esc_html_e( 'Update default Google product category', 'facebook-for-woocommerce' ); ?></button>
121
  <?php
 
122
  return ob_get_clean();
123
  }
124
 
131
  * @since 2.1.0
132
  */
133
  public function render_add_google_product_category_field() {
 
134
  $category_field = new Google_Product_Category_Field();
 
135
  ?>
136
  <div class="form-field term-<?php echo esc_attr( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ); ?>-wrap">
137
  <span><?php echo esc_html( self::get_enhanced_catalog_explanation_text() ); ?></span>
169
  * @param \WP_Term $term current taxonomy term object.
170
  */
171
  public function render_edit_google_product_category_field( \WP_Term $term ) {
 
172
  $category_field = new Google_Product_Category_Field();
173
+ $value = get_term_meta( $term->term_id, \WooCommerce\Facebook\Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
 
174
  ?>
175
  <tr class="form-field">
176
  <td colspan="2">
203
  * @since 2.1.0
204
  */
205
  public function render_google_product_category_tooltip() {
 
206
  $tooltip_text = __( 'Choose a default Google product category for products in this category. Products need at least two category levels defined for tax to be correctly applied.', 'facebook-for-woocommerce' );
 
207
  ?>
208
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
209
  <?php
219
  * @return string
220
  */
221
  public function get_google_product_category_field_title() {
 
222
  return __( 'Default Google product category', 'facebook-for-woocommerce' );
223
  }
224
 
230
  * @since 2.1.0
231
  */
232
  public function ajax_render_enhanced_catalog_attributes_field() {
233
+ $category_id = wc_clean( Helper::get_requested_value( 'selected_category' ) );
234
+ $page_type = wc_clean( Helper::get_requested_value( 'page_type' ) );
235
 
236
  switch ( $page_type ) {
237
  case Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY:
238
+ $tag_id = intval( wc_clean( Helper::get_requested_value( 'tag_id' ) ) );
239
+ $taxonomy = wc_clean( Helper::get_requested_value( 'taxonomy' ) );
240
  $term = get_term( $tag_id, $taxonomy );
241
  $this->render_edit_enhanced_catalog_attributes_field( $term, $category_id );
242
  break;
244
  $this->render_add_enhanced_catalog_attributes_field( $category_id );
245
  break;
246
  case Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT:
247
+ $item_id = intval( wc_clean( Helper::get_requested_value( 'item_id' ) ) );
248
  $product = new \WC_Product( $item_id );
249
+ Products::render_enhanced_catalog_attributes_fields( $category_id, $product );
250
  break;
251
  }
252
  }
263
  */
264
  public function render_edit_enhanced_catalog_attributes_field( \WP_Term $term, $category_id = null ) {
265
  if ( empty( $category_id ) ) {
266
+ $category_id = get_term_meta( $term->term_id, \WooCommerce\Facebook\Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
267
  }
268
 
269
  $enhanced_attribute_fields = new Enhanced_Catalog_Attribute_Fields( Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY, $term );
343
  * @since 2.1.0
344
  */
345
  public function render_enhanced_catalog_attributes_tooltip() {
 
346
  $tooltip_text = __( 'Select default values for enhanced attributes within this category', 'facebook-for-woocommerce' );
 
347
  ?>
348
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
349
  <?php
359
  * @return string
360
  */
361
  public function render_enhanced_catalog_attributes_title() {
 
362
  return __( 'Category Specific Attributes', 'facebook-for-woocommerce' );
363
  }
364
 
375
  */
376
  public function save_google_product_category_and_enhanced_attributes( $term_id, $tt_id, $taxonomy ) {
377
 
378
+ $google_product_category_id = wc_clean( Helper::get_posted_value( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ) );
379
 
380
+ \WooCommerce\Facebook\Product_Categories::update_google_product_category_id( $term_id, $google_product_category_id );
381
  $this->save_enhanced_catalog_attributes( $term_id, $tt_id, $taxonomy );
382
 
383
  $term = get_term( $term_id, $taxonomy );
429
  * @param string $taxonomy Taxonomy slug.
430
  */
431
  public function save_enhanced_catalog_attributes( $term_id, $tt_id, $taxonomy ) {
432
+ $enhanced_catalog_attributes = \WooCommerce\Facebook\Products::get_enhanced_catalog_attributes_from_request();
433
 
434
  foreach ( $enhanced_catalog_attributes as $key => $value ) {
435
+ $meta_key = \WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . $key;
436
  update_term_meta( $term_id, $meta_key, $value );
437
  }
438
 
439
  if ( ! isset( $enhanced_catalog_attributes[ Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY ] ) ) {
440
  // This is a checkbox so won't show in the post data if it's been unchecked,
441
  // hence if it's unset we should clear the term meta for it.
442
+ $meta_key = \WooCommerce\Facebook\Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY;
443
  update_term_meta( $term_id, $meta_key, null );
444
  }
445
  }
455
  * @return bool
456
  */
457
  public function is_categories_screen() {
458
+ return Helper::is_current_screen( 'edit-product_cat' );
 
459
  }
 
 
460
  }
includes/Admin/Product_Sets.php CHANGED
@@ -9,13 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
- if ( ! defined( 'ABSPATH' ) ) {
15
- exit;
16
- }
17
 
18
- use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
19
 
20
  /**
21
  * General handler for the product set admin functionality.
@@ -33,15 +31,15 @@ class Product_Sets {
33
  */
34
  protected $allowed_html = array(
35
  'label' => array(
36
- 'for' => array(),
37
  ),
38
  'input' => array(
39
- 'type' => array(),
40
- 'name' => array(),
41
- 'id' => array(),
42
  ),
43
  'p' => array(
44
- 'class' => array(),
45
  ),
46
  );
47
 
@@ -61,13 +59,10 @@ class Product_Sets {
61
  * @since 2.3.0
62
  */
63
  public function __construct() {
64
-
65
  $this->categories_field = \WC_Facebookcommerce::PRODUCT_SET_META;
66
-
67
  // add taxonomy custom field
68
  add_action( 'fb_product_set_add_form_fields', array( $this, 'category_field_on_new' ) );
69
  add_action( 'fb_product_set_edit_form', array( $this, 'category_field_on_edit' ) );
70
-
71
  // save custom field data
72
  add_action( 'created_fb_product_set', array( $this, 'save_custom_field' ), 10, 2 );
73
  add_action( 'edited_fb_product_set', array( $this, 'save_custom_field' ), 10, 2 );
@@ -97,10 +92,8 @@ class Product_Sets {
97
  * @param WP_Term $term Term object.
98
  */
99
  public function category_field_on_edit( $term ) {
100
-
101
  // gets term id
102
  $term_id = empty( $term->term_id ) ? '' : $term->term_id;
103
-
104
  ?>
105
  <table class="form-table" role="presentation">
106
  <tbody>
@@ -110,7 +103,6 @@ class Product_Sets {
110
  </tr>
111
  </tbody>
112
  </table>
113
-
114
  <?php
115
  }
116
 
@@ -124,10 +116,8 @@ class Product_Sets {
124
  * @param int $tt_id Term taxonomy ID.
125
  */
126
  public function save_custom_field( $term_id, $tt_id ) {
127
-
128
  $wc_product_cats = empty( $_POST[ $this->categories_field ] ) ? '' : $_POST[ $this->categories_field ]; //phpcs:ignore
129
  if ( ! empty( $wc_product_cats ) ) {
130
-
131
  $wc_product_cats = array_map(
132
  function( $item ) {
133
  return absint( $item );
@@ -135,7 +125,6 @@ class Product_Sets {
135
  $wc_product_cats
136
  );
137
  }
138
-
139
  update_term_meta( $term_id, $this->categories_field, $wc_product_cats );
140
  }
141
 
@@ -160,10 +149,8 @@ class Product_Sets {
160
  * @param int $term_id The Term ID that is editing.
161
  */
162
  protected function get_field( $term_id = '' ) {
163
-
164
  $saved_items = get_term_meta( $term_id, $this->categories_field, true );
165
  $product_cats = get_terms( 'product_cat', array( 'hide_empty' => 0 ) );
166
-
167
  ?>
168
  <div class="select2 updating-message"><p></p></div>
169
  <select
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
+ defined( 'ABSPATH' ) || exit;
 
 
15
 
16
+ use WP_Term;
17
 
18
  /**
19
  * General handler for the product set admin functionality.
31
  */
32
  protected $allowed_html = array(
33
  'label' => array(
34
+ 'for' => [],
35
  ),
36
  'input' => array(
37
+ 'type' => [],
38
+ 'name' => [],
39
+ 'id' => [],
40
  ),
41
  'p' => array(
42
+ 'class' => [],
43
  ),
44
  );
45
 
59
  * @since 2.3.0
60
  */
61
  public function __construct() {
 
62
  $this->categories_field = \WC_Facebookcommerce::PRODUCT_SET_META;
 
63
  // add taxonomy custom field
64
  add_action( 'fb_product_set_add_form_fields', array( $this, 'category_field_on_new' ) );
65
  add_action( 'fb_product_set_edit_form', array( $this, 'category_field_on_edit' ) );
 
66
  // save custom field data
67
  add_action( 'created_fb_product_set', array( $this, 'save_custom_field' ), 10, 2 );
68
  add_action( 'edited_fb_product_set', array( $this, 'save_custom_field' ), 10, 2 );
92
  * @param WP_Term $term Term object.
93
  */
94
  public function category_field_on_edit( $term ) {
 
95
  // gets term id
96
  $term_id = empty( $term->term_id ) ? '' : $term->term_id;
 
97
  ?>
98
  <table class="form-table" role="presentation">
99
  <tbody>
103
  </tr>
104
  </tbody>
105
  </table>
 
106
  <?php
107
  }
108
 
116
  * @param int $tt_id Term taxonomy ID.
117
  */
118
  public function save_custom_field( $term_id, $tt_id ) {
 
119
  $wc_product_cats = empty( $_POST[ $this->categories_field ] ) ? '' : $_POST[ $this->categories_field ]; //phpcs:ignore
120
  if ( ! empty( $wc_product_cats ) ) {
 
121
  $wc_product_cats = array_map(
122
  function( $item ) {
123
  return absint( $item );
125
  $wc_product_cats
126
  );
127
  }
 
128
  update_term_meta( $term_id, $this->categories_field, $wc_product_cats );
129
  }
130
 
149
  * @param int $term_id The Term ID that is editing.
150
  */
151
  protected function get_field( $term_id = '' ) {
 
152
  $saved_items = get_term_meta( $term_id, $this->categories_field, true );
153
  $product_cats = get_terms( 'product_cat', array( 'hide_empty' => 0 ) );
 
154
  ?>
155
  <div class="select2 updating-message"><p></p></div>
156
  <select
includes/Admin/Product_Sync_Meta_Box.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
 
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\Admin;
4
 
5
  defined( 'ABSPATH' ) || exit;
6
 
1
  <?php
2
+ // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Admin;
5
 
6
  defined( 'ABSPATH' ) || exit;
7
 
includes/Admin/Products.php CHANGED
@@ -9,13 +9,12 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\AJAX;
17
- use SkyVerge\WooCommerce\Facebook\Products as Products_Handler;
18
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
19
 
20
  /**
21
  * General handler for product admin functionality.
@@ -24,7 +23,6 @@ use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
24
  */
25
  class Products {
26
 
27
-
28
  /** @var string Commerce enabled field */
29
  const FIELD_COMMERCE_ENABLED = 'wc_facebook_commerce_enabled';
30
 
@@ -68,7 +66,6 @@ class Products {
68
  null,
69
  $product
70
  );
71
-
72
  if (
73
  empty( $category_id ) ||
74
  $category_handler->is_category( $category_id ) &&
@@ -77,7 +74,6 @@ class Products {
77
  // show nothing
78
  return;
79
  }
80
-
81
  ?>
82
  <p class="form-field wc-facebook-enhanced-catalog-attribute-row">
83
  <label for="<?php echo esc_attr( Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTES_ID ); ?>">
@@ -97,9 +93,7 @@ class Products {
97
  * @since 2.1.0
98
  */
99
  public static function render_enhanced_catalog_attributes_tooltip() {
100
-
101
  $tooltip_text = __( 'Select values for enhanced attributes for this product', 'facebook-for-woocommerce' );
102
-
103
  ?>
104
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
105
  <?php
@@ -115,7 +109,6 @@ class Products {
115
  * @return string
116
  */
117
  public static function render_enhanced_catalog_attributes_title() {
118
-
119
  return __( 'Category Specific Attributes', 'facebook-for-woocommerce' );
120
  }
121
 
@@ -129,11 +122,8 @@ class Products {
129
  * @param \WC_Product $product product object
130
  */
131
  public static function render_google_product_category_fields( \WC_Product $product ) {
132
-
133
  $field = new Google_Product_Category_Field();
134
-
135
  $field->render( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID );
136
-
137
  ?>
138
  <p class="form-field">
139
  <label for="<?php echo esc_attr( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ); ?>">
@@ -150,7 +140,6 @@ class Products {
150
  <?php
151
  }
152
 
153
-
154
  /**
155
  * Gets a list of attribute names and labels that match any of the given words.
156
  *
@@ -161,23 +150,17 @@ class Products {
161
  * @return array
162
  */
163
  private static function filter_available_product_attribute_names( \WC_Product $product, $words ) {
164
-
165
  $attributes = array();
166
-
167
  foreach ( self::get_available_product_attribute_names( $product ) as $name => $label ) {
168
-
169
  foreach ( $words as $word ) {
170
-
171
- if ( Framework\SV_WC_Helper::str_exists( wc_strtolower( $label ), $word ) || Framework\SV_WC_Helper::str_exists( wc_strtolower( $name ), $word ) ) {
172
  $attributes[ $name ] = $label;
173
  }
174
  }
175
  }
176
-
177
  return $attributes;
178
  }
179
 
180
-
181
  /**
182
  * Gets a indexed list of available product attributes with the name of the attribute as key and the label as the value.
183
  *
@@ -187,7 +170,6 @@ class Products {
187
  * @return array
188
  */
189
  public static function get_available_product_attribute_names( \WC_Product $product ) {
190
-
191
  return array_map(
192
  function( $attribute ) use ( $product ) {
193
  return wc_attribute_label( $attribute->get_name(), $product );
@@ -196,7 +178,6 @@ class Products {
196
  );
197
  }
198
 
199
-
200
  /**
201
  * Renders the Commerce settings fields.
202
  *
@@ -207,7 +188,6 @@ class Products {
207
  * @param \WC_Product $product product object
208
  */
209
  public static function render_commerce_fields( \WC_Product $product ) {
210
-
211
  ?>
212
  <p class="form-field <?php echo esc_attr( self::FIELD_COMMERCE_ENABLED ); ?>_field">
213
  <label for="<?php echo esc_attr( self::FIELD_COMMERCE_ENABLED ); ?>">
@@ -240,11 +220,9 @@ class Products {
240
  ?>
241
  </p>
242
  </div>
243
-
244
  <?php
245
  }
246
 
247
-
248
  /**
249
  * Saves the Commerce settings.
250
  *
@@ -255,29 +233,20 @@ class Products {
255
  * @param \WC_Product $product product object
256
  */
257
  public static function save_commerce_fields( \WC_Product $product ) {
258
-
259
- $commerce_enabled = wc_string_to_bool( Framework\SV_WC_Helper::get_posted_value( self::FIELD_COMMERCE_ENABLED ) );
260
- $google_product_category_id = wc_clean( Framework\SV_WC_Helper::get_posted_value( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ) );
261
  $enhanced_catalog_attributes = Products_Handler::get_enhanced_catalog_attributes_from_request();
262
-
263
  foreach ( $enhanced_catalog_attributes as $key => $value ) {
264
  Products_Handler::update_product_enhanced_catalog_attribute( $product, $key, $value );
265
  }
266
-
267
  if ( ! isset( $enhanced_catalog_attributes[ Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY ] ) ) {
268
  // This is a checkbox so won't show in the post data if it's been unchecked,
269
  // hence if it's unset we should clear the term meta for it.
270
  Products_Handler::update_product_enhanced_catalog_attribute( $product, Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, null );
271
  }
272
-
273
  Products_Handler::update_commerce_enabled_for_product( $product, $commerce_enabled );
274
-
275
  if ( $google_product_category_id !== Products_Handler::get_google_product_category_id( $product ) ) {
276
-
277
  Products_Handler::update_google_product_category_id( $product, $google_product_category_id );
278
  }
279
-
280
  }
281
-
282
-
283
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Helper;
17
+ use WooCommerce\Facebook\Products as Products_Handler;
 
18
 
19
  /**
20
  * General handler for product admin functionality.
23
  */
24
  class Products {
25
 
 
26
  /** @var string Commerce enabled field */
27
  const FIELD_COMMERCE_ENABLED = 'wc_facebook_commerce_enabled';
28
 
66
  null,
67
  $product
68
  );
 
69
  if (
70
  empty( $category_id ) ||
71
  $category_handler->is_category( $category_id ) &&
74
  // show nothing
75
  return;
76
  }
 
77
  ?>
78
  <p class="form-field wc-facebook-enhanced-catalog-attribute-row">
79
  <label for="<?php echo esc_attr( Enhanced_Catalog_Attribute_Fields::FIELD_ENHANCED_CATALOG_ATTRIBUTES_ID ); ?>">
93
  * @since 2.1.0
94
  */
95
  public static function render_enhanced_catalog_attributes_tooltip() {
 
96
  $tooltip_text = __( 'Select values for enhanced attributes for this product', 'facebook-for-woocommerce' );
 
97
  ?>
98
  <span class="woocommerce-help-tip" data-tip="<?php echo esc_attr( $tooltip_text ); ?>"></span>
99
  <?php
109
  * @return string
110
  */
111
  public static function render_enhanced_catalog_attributes_title() {
 
112
  return __( 'Category Specific Attributes', 'facebook-for-woocommerce' );
113
  }
114
 
122
  * @param \WC_Product $product product object
123
  */
124
  public static function render_google_product_category_fields( \WC_Product $product ) {
 
125
  $field = new Google_Product_Category_Field();
 
126
  $field->render( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID );
 
127
  ?>
128
  <p class="form-field">
129
  <label for="<?php echo esc_attr( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ); ?>">
140
  <?php
141
  }
142
 
 
143
  /**
144
  * Gets a list of attribute names and labels that match any of the given words.
145
  *
150
  * @return array
151
  */
152
  private static function filter_available_product_attribute_names( \WC_Product $product, $words ) {
 
153
  $attributes = array();
 
154
  foreach ( self::get_available_product_attribute_names( $product ) as $name => $label ) {
 
155
  foreach ( $words as $word ) {
156
+ if ( Helper::str_exists( wc_strtolower( $label ), $word ) || Helper::str_exists( wc_strtolower( $name ), $word ) ) {
 
157
  $attributes[ $name ] = $label;
158
  }
159
  }
160
  }
 
161
  return $attributes;
162
  }
163
 
 
164
  /**
165
  * Gets a indexed list of available product attributes with the name of the attribute as key and the label as the value.
166
  *
170
  * @return array
171
  */
172
  public static function get_available_product_attribute_names( \WC_Product $product ) {
 
173
  return array_map(
174
  function( $attribute ) use ( $product ) {
175
  return wc_attribute_label( $attribute->get_name(), $product );
178
  );
179
  }
180
 
 
181
  /**
182
  * Renders the Commerce settings fields.
183
  *
188
  * @param \WC_Product $product product object
189
  */
190
  public static function render_commerce_fields( \WC_Product $product ) {
 
191
  ?>
192
  <p class="form-field <?php echo esc_attr( self::FIELD_COMMERCE_ENABLED ); ?>_field">
193
  <label for="<?php echo esc_attr( self::FIELD_COMMERCE_ENABLED ); ?>">
220
  ?>
221
  </p>
222
  </div>
 
223
  <?php
224
  }
225
 
 
226
  /**
227
  * Saves the Commerce settings.
228
  *
233
  * @param \WC_Product $product product object
234
  */
235
  public static function save_commerce_fields( \WC_Product $product ) {
236
+ $commerce_enabled = wc_string_to_bool( Helper::get_posted_value( self::FIELD_COMMERCE_ENABLED ) );
237
+ $google_product_category_id = wc_clean( Helper::get_posted_value( self::FIELD_GOOGLE_PRODUCT_CATEGORY_ID ) );
 
238
  $enhanced_catalog_attributes = Products_Handler::get_enhanced_catalog_attributes_from_request();
 
239
  foreach ( $enhanced_catalog_attributes as $key => $value ) {
240
  Products_Handler::update_product_enhanced_catalog_attribute( $product, $key, $value );
241
  }
 
242
  if ( ! isset( $enhanced_catalog_attributes[ Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY ] ) ) {
243
  // This is a checkbox so won't show in the post data if it's been unchecked,
244
  // hence if it's unset we should clear the term meta for it.
245
  Products_Handler::update_product_enhanced_catalog_attribute( $product, Enhanced_Catalog_Attribute_Fields::OPTIONAL_SELECTOR_KEY, null );
246
  }
 
247
  Products_Handler::update_commerce_enabled_for_product( $product, $commerce_enabled );
 
248
  if ( $google_product_category_id !== Products_Handler::get_google_product_category_id( $product ) ) {
 
249
  Products_Handler::update_google_product_category_id( $product, $google_product_category_id );
250
  }
 
251
  }
 
 
252
  }
includes/Admin/Settings.php CHANGED
@@ -9,12 +9,15 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin;
13
 
14
  use Automattic\WooCommerce\Admin\Features\Features as WooAdminFeatures;
15
  use Automattic\WooCommerce\Admin\Features\Navigation\Menu as WooAdminMenu;
16
- use SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
 
 
18
 
19
  defined( 'ABSPATH' ) or exit;
20
 
@@ -25,7 +28,6 @@ defined( 'ABSPATH' ) or exit;
25
  */
26
  class Settings {
27
 
28
-
29
  /** @var string base settings page ID */
30
  const PAGE_ID = 'wc-facebook';
31
 
@@ -46,14 +48,12 @@ class Settings {
46
  */
47
  public $use_woo_nav;
48
 
49
-
50
  /**
51
  * Settings constructor.
52
  *
53
  * @since 2.0.0
54
  */
55
  public function __construct() {
56
-
57
  $this->screens = array(
58
  Settings_Screens\Connection::ID => new Settings_Screens\Connection(),
59
  Settings_Screens\Product_Sync::ID => new Settings_Screens\Product_Sync(),
@@ -61,11 +61,8 @@ class Settings {
61
  Settings_Screens\Messenger::ID => new Settings_Screens\Messenger(),
62
  Settings_Screens\Advertise::ID => new Settings_Screens\Advertise(),
63
  );
64
-
65
  add_action( 'admin_menu', array( $this, 'add_menu_item' ) );
66
-
67
  add_action( 'wp_loaded', array( $this, 'save' ) );
68
-
69
  add_filter( 'parent_file', array( $this, 'set_parent_and_submenu_file' ) );
70
  }
71
 
@@ -75,36 +72,31 @@ class Settings {
75
  * @since 2.0.0
76
  */
77
  public function add_menu_item() {
78
-
79
  $root_menu_item = 'woocommerce';
80
  $is_marketing_enabled = false;
81
- $this->use_woo_nav = class_exists( WooAdminFeatures::class ) && class_exists( WooAdminMenu::class ) && WooAdminFeatures::is_enabled( 'navigation' );
82
-
83
- if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
84
-
85
  if ( class_exists( WooAdminFeatures::class ) ) {
86
  $is_marketing_enabled = WooAdminFeatures::is_enabled( 'marketing' );
87
  } else {
88
  $is_marketing_enabled = is_callable( '\Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
89
  && \Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
90
  }
91
-
92
  if ( $is_marketing_enabled ) {
93
-
94
  $root_menu_item = 'woocommerce-marketing';
95
  }
96
  }
97
-
98
  add_submenu_page(
99
  $root_menu_item,
100
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
101
  __( 'Facebook', 'facebook-for-woocommerce' ),
102
  'manage_woocommerce',
103
  self::PAGE_ID,
104
- array( $this, 'render' ),
105
  5
106
  );
107
-
108
  $this->connect_to_enhanced_admin( $is_marketing_enabled ? 'marketing_page_wc-facebook' : 'woocommerce_page_wc-facebook' );
109
  $this->register_woo_nav_menu_items();
110
 
@@ -120,7 +112,7 @@ class Settings {
120
  */
121
  private function add_fb_product_sets_to_marketing_menu() {
122
  $is_connected = facebook_for_woocommerce()->get_connection_handler()->is_connected();
123
-
124
  // If a connection is not established, do not add Facebook Product Sets to Marketing menu.
125
  if ( ! $is_connected ) {
126
  return;
@@ -163,16 +155,13 @@ class Settings {
163
  * @param string $screen_id the ID to connect to
164
  */
165
  private function connect_to_enhanced_admin( $screen_id ) {
166
-
167
  if ( is_callable( 'wc_admin_connect_page' ) ) {
168
-
169
  $crumbs = array(
170
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
171
  );
172
-
173
  if ( ! empty( $_GET['tab'] ) ) {
174
  switch ( $_GET['tab'] ) {
175
- case Settings_Screens\Connection::ID:
176
  $crumbs[] = __( 'Connection', 'facebook-for-woocommerce' );
177
  break;
178
  case Settings_Screens\Messenger::ID:
@@ -186,7 +175,6 @@ class Settings {
186
  break;
187
  }
188
  }
189
-
190
  wc_admin_connect_page(
191
  array(
192
  'id' => self::PAGE_ID,
@@ -205,43 +193,28 @@ class Settings {
205
  * @since 2.0.0
206
  */
207
  public function render() {
208
-
209
  $tabs = $this->get_tabs();
210
- $current_tab = Framework\SV_WC_Helper::get_requested_value( 'tab' );
211
-
212
  if ( ! $current_tab ) {
213
  $current_tab = current( array_keys( $tabs ) );
214
  }
215
-
216
  $screen = $this->get_screen( $current_tab );
217
-
218
  ?>
219
-
220
  <div class="wrap woocommerce">
221
-
222
  <?php if ( ! $this->use_woo_nav ) : ?>
223
  <nav class="nav-tab-wrapper woo-nav-tab-wrapper">
224
-
225
  <?php foreach ( $tabs as $id => $label ) : ?>
226
  <a href="<?php echo esc_html( admin_url( 'admin.php?page=' . self::PAGE_ID . '&tab=' . esc_attr( $id ) ) ); ?>" class="nav-tab <?php echo $current_tab === $id ? 'nav-tab-active' : ''; ?>"><?php echo esc_html( $label ); ?></a>
227
  <?php endforeach; ?>
228
-
229
  </nav>
230
  <?php endif; ?>
231
-
232
  <?php facebook_for_woocommerce()->get_message_handler()->show_messages(); ?>
233
-
234
  <?php if ( $screen ) : ?>
235
-
236
  <h1 class="screen-reader-text"><?php echo esc_html( $screen->get_title() ); ?></h1>
237
  <p><?php echo wp_kses_post( $screen->get_description() ); ?></p>
238
-
239
  <?php $screen->render(); ?>
240
-
241
  <?php endif; ?>
242
-
243
  </div>
244
-
245
  <?php
246
  }
247
 
@@ -252,35 +225,24 @@ class Settings {
252
  * @since 2.0.0
253
  */
254
  public function save() {
255
-
256
- if ( ! is_admin() || Framework\SV_WC_Helper::get_requested_value( 'page' ) !== self::PAGE_ID ) {
257
  return;
258
  }
259
-
260
- $screen = $this->get_screen( Framework\SV_WC_Helper::get_posted_value( 'screen_id' ) );
261
-
262
  if ( ! $screen ) {
263
  return;
264
  }
265
-
266
- if ( ! Framework\SV_WC_Helper::get_posted_value( 'save_' . $screen->get_id() . '_settings' ) ) {
267
  return;
268
  }
269
-
270
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
271
  wp_die( __( 'You do not have permission to save these settings.', 'facebook-for-woocommerce' ) );
272
  }
273
-
274
  check_admin_referer( 'wc_facebook_admin_save_' . $screen->get_id() . '_settings' );
275
-
276
  try {
277
-
278
  $screen->save();
279
-
280
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Your settings have been saved.', 'facebook-for-woocommerce' ) );
281
-
282
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
283
-
284
  facebook_for_woocommerce()->get_message_handler()->add_error(
285
  sprintf(
286
  /* translators: Placeholders: %s - user-friendly error message */
@@ -301,9 +263,7 @@ class Settings {
301
  * @return Abstract_Settings_Screen|null
302
  */
303
  public function get_screen( $screen_id ) {
304
-
305
  $screens = $this->get_screens();
306
-
307
  return ! empty( $screens[ $screen_id ] ) && $screens[ $screen_id ] instanceof Abstract_Settings_Screen ? $screens[ $screen_id ] : null;
308
  }
309
 
@@ -316,7 +276,6 @@ class Settings {
316
  * @return Abstract_Settings_Screen[]
317
  */
318
  public function get_screens() {
319
-
320
  /**
321
  * Filters the admin settings screens.
322
  *
@@ -325,17 +284,13 @@ class Settings {
325
  * @param array $screens available screen objects
326
  */
327
  $screens = (array) apply_filters( 'wc_facebook_admin_settings_screens', $this->screens, $this );
328
-
329
  // ensure no bogus values are added via filter
330
  $screens = array_filter(
331
  $screens,
332
  function( $value ) {
333
-
334
  return $value instanceof Abstract_Settings_Screen;
335
-
336
  }
337
  );
338
-
339
  return $screens;
340
  }
341
 
@@ -348,13 +303,10 @@ class Settings {
348
  * @return array
349
  */
350
  public function get_tabs() {
351
-
352
- $tabs = array();
353
-
354
  foreach ( $this->get_screens() as $screen_id => $screen ) {
355
  $tabs[ $screen_id ] = $screen->get_label();
356
  }
357
-
358
  /**
359
  * Filters the admin settings tabs.
360
  *
@@ -374,7 +326,6 @@ class Settings {
374
  if ( ! $this->use_woo_nav ) {
375
  return;
376
  }
377
-
378
  WooAdminMenu::add_plugin_category(
379
  array(
380
  'id' => 'facebook-for-woocommerce',
@@ -382,13 +333,11 @@ class Settings {
382
  'capability' => 'manage_woocommerce',
383
  )
384
  );
385
-
386
  $order = 1;
387
  foreach ( $this->get_screens() as $screen_id => $screen ) {
388
  $url = $screen instanceof Settings_Screens\Product_Sets
389
  ? 'edit-tags.php?taxonomy=fb_product_set&post_type=product'
390
  : 'wc-facebook&tab=' . $screen->get_id();
391
-
392
  WooAdminMenu::add_plugin_item(
393
  array(
394
  'id' => 'facebook-for-woocommerce-' . $screen->get_id(),
@@ -401,6 +350,4 @@ class Settings {
401
  $order++;
402
  }
403
  }
404
-
405
-
406
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin;
13
 
14
  use Automattic\WooCommerce\Admin\Features\Features as WooAdminFeatures;
15
  use Automattic\WooCommerce\Admin\Features\Navigation\Menu as WooAdminMenu;
16
+ use WooCommerce\Facebook\Admin\Settings_Screens;
17
+ use WooCommerce\Facebook\Admin\Settings_Screens\Connection;
18
+ use WooCommerce\Facebook\Framework\Helper;
19
+ use WooCommerce\Facebook\Framework\Plugin\Compatibility;
20
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
21
 
22
  defined( 'ABSPATH' ) or exit;
23
 
28
  */
29
  class Settings {
30
 
 
31
  /** @var string base settings page ID */
32
  const PAGE_ID = 'wc-facebook';
33
 
48
  */
49
  public $use_woo_nav;
50
 
 
51
  /**
52
  * Settings constructor.
53
  *
54
  * @since 2.0.0
55
  */
56
  public function __construct() {
 
57
  $this->screens = array(
58
  Settings_Screens\Connection::ID => new Settings_Screens\Connection(),
59
  Settings_Screens\Product_Sync::ID => new Settings_Screens\Product_Sync(),
61
  Settings_Screens\Messenger::ID => new Settings_Screens\Messenger(),
62
  Settings_Screens\Advertise::ID => new Settings_Screens\Advertise(),
63
  );
 
64
  add_action( 'admin_menu', array( $this, 'add_menu_item' ) );
 
65
  add_action( 'wp_loaded', array( $this, 'save' ) );
 
66
  add_filter( 'parent_file', array( $this, 'set_parent_and_submenu_file' ) );
67
  }
68
 
72
  * @since 2.0.0
73
  */
74
  public function add_menu_item() {
 
75
  $root_menu_item = 'woocommerce';
76
  $is_marketing_enabled = false;
77
+ $this->use_woo_nav = class_exists( WooAdminFeatures::class )
78
+ && class_exists( WooAdminMenu::class )
79
+ && WooAdminFeatures::is_enabled( 'navigation' );
80
+ if ( Compatibility::is_enhanced_admin_available() ) {
81
  if ( class_exists( WooAdminFeatures::class ) ) {
82
  $is_marketing_enabled = WooAdminFeatures::is_enabled( 'marketing' );
83
  } else {
84
  $is_marketing_enabled = is_callable( '\Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
85
  && \Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
86
  }
 
87
  if ( $is_marketing_enabled ) {
 
88
  $root_menu_item = 'woocommerce-marketing';
89
  }
90
  }
 
91
  add_submenu_page(
92
  $root_menu_item,
93
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
94
  __( 'Facebook', 'facebook-for-woocommerce' ),
95
  'manage_woocommerce',
96
  self::PAGE_ID,
97
+ [ $this, 'render' ],
98
  5
99
  );
 
100
  $this->connect_to_enhanced_admin( $is_marketing_enabled ? 'marketing_page_wc-facebook' : 'woocommerce_page_wc-facebook' );
101
  $this->register_woo_nav_menu_items();
102
 
112
  */
113
  private function add_fb_product_sets_to_marketing_menu() {
114
  $is_connected = facebook_for_woocommerce()->get_connection_handler()->is_connected();
115
+
116
  // If a connection is not established, do not add Facebook Product Sets to Marketing menu.
117
  if ( ! $is_connected ) {
118
  return;
155
  * @param string $screen_id the ID to connect to
156
  */
157
  private function connect_to_enhanced_admin( $screen_id ) {
 
158
  if ( is_callable( 'wc_admin_connect_page' ) ) {
 
159
  $crumbs = array(
160
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
161
  );
 
162
  if ( ! empty( $_GET['tab'] ) ) {
163
  switch ( $_GET['tab'] ) {
164
+ case Connection::ID:
165
  $crumbs[] = __( 'Connection', 'facebook-for-woocommerce' );
166
  break;
167
  case Settings_Screens\Messenger::ID:
175
  break;
176
  }
177
  }
 
178
  wc_admin_connect_page(
179
  array(
180
  'id' => self::PAGE_ID,
193
  * @since 2.0.0
194
  */
195
  public function render() {
 
196
  $tabs = $this->get_tabs();
197
+ $current_tab = Helper::get_requested_value( 'tab' );
 
198
  if ( ! $current_tab ) {
199
  $current_tab = current( array_keys( $tabs ) );
200
  }
 
201
  $screen = $this->get_screen( $current_tab );
 
202
  ?>
 
203
  <div class="wrap woocommerce">
 
204
  <?php if ( ! $this->use_woo_nav ) : ?>
205
  <nav class="nav-tab-wrapper woo-nav-tab-wrapper">
 
206
  <?php foreach ( $tabs as $id => $label ) : ?>
207
  <a href="<?php echo esc_html( admin_url( 'admin.php?page=' . self::PAGE_ID . '&tab=' . esc_attr( $id ) ) ); ?>" class="nav-tab <?php echo $current_tab === $id ? 'nav-tab-active' : ''; ?>"><?php echo esc_html( $label ); ?></a>
208
  <?php endforeach; ?>
 
209
  </nav>
210
  <?php endif; ?>
 
211
  <?php facebook_for_woocommerce()->get_message_handler()->show_messages(); ?>
 
212
  <?php if ( $screen ) : ?>
 
213
  <h1 class="screen-reader-text"><?php echo esc_html( $screen->get_title() ); ?></h1>
214
  <p><?php echo wp_kses_post( $screen->get_description() ); ?></p>
 
215
  <?php $screen->render(); ?>
 
216
  <?php endif; ?>
 
217
  </div>
 
218
  <?php
219
  }
220
 
225
  * @since 2.0.0
226
  */
227
  public function save() {
228
+ if ( ! is_admin() || Helper::get_requested_value( 'page' ) !== self::PAGE_ID ) {
 
229
  return;
230
  }
231
+ $screen = $this->get_screen( Helper::get_posted_value( 'screen_id' ) );
 
 
232
  if ( ! $screen ) {
233
  return;
234
  }
235
+ if ( ! Helper::get_posted_value( 'save_' . $screen->get_id() . '_settings' ) ) {
 
236
  return;
237
  }
 
238
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
239
  wp_die( __( 'You do not have permission to save these settings.', 'facebook-for-woocommerce' ) );
240
  }
 
241
  check_admin_referer( 'wc_facebook_admin_save_' . $screen->get_id() . '_settings' );
 
242
  try {
 
243
  $screen->save();
 
244
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Your settings have been saved.', 'facebook-for-woocommerce' ) );
245
+ } catch ( PluginException $exception ) {
 
 
246
  facebook_for_woocommerce()->get_message_handler()->add_error(
247
  sprintf(
248
  /* translators: Placeholders: %s - user-friendly error message */
263
  * @return Abstract_Settings_Screen|null
264
  */
265
  public function get_screen( $screen_id ) {
 
266
  $screens = $this->get_screens();
 
267
  return ! empty( $screens[ $screen_id ] ) && $screens[ $screen_id ] instanceof Abstract_Settings_Screen ? $screens[ $screen_id ] : null;
268
  }
269
 
276
  * @return Abstract_Settings_Screen[]
277
  */
278
  public function get_screens() {
 
279
  /**
280
  * Filters the admin settings screens.
281
  *
284
  * @param array $screens available screen objects
285
  */
286
  $screens = (array) apply_filters( 'wc_facebook_admin_settings_screens', $this->screens, $this );
 
287
  // ensure no bogus values are added via filter
288
  $screens = array_filter(
289
  $screens,
290
  function( $value ) {
 
291
  return $value instanceof Abstract_Settings_Screen;
 
292
  }
293
  );
 
294
  return $screens;
295
  }
296
 
303
  * @return array
304
  */
305
  public function get_tabs() {
306
+ $tabs = [];
 
 
307
  foreach ( $this->get_screens() as $screen_id => $screen ) {
308
  $tabs[ $screen_id ] = $screen->get_label();
309
  }
 
310
  /**
311
  * Filters the admin settings tabs.
312
  *
326
  if ( ! $this->use_woo_nav ) {
327
  return;
328
  }
 
329
  WooAdminMenu::add_plugin_category(
330
  array(
331
  'id' => 'facebook-for-woocommerce',
333
  'capability' => 'manage_woocommerce',
334
  )
335
  );
 
336
  $order = 1;
337
  foreach ( $this->get_screens() as $screen_id => $screen ) {
338
  $url = $screen instanceof Settings_Screens\Product_Sets
339
  ? 'edit-tags.php?taxonomy=fb_product_set&post_type=product'
340
  : 'wc-facebook&tab=' . $screen->get_id();
 
341
  WooAdminMenu::add_plugin_item(
342
  array(
343
  'id' => 'facebook-for-woocommerce-' . $screen->get_id(),
350
  $order++;
351
  }
352
  }
 
 
353
  }
includes/Admin/Settings_Screens/Advertise.php CHANGED
@@ -9,31 +9,27 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Admin;
17
- use SkyVerge\WooCommerce\Facebook\Locale;
18
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0;
19
 
20
  /**
21
  * The Advertise settings screen object.
22
  */
23
- class Advertise extends Admin\Abstract_Settings_Screen {
24
-
25
 
26
  /** @var string screen ID */
27
  const ID = 'advertise';
28
 
29
-
30
  /**
31
  * Advertise settings constructor.
32
  *
33
  * @since 2.2.0
34
  */
35
  public function __construct() {
36
-
37
  $this->id = self::ID;
38
  $this->label = __( 'Advertise', 'facebook-for-woocommerce' );
39
  $this->title = __( 'Advertise', 'facebook-for-woocommerce' );
@@ -48,9 +44,7 @@ class Advertise extends Admin\Abstract_Settings_Screen {
48
  * @since 2.2.0
49
  */
50
  private function add_hooks() {
51
-
52
  add_action( 'admin_head', array( $this, 'output_scripts' ) );
53
-
54
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
55
  }
56
 
@@ -63,11 +57,9 @@ class Advertise extends Admin\Abstract_Settings_Screen {
63
  * @since 2.2.0
64
  */
65
  public function enqueue_assets() {
66
-
67
  if ( ! $this->is_current_screen_page() ) {
68
  return;
69
  }
70
-
71
  wp_enqueue_style( 'wc-facebook-admin-advertise-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-advertise.css', array(), \WC_Facebookcommerce::VERSION );
72
  }
73
 
@@ -80,9 +72,7 @@ class Advertise extends Admin\Abstract_Settings_Screen {
80
  * @since 2.1.0-dev.1
81
  */
82
  public function output_scripts() {
83
-
84
  $connection_handler = facebook_for_woocommerce()->get_connection_handler();
85
-
86
  if ( ! $connection_handler || ! $connection_handler->is_connected() || ! $this->is_current_screen_page() ) {
87
  return;
88
  }
@@ -238,9 +228,6 @@ class Advertise extends Admin\Abstract_Settings_Screen {
238
  * @return array
239
  */
240
  public function get_settings() {
241
-
242
  return array();
243
  }
244
-
245
-
246
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Locale;
17
+ use WooCommerce\Facebook\Admin\Abstract_Settings_Screen;
 
18
 
19
  /**
20
  * The Advertise settings screen object.
21
  */
22
+ class Advertise extends Abstract_Settings_Screen {
 
23
 
24
  /** @var string screen ID */
25
  const ID = 'advertise';
26
 
 
27
  /**
28
  * Advertise settings constructor.
29
  *
30
  * @since 2.2.0
31
  */
32
  public function __construct() {
 
33
  $this->id = self::ID;
34
  $this->label = __( 'Advertise', 'facebook-for-woocommerce' );
35
  $this->title = __( 'Advertise', 'facebook-for-woocommerce' );
44
  * @since 2.2.0
45
  */
46
  private function add_hooks() {
 
47
  add_action( 'admin_head', array( $this, 'output_scripts' ) );
 
48
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
49
  }
50
 
57
  * @since 2.2.0
58
  */
59
  public function enqueue_assets() {
 
60
  if ( ! $this->is_current_screen_page() ) {
61
  return;
62
  }
 
63
  wp_enqueue_style( 'wc-facebook-admin-advertise-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-advertise.css', array(), \WC_Facebookcommerce::VERSION );
64
  }
65
 
72
  * @since 2.1.0-dev.1
73
  */
74
  public function output_scripts() {
 
75
  $connection_handler = facebook_for_woocommerce()->get_connection_handler();
 
76
  if ( ! $connection_handler || ! $connection_handler->is_connected() || ! $this->is_current_screen_page() ) {
77
  return;
78
  }
228
  * @return array
229
  */
230
  public function get_settings() {
 
231
  return array();
232
  }
 
 
233
  }
includes/Admin/Settings_Screens/Connection.php CHANGED
@@ -9,17 +9,17 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Admin;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
18
 
19
  /**
20
  * The Connection settings screen object.
21
  */
22
- class Connection extends Admin\Abstract_Settings_Screen {
23
 
24
 
25
  /** @var string screen ID */
@@ -164,13 +164,11 @@ class Connection extends Admin\Abstract_Settings_Screen {
164
  $static_items['catalog']['url'] = "https://facebook.com/products/catalogs/{$catalog_id}";
165
 
166
  try {
167
-
168
  $response = facebook_for_woocommerce()->get_api()->get_catalog( $catalog_id );
169
-
170
- if ( $name = $response->get_name() ) {
171
  $static_items['catalog']['value'] = $name;
172
  }
173
- } catch ( Framework\SV_WC_API_Exception $exception ) {
174
  }
175
  }
176
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Admin\Abstract_Settings_Screen;
17
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
18
 
19
  /**
20
  * The Connection settings screen object.
21
  */
22
+ class Connection extends Abstract_Settings_Screen {
23
 
24
 
25
  /** @var string screen ID */
164
  $static_items['catalog']['url'] = "https://facebook.com/products/catalogs/{$catalog_id}";
165
 
166
  try {
 
167
  $response = facebook_for_woocommerce()->get_api()->get_catalog( $catalog_id );
168
+ if ( $name = $response->name ) {
 
169
  $static_items['catalog']['value'] = $name;
170
  }
171
+ } catch ( ApiException $exception ) {
172
  }
173
  }
174
 
includes/Admin/Settings_Screens/Messenger.php CHANGED
@@ -9,19 +9,20 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Admin;
17
- use SkyVerge\WooCommerce\Facebook\API\FBE\Configuration;
18
- use SkyVerge\WooCommerce\Facebook\Locale;
19
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
20
 
21
  /**
22
  * The Messenger settings screen object.
23
  */
24
- class Messenger extends Admin\Abstract_Settings_Screen {
25
 
26
 
27
  /** @var string screen ID */
@@ -32,13 +33,10 @@ class Messenger extends Admin\Abstract_Settings_Screen {
32
  * Connection constructor.
33
  */
34
  public function __construct() {
35
-
36
  $this->id = self::ID;
37
  $this->label = __( 'Messenger', 'facebook-for-woocommerce' );
38
  $this->title = __( 'Messenger', 'facebook-for-woocommerce' );
39
-
40
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
41
-
42
  add_action( 'woocommerce_admin_field_messenger_locale', array( $this, 'render_locale_field' ) );
43
  add_action( 'woocommerce_admin_field_messenger_greeting', array( $this, 'render_greeting_field' ) );
44
  }
@@ -52,7 +50,6 @@ class Messenger extends Admin\Abstract_Settings_Screen {
52
  * @since 2.0.0
53
  */
54
  public function enqueue_assets() {
55
-
56
  // TODO: empty for now, until we add more robust Messenger settings {CW 2020-06-17}
57
  }
58
 
@@ -70,11 +67,9 @@ class Messenger extends Admin\Abstract_Settings_Screen {
70
 
71
  $configured_locale = get_option( \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE, '' );;
72
  $supported_locales = Locale::get_supported_locales();
73
-
74
  if ( ! empty( $supported_locales[ $configured_locale ] ) ) {
75
  $configured_locale = $supported_locales[ $configured_locale ];
76
  }
77
-
78
  ?>
79
  <tr valign="top">
80
  <th scope="row" class="titledesc">
@@ -100,7 +95,6 @@ class Messenger extends Admin\Abstract_Settings_Screen {
100
  * @param array $field field data
101
  */
102
  public function render_greeting_field( $field ) {
103
-
104
  ?>
105
  <tr valign="top">
106
  <th scope="row" class="titledesc">
@@ -131,16 +125,12 @@ class Messenger extends Admin\Abstract_Settings_Screen {
131
  * @return array
132
  */
133
  public function get_settings() {
134
-
135
  $is_enabled = get_option( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER, 'no' );
136
-
137
  $settings = array(
138
-
139
  array(
140
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
141
  'type' => 'title',
142
  ),
143
-
144
  array(
145
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER,
146
  'title' => __( 'Enable Messenger', 'facebook-for-woocommerce' ),
@@ -150,23 +140,18 @@ class Messenger extends Admin\Abstract_Settings_Screen {
150
  'value' => $is_enabled,
151
  ),
152
  );
153
-
154
  // only add the static configuration display if messenger is enabled
155
  if ( 'yes' === $is_enabled ) {
156
-
157
  $settings[] = array(
158
  'title' => __( 'Language', 'facebook-for-woocommerce' ),
159
  'type' => 'messenger_locale',
160
  );
161
-
162
  $settings[] = array(
163
  'title' => __( 'Greeting & Colors', 'facebook-for-woocommerce' ),
164
  'type' => 'messenger_greeting',
165
  );
166
  }
167
-
168
  $settings[] = array( 'type' => 'sectionend' );
169
-
170
  return $settings;
171
  }
172
 
@@ -179,7 +164,6 @@ class Messenger extends Admin\Abstract_Settings_Screen {
179
  * @return string
180
  */
181
  public function get_disconnected_message() {
182
-
183
  return sprintf(
184
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
185
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger.', 'facebook-for-woocommerce' ),
@@ -195,73 +179,52 @@ class Messenger extends Admin\Abstract_Settings_Screen {
195
  *
196
  * @since 2.0.0
197
  *
198
- * @throws Framework\SV_WC_Plugin_Exception
199
  */
200
  public function save() {
201
-
202
  $plugin = facebook_for_woocommerce();
203
  $external_business_id = $plugin->get_connection_handler()->get_external_business_id();
204
-
205
  try {
206
-
207
  // first get the latest configuration details
208
  $response = $plugin->get_api()->get_business_configuration( $external_business_id );
209
-
210
  $configuration = $response->get_messenger_configuration();
211
-
212
  if ( ! $configuration ) {
213
- throw new Framework\SV_WC_API_Exception( 'Could not retrieve latest messenger configuration' );
214
  }
215
-
216
  $update = false;
217
- $setting_enabled = wc_string_to_bool( Framework\SV_WC_Helper::get_posted_value( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER ) );
218
-
219
  // only consider updating if the setting has changed
220
  if ( $setting_enabled !== $configuration->is_enabled() ) {
221
  $update = true;
222
  }
223
-
224
  // also consider updating if the site's URL was removed from approved URLs
225
  if ( ! in_array( home_url( '/' ), $configuration->get_domains(), true ) ) {
226
  $update = true;
227
  }
228
-
229
  // make the API call if settings have changed
230
  if ( $update ) {
231
-
232
  $configuration->set_enabled( $setting_enabled );
233
  $configuration->add_domain( home_url( '/' ) );
234
 
235
  try {
236
  $plugin->get_api()->update_messenger_configuration( $external_business_id, $configuration );
237
-
238
  update_option( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER, wc_bool_to_string( $configuration->is_enabled() ) );
239
-
240
  if ( $default_locale = $configuration->get_default_locale() ) {
241
  update_option( \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE, $default_locale );
242
  }
243
- } catch ( Framework\SV_WC_API_Exception $exception ) {
244
-
245
  // always log this error, regardless of debug setting
246
  $plugin->log( 'Could not display messenger settings. ' . $exception->getMessage() );
247
-
248
  }
249
 
250
  delete_transient( 'wc_facebook_business_configuration_refresh' );
251
-
252
  }
253
-
254
  // save any real settings
255
  parent::save();
256
-
257
- } catch ( Framework\SV_WC_API_Exception $exception ) {
258
-
259
  // always log this error, regardless of debug setting
260
  $plugin->log( 'Could not update remote messenger settings. ' . $exception->getMessage() );
261
-
262
- throw new Framework\SV_WC_Plugin_Exception( __( 'Please try again.', 'facebook-for-woocommerce' ) );
263
  }
264
  }
265
-
266
-
267
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Admin\Abstract_Settings_Screen;
17
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
18
+ use WooCommerce\Facebook\Framework\Helper;
19
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
20
+ use WooCommerce\Facebook\Locale;
21
 
22
  /**
23
  * The Messenger settings screen object.
24
  */
25
+ class Messenger extends Abstract_Settings_Screen {
26
 
27
 
28
  /** @var string screen ID */
33
  * Connection constructor.
34
  */
35
  public function __construct() {
 
36
  $this->id = self::ID;
37
  $this->label = __( 'Messenger', 'facebook-for-woocommerce' );
38
  $this->title = __( 'Messenger', 'facebook-for-woocommerce' );
 
39
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
 
40
  add_action( 'woocommerce_admin_field_messenger_locale', array( $this, 'render_locale_field' ) );
41
  add_action( 'woocommerce_admin_field_messenger_greeting', array( $this, 'render_greeting_field' ) );
42
  }
50
  * @since 2.0.0
51
  */
52
  public function enqueue_assets() {
 
53
  // TODO: empty for now, until we add more robust Messenger settings {CW 2020-06-17}
54
  }
55
 
67
 
68
  $configured_locale = get_option( \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE, '' );;
69
  $supported_locales = Locale::get_supported_locales();
 
70
  if ( ! empty( $supported_locales[ $configured_locale ] ) ) {
71
  $configured_locale = $supported_locales[ $configured_locale ];
72
  }
 
73
  ?>
74
  <tr valign="top">
75
  <th scope="row" class="titledesc">
95
  * @param array $field field data
96
  */
97
  public function render_greeting_field( $field ) {
 
98
  ?>
99
  <tr valign="top">
100
  <th scope="row" class="titledesc">
125
  * @return array
126
  */
127
  public function get_settings() {
 
128
  $is_enabled = get_option( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER, 'no' );
 
129
  $settings = array(
 
130
  array(
131
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
132
  'type' => 'title',
133
  ),
 
134
  array(
135
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER,
136
  'title' => __( 'Enable Messenger', 'facebook-for-woocommerce' ),
140
  'value' => $is_enabled,
141
  ),
142
  );
 
143
  // only add the static configuration display if messenger is enabled
144
  if ( 'yes' === $is_enabled ) {
 
145
  $settings[] = array(
146
  'title' => __( 'Language', 'facebook-for-woocommerce' ),
147
  'type' => 'messenger_locale',
148
  );
 
149
  $settings[] = array(
150
  'title' => __( 'Greeting & Colors', 'facebook-for-woocommerce' ),
151
  'type' => 'messenger_greeting',
152
  );
153
  }
 
154
  $settings[] = array( 'type' => 'sectionend' );
 
155
  return $settings;
156
  }
157
 
164
  * @return string
165
  */
166
  public function get_disconnected_message() {
 
167
  return sprintf(
168
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
169
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger.', 'facebook-for-woocommerce' ),
179
  *
180
  * @since 2.0.0
181
  *
182
+ * @throws PluginException
183
  */
184
  public function save() {
 
185
  $plugin = facebook_for_woocommerce();
186
  $external_business_id = $plugin->get_connection_handler()->get_external_business_id();
 
187
  try {
 
188
  // first get the latest configuration details
189
  $response = $plugin->get_api()->get_business_configuration( $external_business_id );
 
190
  $configuration = $response->get_messenger_configuration();
 
191
  if ( ! $configuration ) {
192
+ throw new ApiException( 'Could not retrieve latest messenger configuration' );
193
  }
 
194
  $update = false;
195
+ $setting_enabled = wc_string_to_bool( Helper::get_posted_value( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER ) );
 
196
  // only consider updating if the setting has changed
197
  if ( $setting_enabled !== $configuration->is_enabled() ) {
198
  $update = true;
199
  }
 
200
  // also consider updating if the site's URL was removed from approved URLs
201
  if ( ! in_array( home_url( '/' ), $configuration->get_domains(), true ) ) {
202
  $update = true;
203
  }
 
204
  // make the API call if settings have changed
205
  if ( $update ) {
 
206
  $configuration->set_enabled( $setting_enabled );
207
  $configuration->add_domain( home_url( '/' ) );
208
 
209
  try {
210
  $plugin->get_api()->update_messenger_configuration( $external_business_id, $configuration );
 
211
  update_option( \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER, wc_bool_to_string( $configuration->is_enabled() ) );
 
212
  if ( $default_locale = $configuration->get_default_locale() ) {
213
  update_option( \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE, $default_locale );
214
  }
215
+ } catch ( ApiException $exception ) {
 
216
  // always log this error, regardless of debug setting
217
  $plugin->log( 'Could not display messenger settings. ' . $exception->getMessage() );
 
218
  }
219
 
220
  delete_transient( 'wc_facebook_business_configuration_refresh' );
 
221
  }
 
222
  // save any real settings
223
  parent::save();
224
+ } catch ( ApiException $exception ) {
 
 
225
  // always log this error, regardless of debug setting
226
  $plugin->log( 'Could not update remote messenger settings. ' . $exception->getMessage() );
227
+ throw new PluginException( __( 'Please try again.', 'facebook-for-woocommerce' ) );
 
228
  }
229
  }
 
 
230
  }
includes/Admin/Settings_Screens/Product_Sets.php CHANGED
@@ -9,18 +9,16 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Admin;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_5_4\SV_WC_Helper;
18
 
19
  /**
20
  * The Product sets redirect object.
21
  */
22
- class Product_Sets extends Admin\Abstract_Settings_Screen {
23
-
24
 
25
  /** @var string screen ID */
26
  const ID = 'product_sets';
@@ -29,7 +27,6 @@ class Product_Sets extends Admin\Abstract_Settings_Screen {
29
  * Connection constructor.
30
  */
31
  public function __construct() {
32
-
33
  $this->id = self::ID;
34
  $this->label = __( 'Product sets', 'facebook-for-woocommerce' );
35
  $this->title = __( 'Product sets', 'facebook-for-woocommerce' );
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Admin\Abstract_Settings_Screen;
 
17
 
18
  /**
19
  * The Product sets redirect object.
20
  */
21
+ class Product_Sets extends Abstract_Settings_Screen {
 
22
 
23
  /** @var string screen ID */
24
  const ID = 'product_sets';
27
  * Connection constructor.
28
  */
29
  public function __construct() {
 
30
  $this->id = self::ID;
31
  $this->label = __( 'Product sets', 'facebook-for-woocommerce' );
32
  $this->title = __( 'Product sets', 'facebook-for-woocommerce' );
includes/Admin/Settings_Screens/Product_Sync.php CHANGED
@@ -9,20 +9,20 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Admin;
17
- use SkyVerge\WooCommerce\Facebook\Products;
18
- use SkyVerge\WooCommerce\Facebook\Products\Sync;
19
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Helper;
 
20
 
21
  /**
22
  * The Messenger settings screen object.
23
  */
24
- class Product_Sync extends Admin\Abstract_Settings_Screen {
25
-
26
 
27
  /** @var string screen ID */
28
  const ID = 'product_sync';
@@ -38,15 +38,11 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
38
  * Connection constructor.
39
  */
40
  public function __construct() {
41
-
42
  $this->id = self::ID;
43
  $this->label = __( 'Product sync', 'facebook-for-woocommerce' );
44
  $this->title = __( 'Product sync', 'facebook-for-woocommerce' );
45
-
46
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
47
-
48
  add_action( 'woocommerce_admin_field_product_sync_title', array( $this, 'render_title' ) );
49
-
50
  add_action( 'woocommerce_admin_field_product_sync_google_product_categories', array( $this, 'render_google_product_category_field' ) );
51
  }
52
 
@@ -59,11 +55,9 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
59
  * @since 2.0.0
60
  */
61
  public function enqueue_assets() {
62
-
63
  if ( ! $this->is_current_screen_page() ) {
64
  return;
65
  }
66
-
67
  wp_enqueue_script( 'wc-backbone-modal', null, array( 'backbone' ) );
68
  wp_enqueue_script(
69
  'facebook-for-woocommerce-modal',
@@ -117,7 +111,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
117
  * @return string
118
  */
119
  private function get_default_google_product_category_modal_message() {
120
-
121
  return wp_kses_post( __( 'Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?', 'facebook-for-woocommerce' ) );
122
  }
123
 
@@ -130,7 +123,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
130
  * @return string
131
  */
132
  private function get_default_google_product_category_modal_message_empty() {
133
-
134
  return sprintf(
135
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
136
  esc_html__( 'Products and categories that inherit this global setting (they do not have a specific Google product category set) will use the new default immediately. %1$sIf you have cleared the Google Product Category%2$s, items inheriting the default will not be available for Instagram checkout. Are you sure you want to proceed?', 'facebook-for-woocommerce' ),
@@ -148,9 +140,7 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
148
  * @return string
149
  */
150
  private function get_default_google_product_category_modal_buttons() {
151
-
152
  ob_start();
153
-
154
  ?>
155
  <button
156
  class="button button-large"
@@ -203,18 +193,13 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
203
  * @since 2.0.0
204
  */
205
  public function save() {
206
-
207
  $integration = facebook_for_woocommerce()->get_integration();
208
-
209
  $previous_product_cat_ids = $integration->get_excluded_product_category_ids();
210
  $previous_product_tag_ids = $integration->get_excluded_product_tag_ids();
211
-
212
  parent::save();
213
-
214
  // when settings are saved, if there are new excluded categories/terms we should exclude corresponding products from sync
215
  $new_product_cat_ids = array_diff( $integration->get_excluded_product_category_ids(), $previous_product_cat_ids );
216
  $new_product_tag_ids = array_diff( $integration->get_excluded_product_tag_ids(), $previous_product_tag_ids );
217
-
218
  $this->disable_sync_for_excluded_products( $new_product_cat_ids, $new_product_tag_ids );
219
  }
220
 
@@ -228,7 +213,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
228
  * @param array $product_tag_ids IDs of excluded tags
229
  */
230
  private function disable_sync_for_excluded_products( $product_cat_ids, $product_tag_ids ) {
231
-
232
  // disable sync for all products belonging to excluded categories
233
  Products::disable_sync_for_products_with_terms(
234
  array(
@@ -236,7 +220,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
236
  'include' => $product_cat_ids,
237
  )
238
  );
239
-
240
  // disable sync for all products belonging to excluded tags
241
  Products::disable_sync_for_products_with_terms(
242
  array(
@@ -255,7 +238,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
255
  * @return array
256
  */
257
  public function get_settings() {
258
-
259
  $term_query = new \WP_Term_Query(
260
  array(
261
  'taxonomy' => 'product_cat',
@@ -263,9 +245,7 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
263
  'fields' => 'id=>name',
264
  )
265
  );
266
-
267
  $product_categories = $term_query->get_terms();
268
-
269
  $term_query = new \WP_Term_Query(
270
  array(
271
  'taxonomy' => 'product_tag',
@@ -274,16 +254,12 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
274
  'fields' => 'id=>name',
275
  )
276
  );
277
-
278
  $product_tags = $term_query->get_terms();
279
-
280
  return array(
281
-
282
  array(
283
  'type' => 'product_sync_title',
284
  'title' => __( 'Product sync', 'facebook-for-woocommerce' ),
285
  ),
286
-
287
  array(
288
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
289
  'title' => __( 'Enable product sync', 'facebook-for-woocommerce' ),
@@ -333,7 +309,7 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
333
  ),
334
  ),
335
  array(
336
- 'id' => \SkyVerge\WooCommerce\Facebook\Commerce::OPTION_GOOGLE_PRODUCT_CATEGORY_ID,
337
  'type' => 'product_sync_google_product_categories',
338
  'title' => __( 'Default Google product category', 'facebook-for-woocommerce' ),
339
  'desc_tip' => __( 'Choose a default Google product category for your products. Defaults can also be set for product categories. Products need at least two category levels defined for tax to be correctly applied.', 'facebook-for-woocommerce' ),
@@ -353,9 +329,7 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
353
  * @param array $field field data
354
  */
355
  public function render_google_product_category_field( $field ) {
356
-
357
- $category_field = new Admin\Google_Product_Category_Field();
358
-
359
  ?>
360
  <tr valign="top">
361
  <th scope="row" class="titledesc">
@@ -379,7 +353,6 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
379
  * @return string
380
  */
381
  public function get_disconnected_message() {
382
-
383
  return sprintf(
384
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
385
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage product sync.', 'facebook-for-woocommerce' ),
@@ -387,6 +360,4 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
387
  '</a>'
388
  );
389
  }
390
-
391
-
392
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Admin\Settings_Screens;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Admin\Abstract_Settings_Screen;
17
+ use WooCommerce\Facebook\Admin\Google_Product_Category_Field;
18
+ use WooCommerce\Facebook\Commerce;
19
+ use WooCommerce\Facebook\Products;
20
+ use WooCommerce\Facebook\Products\Sync;
21
 
22
  /**
23
  * The Messenger settings screen object.
24
  */
25
+ class Product_Sync extends Abstract_Settings_Screen {
 
26
 
27
  /** @var string screen ID */
28
  const ID = 'product_sync';
38
  * Connection constructor.
39
  */
40
  public function __construct() {
 
41
  $this->id = self::ID;
42
  $this->label = __( 'Product sync', 'facebook-for-woocommerce' );
43
  $this->title = __( 'Product sync', 'facebook-for-woocommerce' );
 
44
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
 
45
  add_action( 'woocommerce_admin_field_product_sync_title', array( $this, 'render_title' ) );
 
46
  add_action( 'woocommerce_admin_field_product_sync_google_product_categories', array( $this, 'render_google_product_category_field' ) );
47
  }
48
 
55
  * @since 2.0.0
56
  */
57
  public function enqueue_assets() {
 
58
  if ( ! $this->is_current_screen_page() ) {
59
  return;
60
  }
 
61
  wp_enqueue_script( 'wc-backbone-modal', null, array( 'backbone' ) );
62
  wp_enqueue_script(
63
  'facebook-for-woocommerce-modal',
111
  * @return string
112
  */
113
  private function get_default_google_product_category_modal_message() {
 
114
  return wp_kses_post( __( 'Products and categories that inherit this global setting (i.e. they do not have a specific Google product category set) will use the new default immediately. Are you sure you want to proceed?', 'facebook-for-woocommerce' ) );
115
  }
116
 
123
  * @return string
124
  */
125
  private function get_default_google_product_category_modal_message_empty() {
 
126
  return sprintf(
127
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
128
  esc_html__( 'Products and categories that inherit this global setting (they do not have a specific Google product category set) will use the new default immediately. %1$sIf you have cleared the Google Product Category%2$s, items inheriting the default will not be available for Instagram checkout. Are you sure you want to proceed?', 'facebook-for-woocommerce' ),
140
  * @return string
141
  */
142
  private function get_default_google_product_category_modal_buttons() {
 
143
  ob_start();
 
144
  ?>
145
  <button
146
  class="button button-large"
193
  * @since 2.0.0
194
  */
195
  public function save() {
 
196
  $integration = facebook_for_woocommerce()->get_integration();
 
197
  $previous_product_cat_ids = $integration->get_excluded_product_category_ids();
198
  $previous_product_tag_ids = $integration->get_excluded_product_tag_ids();
 
199
  parent::save();
 
200
  // when settings are saved, if there are new excluded categories/terms we should exclude corresponding products from sync
201
  $new_product_cat_ids = array_diff( $integration->get_excluded_product_category_ids(), $previous_product_cat_ids );
202
  $new_product_tag_ids = array_diff( $integration->get_excluded_product_tag_ids(), $previous_product_tag_ids );
 
203
  $this->disable_sync_for_excluded_products( $new_product_cat_ids, $new_product_tag_ids );
204
  }
205
 
213
  * @param array $product_tag_ids IDs of excluded tags
214
  */
215
  private function disable_sync_for_excluded_products( $product_cat_ids, $product_tag_ids ) {
 
216
  // disable sync for all products belonging to excluded categories
217
  Products::disable_sync_for_products_with_terms(
218
  array(
220
  'include' => $product_cat_ids,
221
  )
222
  );
 
223
  // disable sync for all products belonging to excluded tags
224
  Products::disable_sync_for_products_with_terms(
225
  array(
238
  * @return array
239
  */
240
  public function get_settings() {
 
241
  $term_query = new \WP_Term_Query(
242
  array(
243
  'taxonomy' => 'product_cat',
245
  'fields' => 'id=>name',
246
  )
247
  );
 
248
  $product_categories = $term_query->get_terms();
 
249
  $term_query = new \WP_Term_Query(
250
  array(
251
  'taxonomy' => 'product_tag',
254
  'fields' => 'id=>name',
255
  )
256
  );
 
257
  $product_tags = $term_query->get_terms();
 
258
  return array(
 
259
  array(
260
  'type' => 'product_sync_title',
261
  'title' => __( 'Product sync', 'facebook-for-woocommerce' ),
262
  ),
 
263
  array(
264
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
265
  'title' => __( 'Enable product sync', 'facebook-for-woocommerce' ),
309
  ),
310
  ),
311
  array(
312
+ 'id' => Commerce::OPTION_GOOGLE_PRODUCT_CATEGORY_ID,
313
  'type' => 'product_sync_google_product_categories',
314
  'title' => __( 'Default Google product category', 'facebook-for-woocommerce' ),
315
  'desc_tip' => __( 'Choose a default Google product category for your products. Defaults can also be set for product categories. Products need at least two category levels defined for tax to be correctly applied.', 'facebook-for-woocommerce' ),
329
  * @param array $field field data
330
  */
331
  public function render_google_product_category_field( $field ) {
332
+ $category_field = new Google_Product_Category_Field();
 
 
333
  ?>
334
  <tr valign="top">
335
  <th scope="row" class="titledesc">
353
  * @return string
354
  */
355
  public function get_disconnected_message() {
 
356
  return sprintf(
357
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
358
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage product sync.', 'facebook-for-woocommerce' ),
360
  '</a>'
361
  );
362
  }
 
 
363
  }
includes/Admin/Tasks/Setup.php CHANGED
@@ -7,7 +7,7 @@
7
  * @package FacebookCommerce
8
  */
9
 
10
- namespace SkyVerge\WooCommerce\Facebook\Admin\Tasks;
11
 
12
  defined( 'ABSPATH' ) || exit;
13
 
7
  * @package FacebookCommerce
8
  */
9
 
10
+ namespace WooCommerce\Facebook\Admin\Tasks;
11
 
12
  defined( 'ABSPATH' ) || exit;
13
 
includes/Commerce.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
@@ -35,9 +35,6 @@ class Commerce {
35
  * @since 2.1.0
36
  */
37
  public function __construct() {
38
-
39
- require_once __DIR__ . '/Commerce/Orders.php';
40
-
41
  $this->orders = new Commerce\Orders();
42
  }
43
 
@@ -134,7 +131,7 @@ class Commerce {
134
  *
135
  * @since 2.1.0
136
  *
137
- * @return \SkyVerge\WooCommerce\Facebook\Commerce\Orders
138
  */
139
  public function get_orders_handler() {
140
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
35
  * @since 2.1.0
36
  */
37
  public function __construct() {
 
 
 
38
  $this->orders = new Commerce\Orders();
39
  }
40
 
131
  *
132
  * @since 2.1.0
133
  *
134
+ * @return \WooCommerce\Facebook\Commerce\Orders
135
  */
136
  public function get_orders_handler() {
137
 
includes/Commerce/Orders.php CHANGED
@@ -9,15 +9,13 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Commerce;
13
 
14
- use SkyVerge\WooCommerce\Facebook\API\Orders\Cancel\Request as Cancellation_Request;
15
- use SkyVerge\WooCommerce\Facebook\API\Orders\Order;
16
- use SkyVerge\WooCommerce\Facebook\Products;
17
- use SkyVerge\WooCommerce\Facebook\API\Orders\Refund\Request as Refund_Request;
18
- use SkyVerge\WooCommerce\Facebook\Utilities;
19
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_API_Exception;
20
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Plugin_Exception;
21
 
22
  defined( 'ABSPATH' ) or exit;
23
 
@@ -78,7 +76,6 @@ class Orders {
78
  * @since 2.1.0
79
  */
80
  public function __construct() {
81
-
82
  $this->add_hooks();
83
  }
84
 
@@ -92,7 +89,6 @@ class Orders {
92
  * @return bool
93
  */
94
  public static function is_order_pending( \WC_Order $order ) {
95
-
96
  return self::is_commerce_order( $order ) && 'pending' === $order->get_status();
97
  }
98
 
@@ -106,7 +102,6 @@ class Orders {
106
  * @return bool
107
  */
108
  public static function is_commerce_order( \WC_Order $order ) {
109
-
110
  return in_array( $order->get_created_via(), array( 'instagram', 'facebook' ), true );
111
  }
112
 
@@ -141,7 +136,7 @@ class Orders {
141
  *
142
  * @param Order $remote_order Orders API order object
143
  * @return \WC_Order
144
- * @throws SV_WC_Plugin_Exception|\WC_Data_Exception
145
  */
146
  public function create_local_order( Order $remote_order ) {
147
 
@@ -164,7 +159,7 @@ class Orders {
164
  * @param Order $remote_order Orders API order object
165
  * @param \WC_Order $local_order local order object
166
  * @return \WC_Order
167
- * @throws SV_WC_Plugin_Exception|\WC_Data_Exception
168
  */
169
  public function update_local_order( Order $remote_order, \WC_Order $local_order ) {
170
 
@@ -386,7 +381,7 @@ class Orders {
386
 
387
  $response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_new_orders( $page_id );
388
 
389
- } catch ( SV_WC_API_Exception $exception ) {
390
 
391
  facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
392
 
@@ -436,7 +431,7 @@ class Orders {
436
  )
437
  );
438
 
439
- } catch ( SV_WC_API_Exception $exception ) {
440
 
441
  $local_order->add_order_note( 'Error acknowledging the order: ' . $exception->getMessage() );
442
 
@@ -468,7 +463,7 @@ class Orders {
468
 
469
  $response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_cancelled_orders( $page_id );
470
 
471
- } catch ( SV_WC_API_Exception $exception ) {
472
 
473
  facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
474
 
@@ -573,7 +568,7 @@ class Orders {
573
  * @param \WC_Order $order order object
574
  * @param string $tracking_number shipping tracking number
575
  * @param string $carrier shipping carrier
576
- * @throws SV_WC_Plugin_Exception
577
  */
578
  public function fulfill_order( \WC_Order $order, $tracking_number, $carrier ) {
579
 
@@ -582,14 +577,14 @@ class Orders {
582
  $remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
583
 
584
  if ( ! $remote_id ) {
585
- throw new SV_WC_Plugin_Exception( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
586
  }
587
 
588
  $shipment_utilities = new Utilities\Shipment();
589
 
590
  if ( ! $shipment_utilities->is_valid_carrier( $carrier ) ) {
591
  /** translators: Placeholders: %s - shipping carrier code */
592
- throw new SV_WC_Plugin_Exception( sprintf( __( '%s is not a valid shipping carrier code.', 'facebook-for-woocommerce' ), $carrier ) );
593
  }
594
 
595
  $items = array();
@@ -607,7 +602,7 @@ class Orders {
607
  }
608
 
609
  if ( empty( $items ) ) {
610
- throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
611
  }
612
 
613
  $fulfillment_data = array(
@@ -630,7 +625,7 @@ class Orders {
630
  )
631
  );
632
 
633
- } catch ( SV_WC_Plugin_Exception $exception ) {
634
 
635
  $order->add_order_note(
636
  sprintf(
@@ -653,7 +648,7 @@ class Orders {
653
  *
654
  * @param \WC_Order_Refund $refund order refund object
655
  * @param string $reason_code refund reason code
656
- * @throws SV_WC_Plugin_Exception
657
  */
658
  public function add_order_refund( \WC_Order_Refund $refund, $reason_code ) {
659
 
@@ -679,13 +674,13 @@ class Orders {
679
  $parent_order = wc_get_order( $refund->get_parent_id() );
680
 
681
  if ( ! $parent_order instanceof \WC_Order ) {
682
- throw new SV_WC_Plugin_Exception( __( 'Parent order not found.', 'facebook-for-woocommerce' ) );
683
  }
684
 
685
  $remote_id = $parent_order->get_meta( self::REMOTE_ID_META_KEY );
686
 
687
  if ( ! $remote_id ) {
688
- throw new SV_WC_Plugin_Exception( __( 'Remote ID for parent order not found.', 'facebook-for-woocommerce' ) );
689
  }
690
 
691
  $refund_data = array(
@@ -721,7 +716,7 @@ class Orders {
721
  )
722
  );
723
 
724
- } catch ( SV_WC_Plugin_Exception $exception ) {
725
 
726
  if ( ! empty( $parent_order ) && $parent_order instanceof \WC_Order ) {
727
 
@@ -752,7 +747,7 @@ class Orders {
752
  *
753
  * @param \WC_Order_Refund $refund refund object
754
  * @return array
755
- * @throws SV_WC_Plugin_Exception
756
  */
757
  private function get_refund_items( \WC_Order_Refund $refund ) {
758
 
@@ -784,7 +779,7 @@ class Orders {
784
  }
785
 
786
  if ( empty( $items ) ) {
787
- throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
788
  }
789
 
790
  return $items;
@@ -798,7 +793,7 @@ class Orders {
798
  *
799
  * @param \WC_Order $order order object
800
  * @param string $reason_code cancellation reason code
801
- * @throws SV_WC_Plugin_Exception
802
  */
803
  public function cancel_order( \WC_Order $order, $reason_code ) {
804
 
@@ -817,7 +812,7 @@ class Orders {
817
  $remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
818
 
819
  if ( ! $remote_id ) {
820
- throw new SV_WC_Plugin_Exception( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
821
  }
822
 
823
  $api->cancel_order( $remote_id, $reason_code );
@@ -830,7 +825,7 @@ class Orders {
830
  )
831
  );
832
 
833
- } catch ( SV_WC_Plugin_Exception $exception ) {
834
 
835
  $order->add_order_note(
836
  sprintf(
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Commerce;
13
 
14
+ use WooCommerce\Facebook\API\Orders\Order;
15
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
16
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
17
+ use WooCommerce\Facebook\Products;
18
+ use WooCommerce\Facebook\Utilities;
 
 
19
 
20
  defined( 'ABSPATH' ) or exit;
21
 
76
  * @since 2.1.0
77
  */
78
  public function __construct() {
 
79
  $this->add_hooks();
80
  }
81
 
89
  * @return bool
90
  */
91
  public static function is_order_pending( \WC_Order $order ) {
 
92
  return self::is_commerce_order( $order ) && 'pending' === $order->get_status();
93
  }
94
 
102
  * @return bool
103
  */
104
  public static function is_commerce_order( \WC_Order $order ) {
 
105
  return in_array( $order->get_created_via(), array( 'instagram', 'facebook' ), true );
106
  }
107
 
136
  *
137
  * @param Order $remote_order Orders API order object
138
  * @return \WC_Order
139
+ * @throws PluginException|\WC_Data_Exception
140
  */
141
  public function create_local_order( Order $remote_order ) {
142
 
159
  * @param Order $remote_order Orders API order object
160
  * @param \WC_Order $local_order local order object
161
  * @return \WC_Order
162
+ * @throws PluginException|\WC_Data_Exception
163
  */
164
  public function update_local_order( Order $remote_order, \WC_Order $local_order ) {
165
 
381
 
382
  $response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_new_orders( $page_id );
383
 
384
+ } catch ( ApiException $exception ) {
385
 
386
  facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
387
 
431
  )
432
  );
433
 
434
+ } catch ( ApiException $exception ) {
435
 
436
  $local_order->add_order_note( 'Error acknowledging the order: ' . $exception->getMessage() );
437
 
463
 
464
  $response = facebook_for_woocommerce()->get_api( facebook_for_woocommerce()->get_connection_handler()->get_page_access_token() )->get_cancelled_orders( $page_id );
465
 
466
+ } catch ( ApiException $exception ) {
467
 
468
  facebook_for_woocommerce()->log( 'Error fetching Commerce orders from the Orders API: ' . $exception->getMessage() );
469
 
568
  * @param \WC_Order $order order object
569
  * @param string $tracking_number shipping tracking number
570
  * @param string $carrier shipping carrier
571
+ * @throws PluginException
572
  */
573
  public function fulfill_order( \WC_Order $order, $tracking_number, $carrier ) {
574
 
577
  $remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
578
 
579
  if ( ! $remote_id ) {
580
+ throw new PluginException( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
581
  }
582
 
583
  $shipment_utilities = new Utilities\Shipment();
584
 
585
  if ( ! $shipment_utilities->is_valid_carrier( $carrier ) ) {
586
  /** translators: Placeholders: %s - shipping carrier code */
587
+ throw new PluginException( sprintf( __( '%s is not a valid shipping carrier code.', 'facebook-for-woocommerce' ), $carrier ) );
588
  }
589
 
590
  $items = array();
602
  }
603
 
604
  if ( empty( $items ) ) {
605
+ throw new PluginException( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
606
  }
607
 
608
  $fulfillment_data = array(
625
  )
626
  );
627
 
628
+ } catch ( PluginException $exception ) {
629
 
630
  $order->add_order_note(
631
  sprintf(
648
  *
649
  * @param \WC_Order_Refund $refund order refund object
650
  * @param string $reason_code refund reason code
651
+ * @throws PluginException
652
  */
653
  public function add_order_refund( \WC_Order_Refund $refund, $reason_code ) {
654
 
674
  $parent_order = wc_get_order( $refund->get_parent_id() );
675
 
676
  if ( ! $parent_order instanceof \WC_Order ) {
677
+ throw new PluginException( __( 'Parent order not found.', 'facebook-for-woocommerce' ) );
678
  }
679
 
680
  $remote_id = $parent_order->get_meta( self::REMOTE_ID_META_KEY );
681
 
682
  if ( ! $remote_id ) {
683
+ throw new PluginException( __( 'Remote ID for parent order not found.', 'facebook-for-woocommerce' ) );
684
  }
685
 
686
  $refund_data = array(
716
  )
717
  );
718
 
719
+ } catch ( PluginException $exception ) {
720
 
721
  if ( ! empty( $parent_order ) && $parent_order instanceof \WC_Order ) {
722
 
747
  *
748
  * @param \WC_Order_Refund $refund refund object
749
  * @return array
750
+ * @throws PluginException
751
  */
752
  private function get_refund_items( \WC_Order_Refund $refund ) {
753
 
779
  }
780
 
781
  if ( empty( $items ) ) {
782
+ throw new PluginException( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
783
  }
784
 
785
  return $items;
793
  *
794
  * @param \WC_Order $order order object
795
  * @param string $reason_code cancellation reason code
796
+ * @throws PluginException
797
  */
798
  public function cancel_order( \WC_Order $order, $reason_code ) {
799
 
812
  $remote_id = $order->get_meta( self::REMOTE_ID_META_KEY );
813
 
814
  if ( ! $remote_id ) {
815
+ throw new PluginException( __( 'Remote ID not found.', 'facebook-for-woocommerce' ) );
816
  }
817
 
818
  $api->cancel_order( $remote_id, $reason_code );
825
  )
826
  );
827
 
828
+ } catch ( PluginException $exception ) {
829
 
830
  $order->add_order_note(
831
  sprintf(
includes/Debug/ProfilingLogger.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Debug;
5
 
6
  defined( 'ABSPATH' ) || exit;
7
 
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Debug;
5
 
6
  defined( 'ABSPATH' ) || exit;
7
 
includes/Debug/ProfilingLoggerProcess.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Debug;
5
 
6
  defined( 'ABSPATH' ) || exit;
7
 
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Debug;
5
 
6
  defined( 'ABSPATH' ) || exit;
7
 
includes/Events/AAMSettings.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/Events/Event.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
@@ -39,7 +39,6 @@ class Event {
39
  * }
40
  */
41
  public static function get_version_info() {
42
-
43
  return array(
44
  'source' => 'woocommerce',
45
  'version' => WC()->version,
@@ -54,9 +53,7 @@ class Event {
54
  * @return string
55
  */
56
  public static function get_platform_identifier() {
57
-
58
  $info = self::get_version_info();
59
-
60
  return "{$info['source']}-{$info['version']}-{$info['pluginVersion']}";
61
  }
62
 
@@ -71,7 +68,6 @@ class Event {
71
  * @param array $data event data
72
  */
73
  public function __construct( $data = array() ) {
74
-
75
  $this->prepare_data( $data );
76
  }
77
 
@@ -87,7 +83,6 @@ class Event {
87
  * @param array $data event data
88
  */
89
  protected function prepare_data( $data ) {
90
-
91
  $this->data = wp_parse_args(
92
  $data,
93
  array(
@@ -123,7 +118,6 @@ class Event {
123
  'browser_id' => $this->get_browser_id(),
124
  )
125
  );
126
-
127
  // Country key is not the same in pixel and CAPI events, see:
128
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
129
  // https://developers.facebook.com/docs/marketing-api/conversions-api/parameters
@@ -132,9 +126,7 @@ class Event {
132
  $this->data['user_data']['country'] = $country;
133
  unset( $this->data['user_data']['cn'] );
134
  }
135
-
136
  $this->data['user_data'] = Normalizer::normalize_array( $this->data['user_data'], false );
137
-
138
  $this->data['user_data'] = $this->hash_pii_data( $this->data['user_data'] );
139
  }
140
 
@@ -169,17 +161,12 @@ class Event {
169
  * @return string
170
  */
171
  protected function generate_event_id() {
172
-
173
  try {
174
  $data = random_bytes( 16 );
175
-
176
  $data[6] = chr( ord( $data[6] ) & 0x0f | 0x40 ); // set version to 0100
177
  $data[8] = chr( ord( $data[8] ) & 0x3f | 0x80 ); // set bits 6-7 to 10
178
-
179
  return vsprintf( '%s%s-%s-%s-%s-%s%s%s', str_split( bin2hex( $data ), 4 ) );
180
-
181
  } catch ( \Exception $e ) {
182
-
183
  // fall back to mt_rand if random_bytes is unavailable
184
  return sprintf(
185
  '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
@@ -212,13 +199,9 @@ class Event {
212
  * @return string
213
  */
214
  protected function get_current_url() {
215
-
216
  if ( wp_doing_ajax() ) {
217
-
218
  $url = wp_get_raw_referer();
219
-
220
  } else {
221
-
222
  /**
223
  * Instead of relying on the HTTP_HOST server var, we use home_url(),
224
  * so that we get the host configured in site options.
@@ -227,7 +210,6 @@ class Event {
227
  */
228
  $url = home_url() . $_SERVER['REQUEST_URI'];
229
  }
230
-
231
  return $url;
232
  }
233
 
@@ -240,7 +222,6 @@ class Event {
240
  * @return string
241
  */
242
  protected function get_client_ip() {
243
-
244
  return \WC_Geolocation::get_ip_address();
245
  }
246
 
@@ -253,7 +234,6 @@ class Event {
253
  * @return string
254
  */
255
  protected function get_client_user_agent() {
256
-
257
  return ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
258
  }
259
 
@@ -268,24 +248,17 @@ class Event {
268
  * @return string
269
  */
270
  protected function get_click_id() {
271
-
272
  $click_id = '';
273
-
274
  if ( ! empty( $_COOKIE['_fbc'] ) ) {
275
-
276
  $click_id = $_COOKIE['_fbc'];
277
-
278
  } elseif ( ! empty( $_REQUEST['fbclid'] ) ) {
279
-
280
  // generate the click ID based on the query parameter
281
  $version = 'fb';
282
  $subdomain_index = 1;
283
  $creation_time = time();
284
  $fbclid = $_REQUEST['fbclid'];
285
-
286
- $click_id = "{$version}.{$subdomain_index}.{$creation_time}.{$fbclid}";
287
  }
288
-
289
  return $click_id;
290
  }
291
 
@@ -298,7 +271,6 @@ class Event {
298
  * @return string
299
  */
300
  protected function get_browser_id() {
301
-
302
  return ! empty( $_COOKIE['_fbp'] ) ? $_COOKIE['_fbp'] : '';
303
  }
304
 
@@ -311,7 +283,6 @@ class Event {
311
  * @return array
312
  */
313
  public function get_data() {
314
-
315
  return $this->data;
316
  }
317
 
@@ -324,7 +295,6 @@ class Event {
324
  * @return string
325
  */
326
  public function get_id() {
327
-
328
  return ! empty( $this->data['event_id'] ) ? $this->data['event_id'] : '';
329
  }
330
 
@@ -337,7 +307,6 @@ class Event {
337
  * @return string
338
  */
339
  public function get_name() {
340
-
341
  return ! empty( $this->data['event_name'] ) ? $this->data['event_name'] : '';
342
  }
343
 
@@ -350,7 +319,6 @@ class Event {
350
  * @return array
351
  */
352
  public function get_user_data() {
353
-
354
  return ! empty( $this->data['user_data'] ) ? $this->data['user_data'] : array();
355
  }
356
 
@@ -363,8 +331,6 @@ class Event {
363
  * @return array
364
  */
365
  public function get_custom_data() {
366
-
367
  return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : array();
368
  }
369
-
370
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Events;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
39
  * }
40
  */
41
  public static function get_version_info() {
 
42
  return array(
43
  'source' => 'woocommerce',
44
  'version' => WC()->version,
53
  * @return string
54
  */
55
  public static function get_platform_identifier() {
 
56
  $info = self::get_version_info();
 
57
  return "{$info['source']}-{$info['version']}-{$info['pluginVersion']}";
58
  }
59
 
68
  * @param array $data event data
69
  */
70
  public function __construct( $data = array() ) {
 
71
  $this->prepare_data( $data );
72
  }
73
 
83
  * @param array $data event data
84
  */
85
  protected function prepare_data( $data ) {
 
86
  $this->data = wp_parse_args(
87
  $data,
88
  array(
118
  'browser_id' => $this->get_browser_id(),
119
  )
120
  );
 
121
  // Country key is not the same in pixel and CAPI events, see:
122
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
123
  // https://developers.facebook.com/docs/marketing-api/conversions-api/parameters
126
  $this->data['user_data']['country'] = $country;
127
  unset( $this->data['user_data']['cn'] );
128
  }
 
129
  $this->data['user_data'] = Normalizer::normalize_array( $this->data['user_data'], false );
 
130
  $this->data['user_data'] = $this->hash_pii_data( $this->data['user_data'] );
131
  }
132
 
161
  * @return string
162
  */
163
  protected function generate_event_id() {
 
164
  try {
165
  $data = random_bytes( 16 );
 
166
  $data[6] = chr( ord( $data[6] ) & 0x0f | 0x40 ); // set version to 0100
167
  $data[8] = chr( ord( $data[8] ) & 0x3f | 0x80 ); // set bits 6-7 to 10
 
168
  return vsprintf( '%s%s-%s-%s-%s-%s%s%s', str_split( bin2hex( $data ), 4 ) );
 
169
  } catch ( \Exception $e ) {
 
170
  // fall back to mt_rand if random_bytes is unavailable
171
  return sprintf(
172
  '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
199
  * @return string
200
  */
201
  protected function get_current_url() {
 
202
  if ( wp_doing_ajax() ) {
 
203
  $url = wp_get_raw_referer();
 
204
  } else {
 
205
  /**
206
  * Instead of relying on the HTTP_HOST server var, we use home_url(),
207
  * so that we get the host configured in site options.
210
  */
211
  $url = home_url() . $_SERVER['REQUEST_URI'];
212
  }
 
213
  return $url;
214
  }
215
 
222
  * @return string
223
  */
224
  protected function get_client_ip() {
 
225
  return \WC_Geolocation::get_ip_address();
226
  }
227
 
234
  * @return string
235
  */
236
  protected function get_client_user_agent() {
 
237
  return ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
238
  }
239
 
248
  * @return string
249
  */
250
  protected function get_click_id() {
 
251
  $click_id = '';
 
252
  if ( ! empty( $_COOKIE['_fbc'] ) ) {
 
253
  $click_id = $_COOKIE['_fbc'];
 
254
  } elseif ( ! empty( $_REQUEST['fbclid'] ) ) {
 
255
  // generate the click ID based on the query parameter
256
  $version = 'fb';
257
  $subdomain_index = 1;
258
  $creation_time = time();
259
  $fbclid = $_REQUEST['fbclid'];
260
+ $click_id = "{$version}.{$subdomain_index}.{$creation_time}.{$fbclid}";
 
261
  }
 
262
  return $click_id;
263
  }
264
 
271
  * @return string
272
  */
273
  protected function get_browser_id() {
 
274
  return ! empty( $_COOKIE['_fbp'] ) ? $_COOKIE['_fbp'] : '';
275
  }
276
 
283
  * @return array
284
  */
285
  public function get_data() {
 
286
  return $this->data;
287
  }
288
 
295
  * @return string
296
  */
297
  public function get_id() {
 
298
  return ! empty( $this->data['event_id'] ) ? $this->data['event_id'] : '';
299
  }
300
 
307
  * @return string
308
  */
309
  public function get_name() {
 
310
  return ! empty( $this->data['event_name'] ) ? $this->data['event_name'] : '';
311
  }
312
 
319
  * @return array
320
  */
321
  public function get_user_data() {
 
322
  return ! empty( $this->data['user_data'] ) ? $this->data['user_data'] : array();
323
  }
324
 
331
  * @return array
332
  */
333
  public function get_custom_data() {
 
334
  return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : array();
335
  }
 
336
  }
includes/Events/Normalizer.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Events;
13
 
14
  use InvalidArgumentException;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Events;
13
 
14
  use InvalidArgumentException;
15
 
includes/Exceptions/ConnectWCAPIException.php DELETED
@@ -1,15 +0,0 @@
1
- <?php
2
- // phpcs:ignoreFile
3
-
4
- namespace SkyVerge\WooCommerce\Facebook\API\Exceptions;
5
-
6
- defined( 'ABSPATH' ) or exit;
7
-
8
- /**
9
- * Class Connect_WC_API_Exception.
10
- * Exception is trown when Connection with FB fails. @see \SkyVerge\WooCommerce\Facebook\Handlers\Connection
11
- *
12
- * @package SkyVerge\WooCommerce\Facebook\API\Exceptions
13
- */
14
- class Connect_WC_API_Exception extends \Exception {
15
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/Feed/FeedConfigurationDetection.php CHANGED
@@ -1,14 +1,13 @@
1
  <?php
 
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\Feed;
4
 
5
- if ( ! defined( 'ABSPATH' ) ) {
6
- exit;
7
- }
8
 
9
  use Error;
10
- use SkyVerge\WooCommerce\Facebook\Utilities\Heartbeat;
11
- use SkyVerge\WooCommerce\Facebook\Products\Feed;
12
 
13
  /**
14
  * A class responsible detecting feed configuration.
@@ -45,11 +44,10 @@ class FeedConfigurationDetection {
45
  * Get config settings for feed-based sync for WooCommerce Tracker.
46
  *
47
  * @throws Error Catalog id missing.
48
- * @return Array Key-value array of various configuration settings.
49
  */
50
  private function get_data_source_feed_tracker_info() {
51
  $integration = facebook_for_woocommerce()->get_integration();
52
- $graph_api = $integration->get_graph_api();
53
  $integration_feed_id = $integration->get_feed_id();
54
  $catalog_id = $integration->get_product_catalog_id();
55
 
@@ -62,7 +60,7 @@ class FeedConfigurationDetection {
62
  }
63
 
64
  // Get all feeds configured for the catalog.
65
- $feed_nodes = $this->get_feed_nodes_for_catalog( $catalog_id, $graph_api );
66
 
67
  $info['feed-count'] = count( $feed_nodes );
68
 
@@ -80,7 +78,7 @@ class FeedConfigurationDetection {
80
  */
81
  $active_feed_metadata = array();
82
  foreach ( $feed_nodes as $feed ) {
83
- $metadata = $this->get_feed_metadata( $feed['id'], $graph_api );
84
 
85
  if ( $feed['id'] === $integration_feed_id ) {
86
  $active_feed_metadata = $metadata;
@@ -139,7 +137,7 @@ class FeedConfigurationDetection {
139
  }
140
 
141
  // Get more detailed metadata about the most recent feed upload.
142
- $upload_metadata = $this->get_feed_upload_metadata( $latest_upload['id'], $graph_api );
143
 
144
  $upload['error-count'] = $upload_metadata['error_count'];
145
  $upload['warning-count'] = $upload_metadata['warning_count'];
@@ -163,41 +161,32 @@ class FeedConfigurationDetection {
163
  *
164
  * @throws Error Feed configurations fetch was not successful.
165
  * @param String $catalog_id Facebook Catalog ID.
166
- * @param WC_Facebookcommerce_Graph_API $graph_api Facebook Graph handler instance.
167
  *
168
- * @return Array Array of feed configurations.
169
  */
170
- private function get_feed_nodes_for_catalog( $catalog_id, $graph_api ) {
171
- // Read all the feed configurations specified for the catalog.
172
- $response = $graph_api->read_feeds( $catalog_id );
173
- $code = (int) wp_remote_retrieve_response_code( $response );
174
- if ( 200 !== $code ) {
175
- throw new Error( 'Reading catalog feeds error', $code );
176
- }
177
-
178
- $response_body = wp_remote_retrieve_body( $response );
179
 
180
- $body = json_decode( $response_body, true );
181
- return $body['data'];
 
 
 
 
 
 
 
182
  }
183
 
184
  /**
185
  * Given feed id fetch this feed configuration metadata.
186
  *
187
- * @throws Error Feed metadata fetch was not successful.
188
- * @param String $feed_id Facebook Feed ID.
189
- * @param WC_Facebookcommerce_Graph_API $graph_api Facebook Graph handler instance.
190
- *
191
- * @return Array Array of feed configurations.
192
  */
193
- private function get_feed_metadata( $feed_id, $graph_api ) {
194
- $response = $graph_api->read_feed_metadata( $feed_id );
195
- $code = (int) wp_remote_retrieve_response_code( $response );
196
- if ( 200 !== $code ) {
197
- throw new Error( 'Error reading feed metadata', $code );
198
- }
199
- $response_body = wp_remote_retrieve_body( $response );
200
- return json_decode( $response_body, true );
201
  }
202
 
203
  /**
@@ -205,18 +194,11 @@ class FeedConfigurationDetection {
205
  *
206
  * @throws Error Upload metadata fetch was not successful.
207
  * @param String $upload_id Facebook Feed upload ID.
208
- * @param WC_Facebookcommerce_Graph_API $graph_api Facebook Graph handler instance.
209
- *
210
- * @return Array Array of feed configurations.
211
  */
212
- private function get_feed_upload_metadata( $upload_id, $graph_api ) {
213
- $response = $graph_api->read_upload_metadata( $upload_id );
214
- $code = (int) wp_remote_retrieve_response_code( $response );
215
- if ( 200 !== $code ) {
216
- throw new Error( 'Error reading feed upload metadata', $code );
217
- }
218
- $response_body = wp_remote_retrieve_body( $response );
219
- return json_decode( $response_body, true );
220
  }
221
 
222
  }
1
  <?php
2
+ // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Feed;
5
 
6
+ defined( 'ABSPATH' ) || exit;
 
 
7
 
8
  use Error;
9
+ use WooCommerce\Facebook\Products\Feed;
10
+ use WooCommerce\Facebook\Utilities\Heartbeat;
11
 
12
  /**
13
  * A class responsible detecting feed configuration.
44
  * Get config settings for feed-based sync for WooCommerce Tracker.
45
  *
46
  * @throws Error Catalog id missing.
47
+ * @return array Key-value array of various configuration settings.
48
  */
49
  private function get_data_source_feed_tracker_info() {
50
  $integration = facebook_for_woocommerce()->get_integration();
 
51
  $integration_feed_id = $integration->get_feed_id();
52
  $catalog_id = $integration->get_product_catalog_id();
53
 
60
  }
61
 
62
  // Get all feeds configured for the catalog.
63
+ $feed_nodes = $this->get_feed_nodes_for_catalog( $catalog_id );
64
 
65
  $info['feed-count'] = count( $feed_nodes );
66
 
78
  */
79
  $active_feed_metadata = array();
80
  foreach ( $feed_nodes as $feed ) {
81
+ $metadata = $this->get_feed_metadata( $feed['id'] );
82
 
83
  if ( $feed['id'] === $integration_feed_id ) {
84
  $active_feed_metadata = $metadata;
137
  }
138
 
139
  // Get more detailed metadata about the most recent feed upload.
140
+ $upload_metadata = $this->get_feed_upload_metadata( $latest_upload['id'] );
141
 
142
  $upload['error-count'] = $upload_metadata['error_count'];
143
  $upload['warning-count'] = $upload_metadata['warning_count'];
161
  *
162
  * @throws Error Feed configurations fetch was not successful.
163
  * @param String $catalog_id Facebook Catalog ID.
 
164
  *
165
+ * @return array Array of feed configurations.
166
  */
 
 
 
 
 
 
 
 
 
167
 
168
+ /**
169
+ * @param string $product_catalog_id
170
+ * @return array Facebook Product Feeds.
171
+ * @throws \WooCommerce\Facebook\API\Exceptions\Request_Limit_Reached
172
+ * @throws \WooCommerce\Facebook\Framework\Api\Exception
173
+ */
174
+ private function get_feed_nodes_for_catalog( string $product_catalog_id ) {
175
+ $response = facebook_for_woocommerce()->get_api()->read_feeds( $product_catalog_id );
176
+ return $response->data;
177
  }
178
 
179
  /**
180
  * Given feed id fetch this feed configuration metadata.
181
  *
182
+ * @param string $feed_id Facebook Product Feed ID.
183
+ * @return \WooCommerce\Facebook\API\Response
184
+ * @throws \WooCommerce\Facebook\API\Exceptions\Request_Limit_Reached
185
+ * @throws \WooCommerce\Facebook\Framework\Api\Exception
 
186
  */
187
+ private function get_feed_metadata( string $feed_id ) {
188
+ $response = facebook_for_woocommerce()->get_api()->read_feed( $feed_id );
189
+ return $response;
 
 
 
 
 
190
  }
191
 
192
  /**
194
  *
195
  * @throws Error Upload metadata fetch was not successful.
196
  * @param String $upload_id Facebook Feed upload ID.
197
+ * @return array Array of feed configurations.
 
 
198
  */
199
+ private function get_feed_upload_metadata( $upload_id ) {
200
+ $response = facebook_for_woocommerce()->get_api()->read_upload( $upload_id );
201
+ return $response;
 
 
 
 
 
202
  }
203
 
204
  }
vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wp-admin-message-handler.php → includes/Framework/AdminMessageHandler.php RENAMED
@@ -1,36 +1,15 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Admin Message Handler
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WordPress/WP-Admin-Message-Handler
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
26
 
27
  defined( 'ABSPATH' ) or exit;
28
 
29
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_Admin_Message_Handler' ) ) :
30
-
31
-
32
  /**
33
- * # WordPress Admin Message Handler Class
34
  *
35
  * This class provides a reusable wordpress admin messaging facility for setting
36
  * and displaying messages and error messages across admin page requests without
@@ -52,7 +31,7 @@ if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_A
52
  *
53
  * @version 1.0.1
54
  */
55
- class SV_WP_Admin_Message_Handler {
56
 
57
 
58
  /** transient message prefix */
@@ -66,16 +45,16 @@ class SV_WP_Admin_Message_Handler {
66
  private $message_id;
67
 
68
  /** @var array array of messages */
69
- private $messages = array();
70
 
71
  /** @var array array of error messages */
72
- private $errors = array();
73
 
74
  /** @var array array of warning messages */
75
- private $warnings = array();
76
 
77
  /** @var array array of info messages */
78
- private $infos = array();
79
 
80
 
81
  /**
@@ -86,12 +65,9 @@ class SV_WP_Admin_Message_Handler {
86
  * this to a unique identifier based on the client plugin, such as __FILE__
87
  */
88
  public function __construct( $message_id = null ) {
89
-
90
  $this->message_id = $message_id;
91
-
92
  // load any available messages
93
  $this->load_messages();
94
-
95
  add_filter( 'wp_redirect', array( $this, 'redirect' ), 1, 2 );
96
  }
97
 
@@ -103,10 +79,8 @@ class SV_WP_Admin_Message_Handler {
103
  * @return boolean true if any messages were set, false otherwise
104
  */
105
  public function set_messages() {
106
-
107
  // any messages to persist?
108
  if ( $this->message_count() > 0 || $this->info_count() > 0 || $this->warning_count() > 0 || $this->error_count() > 0 ) {
109
-
110
  set_transient(
111
  self::MESSAGE_TRANSIENT_PREFIX . $this->get_message_id(),
112
  array(
@@ -117,10 +91,8 @@ class SV_WP_Admin_Message_Handler {
117
  ),
118
  60 * 60
119
  );
120
-
121
  return true;
122
  }
123
-
124
  return false;
125
  }
126
 
@@ -131,16 +103,12 @@ class SV_WP_Admin_Message_Handler {
131
  * @since 1.0.0
132
  */
133
  public function load_messages() {
134
-
135
  if ( isset( $_GET[ self::MESSAGE_ID_GET_NAME ] ) && $this->get_message_id() == $_GET[ self::MESSAGE_ID_GET_NAME ] ) {
136
-
137
  $memo = get_transient( self::MESSAGE_TRANSIENT_PREFIX . $_GET[ self::MESSAGE_ID_GET_NAME ] );
138
-
139
  if ( isset( $memo['errors'] ) ) $this->errors = $memo['errors'];
140
  if ( isset( $memo['warnings'] ) ) $this->warnings = $memo['warnings'];
141
  if ( isset( $memo['infos'] ) ) $this->infos = $memo['infos'];
142
  if ( isset( $memo['messages'] ) ) $this->messages = $memo['messages'];
143
-
144
  $this->clear_messages( $_GET[ self::MESSAGE_ID_GET_NAME ] );
145
  }
146
  }
@@ -356,44 +324,34 @@ class SV_WP_Admin_Message_Handler {
356
  * default: `manage_woocommerce`
357
  * }
358
  */
359
- public function show_messages( $params = array() ) {
360
-
361
  $params = wp_parse_args( $params, array(
362
  'capabilities' => array(
363
  'manage_woocommerce',
364
  ),
365
  ) );
366
-
367
- $check_user_capabilities = array();
368
-
369
  // check if user has at least one capability that allows to see messages
370
  foreach ( $params['capabilities'] as $capability ) {
371
  $check_user_capabilities[] = current_user_can( $capability );
372
  }
373
-
374
  // bail out if user has no minimum capabilities to see messages
375
  if ( ! in_array( true, $check_user_capabilities, true ) ) {
376
  return;
377
  }
378
-
379
  $output = '';
380
-
381
  if ( $this->error_count() > 0 ) {
382
  $output .= '<div id="wp-admin-message-handler-error" class="notice-error notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_errors() ) . '</strong></li></ul></div>';
383
  }
384
-
385
  if ( $this->warning_count() > 0 ) {
386
  $output .= '<div id="wp-admin-message-handler-warning" class="notice-warning notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_warnings() ) . '</strong></li></ul></div>';
387
  }
388
-
389
  if ( $this->info_count() > 0 ) {
390
  $output .= '<div id="wp-admin-message-handler-warning" class="notice-info notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_infos() ) . '</strong></li></ul></div>';
391
  }
392
-
393
  if ( $this->message_count() > 0 ) {
394
  $output .= '<div id="wp-admin-message-handler-message" class="notice-success notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_messages() ) . '</strong></li></ul></div>';
395
  }
396
-
397
  echo wp_kses_post( $output );
398
  }
399
 
@@ -407,12 +365,10 @@ class SV_WP_Admin_Message_Handler {
407
  * @return string the URL to redirect to
408
  */
409
  public function redirect( $location, $status ) {
410
-
411
  // add the admin message id param to the
412
  if ( $this->set_messages() ) {
413
  $location = add_query_arg( self::MESSAGE_ID_GET_NAME, $this->get_message_id(), $location );
414
  }
415
-
416
  return $location;
417
  }
418
 
@@ -424,15 +380,7 @@ class SV_WP_Admin_Message_Handler {
424
  * @return string unique identifier
425
  */
426
  protected function get_message_id() {
427
-
428
  if ( ! isset( $this->message_id ) ) $this->message_id = __FILE__;
429
-
430
  return wp_create_nonce( $this->message_id );
431
-
432
  }
433
-
434
-
435
  }
436
-
437
-
438
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework;
8
 
9
  defined( 'ABSPATH' ) or exit;
10
 
 
 
 
11
  /**
12
+ * Admin Message Handler Class
13
  *
14
  * This class provides a reusable wordpress admin messaging facility for setting
15
  * and displaying messages and error messages across admin page requests without
31
  *
32
  * @version 1.0.1
33
  */
34
+ class AdminMessageHandler {
35
 
36
 
37
  /** transient message prefix */
45
  private $message_id;
46
 
47
  /** @var array array of messages */
48
+ private $messages = [];
49
 
50
  /** @var array array of error messages */
51
+ private $errors = [];
52
 
53
  /** @var array array of warning messages */
54
+ private $warnings = [];
55
 
56
  /** @var array array of info messages */
57
+ private $infos = [];
58
 
59
 
60
  /**
65
  * this to a unique identifier based on the client plugin, such as __FILE__
66
  */
67
  public function __construct( $message_id = null ) {
 
68
  $this->message_id = $message_id;
 
69
  // load any available messages
70
  $this->load_messages();
 
71
  add_filter( 'wp_redirect', array( $this, 'redirect' ), 1, 2 );
72
  }
73
 
79
  * @return boolean true if any messages were set, false otherwise
80
  */
81
  public function set_messages() {
 
82
  // any messages to persist?
83
  if ( $this->message_count() > 0 || $this->info_count() > 0 || $this->warning_count() > 0 || $this->error_count() > 0 ) {
 
84
  set_transient(
85
  self::MESSAGE_TRANSIENT_PREFIX . $this->get_message_id(),
86
  array(
91
  ),
92
  60 * 60
93
  );
 
94
  return true;
95
  }
 
96
  return false;
97
  }
98
 
103
  * @since 1.0.0
104
  */
105
  public function load_messages() {
 
106
  if ( isset( $_GET[ self::MESSAGE_ID_GET_NAME ] ) && $this->get_message_id() == $_GET[ self::MESSAGE_ID_GET_NAME ] ) {
 
107
  $memo = get_transient( self::MESSAGE_TRANSIENT_PREFIX . $_GET[ self::MESSAGE_ID_GET_NAME ] );
 
108
  if ( isset( $memo['errors'] ) ) $this->errors = $memo['errors'];
109
  if ( isset( $memo['warnings'] ) ) $this->warnings = $memo['warnings'];
110
  if ( isset( $memo['infos'] ) ) $this->infos = $memo['infos'];
111
  if ( isset( $memo['messages'] ) ) $this->messages = $memo['messages'];
 
112
  $this->clear_messages( $_GET[ self::MESSAGE_ID_GET_NAME ] );
113
  }
114
  }
324
  * default: `manage_woocommerce`
325
  * }
326
  */
327
+ public function show_messages( $params = [] ) {
 
328
  $params = wp_parse_args( $params, array(
329
  'capabilities' => array(
330
  'manage_woocommerce',
331
  ),
332
  ) );
333
+ $check_user_capabilities = [];
 
 
334
  // check if user has at least one capability that allows to see messages
335
  foreach ( $params['capabilities'] as $capability ) {
336
  $check_user_capabilities[] = current_user_can( $capability );
337
  }
 
338
  // bail out if user has no minimum capabilities to see messages
339
  if ( ! in_array( true, $check_user_capabilities, true ) ) {
340
  return;
341
  }
 
342
  $output = '';
 
343
  if ( $this->error_count() > 0 ) {
344
  $output .= '<div id="wp-admin-message-handler-error" class="notice-error notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_errors() ) . '</strong></li></ul></div>';
345
  }
 
346
  if ( $this->warning_count() > 0 ) {
347
  $output .= '<div id="wp-admin-message-handler-warning" class="notice-warning notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_warnings() ) . '</strong></li></ul></div>';
348
  }
 
349
  if ( $this->info_count() > 0 ) {
350
  $output .= '<div id="wp-admin-message-handler-warning" class="notice-info notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_infos() ) . '</strong></li></ul></div>';
351
  }
 
352
  if ( $this->message_count() > 0 ) {
353
  $output .= '<div id="wp-admin-message-handler-message" class="notice-success notice"><ul><li><strong>' . implode( '</strong></li><li><strong>', $this->get_messages() ) . '</strong></li></ul></div>';
354
  }
 
355
  echo wp_kses_post( $output );
356
  }
357
 
365
  * @return string the URL to redirect to
366
  */
367
  public function redirect( $location, $status ) {
 
368
  // add the admin message id param to the
369
  if ( $this->set_messages() ) {
370
  $location = add_query_arg( self::MESSAGE_ID_GET_NAME, $this->get_message_id(), $location );
371
  }
 
372
  return $location;
373
  }
374
 
380
  * @return string unique identifier
381
  */
382
  protected function get_message_id() {
 
383
  if ( ! isset( $this->message_id ) ) $this->message_id = __FILE__;
 
384
  return wp_create_nonce( $this->message_id );
 
385
  }
 
 
386
  }
 
 
 
vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-admin-notice-handler.php → includes/Framework/AdminNoticeHandler.php RENAMED
@@ -1,51 +1,27 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Plugin/Classes
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
26
 
27
  defined( 'ABSPATH' ) or exit;
28
 
29
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_Admin_Notice_Handler' ) ) :
30
-
31
-
32
  /**
33
- * SkyVerge Admin Notice Handler Class
34
  *
35
  * The purpose of this class is to provide a facility for displaying
36
  * conditional (often dismissible) admin notices during a single page
37
  * request
38
- *
39
- * @since 3.0.0
40
  */
41
- class SV_WC_Admin_Notice_Handler {
42
 
43
-
44
- /** @var SV_WC_Plugin the plugin */
45
  private $plugin;
46
 
47
  /** @var array associative array of id to notice text */
48
- private $admin_notices = array();
49
 
50
  /** @var boolean static member to enforce a single rendering of the admin notice placeholder element */
51
  static private $admin_notice_placeholder_rendered = false;
@@ -89,7 +65,7 @@ class SV_WC_Admin_Notice_Handler {
89
  * @type string $notice_class Additional classes for the notice.
90
  * }
91
  */
92
- public function add_admin_notice( $message, $message_id, $params = array() ) {
93
 
94
  $params = wp_parse_args( $params, array(
95
  'dismissible' => true,
@@ -122,7 +98,7 @@ class SV_WC_Admin_Notice_Handler {
122
  * }
123
  * @return bool
124
  */
125
- public function should_display_notice( $message_id, $params = array() ) {
126
 
127
  // bail out if user is not a shop manager
128
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
@@ -205,7 +181,7 @@ class SV_WC_Admin_Notice_Handler {
205
  * @type string $notice_class Additional classes for the notice.
206
  * }
207
  */
208
- public function render_admin_notice( $message, $message_id, $params = array() ) {
209
 
210
  $params = wp_parse_args( $params, array(
211
  'dismissible' => true,
@@ -312,17 +288,12 @@ class SV_WC_Admin_Notice_Handler {
312
  * @param int $user_id optional user identifier, defaults to current user
313
  */
314
  public function dismiss_notice( $message_id, $user_id = null ) {
315
-
316
  if ( is_null( $user_id ) ) {
317
  $user_id = get_current_user_id();
318
  }
319
-
320
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
321
-
322
  $dismissed_notices[ $message_id ] = true;
323
-
324
  update_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', $dismissed_notices );
325
-
326
  /**
327
  * Admin Notice Dismissed Action.
328
  *
@@ -344,15 +315,11 @@ class SV_WC_Admin_Notice_Handler {
344
  * @param int $user_id optional user identifier, defaults to current user
345
  */
346
  public function undismiss_notice( $message_id, $user_id = null ) {
347
-
348
  if ( is_null( $user_id ) ) {
349
  $user_id = get_current_user_id();
350
  }
351
-
352
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
353
-
354
  $dismissed_notices[ $message_id ] = false;
355
-
356
  update_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', $dismissed_notices );
357
  }
358
 
@@ -367,9 +334,7 @@ class SV_WC_Admin_Notice_Handler {
367
  * @return boolean true if the message has been dismissed by the admin user
368
  */
369
  public function is_notice_dismissed( $message_id, $user_id = null ) {
370
-
371
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
372
-
373
  return isset( $dismissed_notices[ $message_id ] ) && $dismissed_notices[ $message_id ];
374
  }
375
 
@@ -383,15 +348,12 @@ class SV_WC_Admin_Notice_Handler {
383
  * @return array of message id to dismissed status (true or false)
384
  */
385
  public function get_dismissed_notices( $user_id = null ) {
386
-
387
  if ( is_null( $user_id ) ) {
388
  $user_id = get_current_user_id();
389
  }
390
-
391
  $dismissed_notices = get_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', true );
392
-
393
  if ( empty( $dismissed_notices ) ) {
394
- return array();
395
  } else {
396
  return $dismissed_notices;
397
  }
@@ -407,9 +369,7 @@ class SV_WC_Admin_Notice_Handler {
407
  * @since 3.0.0
408
  */
409
  public function handle_dismiss_notice() {
410
-
411
  $this->dismiss_notice( $_REQUEST['messageid'] );
412
-
413
  }
414
 
415
 
@@ -419,8 +379,7 @@ class SV_WC_Admin_Notice_Handler {
419
  /**
420
  * Get the plugin
421
  *
422
- * @since 3.0.0
423
- * @return SV_WC_Plugin returns the plugin instance
424
  */
425
  protected function get_plugin() {
426
  return $this->plugin;
@@ -428,6 +387,3 @@ class SV_WC_Admin_Notice_Handler {
428
 
429
 
430
  }
431
-
432
-
433
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework;
8
 
9
  defined( 'ABSPATH' ) or exit;
10
 
 
 
 
11
  /**
12
+ * Admin Notice Handler Class
13
  *
14
  * The purpose of this class is to provide a facility for displaying
15
  * conditional (often dismissible) admin notices during a single page
16
  * request
 
 
17
  */
18
+ class AdminNoticeHandler {
19
 
20
+ /** @var Plugin the plugin */
 
21
  private $plugin;
22
 
23
  /** @var array associative array of id to notice text */
24
+ private $admin_notices = [];
25
 
26
  /** @var boolean static member to enforce a single rendering of the admin notice placeholder element */
27
  static private $admin_notice_placeholder_rendered = false;
65
  * @type string $notice_class Additional classes for the notice.
66
  * }
67
  */
68
+ public function add_admin_notice( $message, $message_id, $params = [] ) {
69
 
70
  $params = wp_parse_args( $params, array(
71
  'dismissible' => true,
98
  * }
99
  * @return bool
100
  */
101
+ public function should_display_notice( $message_id, $params = [] ) {
102
 
103
  // bail out if user is not a shop manager
104
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
181
  * @type string $notice_class Additional classes for the notice.
182
  * }
183
  */
184
+ public function render_admin_notice( $message, $message_id, $params = [] ) {
185
 
186
  $params = wp_parse_args( $params, array(
187
  'dismissible' => true,
288
  * @param int $user_id optional user identifier, defaults to current user
289
  */
290
  public function dismiss_notice( $message_id, $user_id = null ) {
 
291
  if ( is_null( $user_id ) ) {
292
  $user_id = get_current_user_id();
293
  }
 
294
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
 
295
  $dismissed_notices[ $message_id ] = true;
 
296
  update_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', $dismissed_notices );
 
297
  /**
298
  * Admin Notice Dismissed Action.
299
  *
315
  * @param int $user_id optional user identifier, defaults to current user
316
  */
317
  public function undismiss_notice( $message_id, $user_id = null ) {
 
318
  if ( is_null( $user_id ) ) {
319
  $user_id = get_current_user_id();
320
  }
 
321
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
 
322
  $dismissed_notices[ $message_id ] = false;
 
323
  update_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', $dismissed_notices );
324
  }
325
 
334
  * @return boolean true if the message has been dismissed by the admin user
335
  */
336
  public function is_notice_dismissed( $message_id, $user_id = null ) {
 
337
  $dismissed_notices = $this->get_dismissed_notices( $user_id );
 
338
  return isset( $dismissed_notices[ $message_id ] ) && $dismissed_notices[ $message_id ];
339
  }
340
 
348
  * @return array of message id to dismissed status (true or false)
349
  */
350
  public function get_dismissed_notices( $user_id = null ) {
 
351
  if ( is_null( $user_id ) ) {
352
  $user_id = get_current_user_id();
353
  }
 
354
  $dismissed_notices = get_user_meta( $user_id, '_wc_plugin_framework_' . $this->get_plugin()->get_id() . '_dismissed_messages', true );
 
355
  if ( empty( $dismissed_notices ) ) {
356
+ return [];
357
  } else {
358
  return $dismissed_notices;
359
  }
369
  * @since 3.0.0
370
  */
371
  public function handle_dismiss_notice() {
 
372
  $this->dismiss_notice( $_REQUEST['messageid'] );
 
373
  }
374
 
375
 
379
  /**
380
  * Get the plugin
381
  *
382
+ * @return Plugin returns the plugin instance
 
383
  */
384
  protected function get_plugin() {
385
  return $this->plugin;
387
 
388
 
389
  }
 
 
 
vendor/skyverge/wc-plugin-framework/woocommerce/api/class-sv-wc-api-base.php → includes/Framework/Api/Base.php RENAMED
@@ -1,33 +1,17 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/API
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
26
-
27
- defined( 'ABSPATH' ) or exit;
28
 
29
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_API_Base' ) ) :
 
 
 
30
 
 
31
 
32
  /**
33
  * # WooCommerce Plugin Framework API Base Class
@@ -37,7 +21,7 @@ if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_A
37
  *
38
  * @version 2.2.0
39
  */
40
- abstract class SV_WC_API_Base {
41
 
42
 
43
  /** @var string request method, defaults to POST */
@@ -47,7 +31,7 @@ abstract class SV_WC_API_Base {
47
  protected $request_uri;
48
 
49
  /** @var array request headers */
50
- protected $request_headers = array();
51
 
52
  /** @var string request user-agent */
53
  protected $request_user_agent;
@@ -58,7 +42,7 @@ abstract class SV_WC_API_Base {
58
  /** @var string request duration */
59
  protected $request_duration;
60
 
61
- /** @var SV_WC_API_Request|object request */
62
  protected $request;
63
 
64
  /** @var string response code */
@@ -76,7 +60,7 @@ abstract class SV_WC_API_Base {
76
  /** @var string response handler class name */
77
  protected $response_handler;
78
 
79
- /** @var SV_WC_API_Response|object response */
80
  protected $response;
81
 
82
 
@@ -85,44 +69,32 @@ abstract class SV_WC_API_Base {
85
  *
86
  * @since 2.2.0
87
  *
88
- * @param SV_WC_API_Request|object $request class instance which implements SV_WC_API_Request
89
- * @return SV_WC_API_Response|object class instance which implements SV_WC_API_Response
90
- * @throws SV_WC_API_Exception may be thrown in implementations
91
  */
92
  protected function perform_request( $request ) {
93
-
94
  // ensure API is in its default state
95
  $this->reset_response();
96
-
97
  // save the request object
98
  $this->request = $request;
99
-
100
  $start_time = microtime( true );
101
-
102
  // if this API requires TLS v1.2, force it
103
- if ( $this->get_plugin()->require_tls_1_2() ) {
104
  add_action( 'http_api_curl', array( $this, 'set_tls_1_2_request' ), 10, 3 );
105
  }
106
-
107
  // perform the request
108
  $response = $this->do_remote_request( $this->get_request_uri(), $this->get_request_args() );
109
-
110
  // calculate request duration
111
  $this->request_duration = round( microtime( true ) - $start_time, 5 );
112
-
113
  try {
114
-
115
  // parse & validate response
116
  $response = $this->handle_response( $response );
117
-
118
- } catch ( SV_WC_Plugin_Exception $e ) {
119
-
120
  // alert other actors that a request has been made
121
  $this->broadcast_request();
122
-
123
  throw $e;
124
  }
125
-
126
  return $response;
127
  }
128
 
@@ -135,11 +107,10 @@ abstract class SV_WC_API_Base {
135
  * @since 2.2.0
136
  *
137
  * @param string $request_uri
138
- * @param string $request_args
139
  * @return array|\WP_Error
140
  */
141
- protected function do_remote_request( $request_uri, $request_args ) {
142
-
143
  return wp_safe_remote_request( $request_uri, $request_args );
144
  }
145
 
@@ -149,14 +120,13 @@ abstract class SV_WC_API_Base {
149
  *
150
  * @since 2.2.0
151
  * @param array|\WP_Error $response response data
152
- * @throws SV_WC_API_Exception network issues, timeouts, API errors, etc
153
- * @return SV_WC_API_Request|object request class instance that implements SV_WC_API_Request
154
  */
155
- protected function handle_response( $response ) {
156
-
157
  // check for WP HTTP API specific errors (network timeout, etc)
158
  if ( is_wp_error( $response ) ) {
159
- throw new SV_WC_API_Exception( $response->get_error_message(), (int) $response->get_error_code() );
160
  }
161
 
162
  // set response data
@@ -173,17 +143,9 @@ abstract class SV_WC_API_Base {
173
 
174
  $this->response_headers = $response_headers;
175
 
176
- // allow child classes to validate response prior to parsing -- this is useful
177
- // for checking HTTP status codes, etc.
178
- $this->do_pre_parse_response_validation();
179
-
180
  // parse the response body and tie it to the request
181
  $this->response = $this->get_parsed_response( $this->raw_response_body );
182
 
183
- // allow child classes to validate response after parsing -- this is useful
184
- // for checking error codes/messages included in a parsed response
185
- $this->do_post_parse_response_validation();
186
-
187
  // fire do_action() so other actors can act on request/response data,
188
  // primarily used for logging
189
  $this->broadcast_request();
@@ -192,55 +154,15 @@ abstract class SV_WC_API_Base {
192
  }
193
 
194
 
195
- /**
196
- * Allow child classes to validate a response prior to instantiating the
197
- * response object. Useful for checking response codes or messages, e.g.
198
- * throw an exception if the response code is not 200.
199
- *
200
- * A child class implementing this method should simply return true if the response
201
- * processing should continue, or throw a \SV_WC_API_Exception with a
202
- * relevant error message & code to stop processing.
203
- *
204
- * Note: Child classes *must* sanitize the raw response body before throwing
205
- * an exception, as it will be included in the broadcast_request() method
206
- * which is typically used to log requests.
207
- *
208
- * @since 2.2.0
209
- */
210
- protected function do_pre_parse_response_validation() {
211
- // stub method
212
- }
213
-
214
-
215
- /**
216
- * Allow child classes to validate a response after it has been parsed
217
- * and instantiated. This is useful for check error codes or messages that
218
- * exist in the parsed response.
219
- *
220
- * A child class implementing this method should simply return true if the response
221
- * processing should continue, or throw a \SV_WC_API_Exception with a
222
- * relevant error message & code to stop processing.
223
- *
224
- * Note: Response body sanitization is handled automatically
225
- *
226
- * @since 2.2.0
227
- */
228
- protected function do_post_parse_response_validation() {
229
- // stub method
230
- }
231
-
232
-
233
  /**
234
  * Return the parsed response object for the request
235
  *
236
  * @since 2.2.0
237
  * @param string $raw_response_body
238
- * @return object|SV_WC_API_Request response class instance which implements SV_WC_API_Request
239
  */
240
  protected function get_parsed_response( $raw_response_body ) {
241
-
242
  $handler_class = $this->get_response_handler();
243
-
244
  return new $handler_class( $raw_response_body );
245
  }
246
 
@@ -252,22 +174,21 @@ abstract class SV_WC_API_Base {
252
  * @since 2.2.0
253
  */
254
  protected function broadcast_request() {
255
-
256
- $request_data = array(
257
  'method' => $this->get_request_method(),
258
  'uri' => $this->get_request_uri(),
259
  'user-agent' => $this->get_request_user_agent(),
260
  'headers' => $this->get_sanitized_request_headers(),
261
  'body' => $this->get_sanitized_request_body(),
262
  'duration' => $this->get_request_duration() . 's', // seconds
263
- );
264
 
265
- $response_data = array(
266
  'code' => $this->get_response_code(),
267
  'message' => $this->get_response_message(),
268
  'headers' => $this->get_response_headers(),
269
  'body' => $this->get_sanitized_response_body() ? $this->get_sanitized_response_body() : $this->get_raw_response_body(),
270
- );
271
 
272
  /**
273
  * API Base Request Performed Action.
@@ -290,7 +211,7 @@ abstract class SV_WC_API_Base {
290
  * @type string $headers response HTTP headers
291
  * @type string $body response body
292
  * }
293
- * @param SV_WC_API_Base $this instance
294
  */
295
  do_action( 'wc_' . $this->get_api_id() . '_api_request_performed', $request_data, $response_data, $this );
296
  }
@@ -302,7 +223,6 @@ abstract class SV_WC_API_Base {
302
  * @since 1.0.0
303
  */
304
  protected function reset_response() {
305
-
306
  $this->response_code = null;
307
  $this->response_message = null;
308
  $this->response_headers = null;
@@ -322,21 +242,16 @@ abstract class SV_WC_API_Base {
322
  * @return string
323
  */
324
  protected function get_request_uri() {
325
-
326
  $uri = $this->request_uri . $this->get_request_path();
327
-
328
  // append any query params to the URL when necessary
329
  if ( $query = $this->get_request_query() ) {
330
-
331
  $url_parts = parse_url( $uri );
332
-
333
  // if the URL already has some query params, add to them
334
  if ( ! empty( $url_parts['query'] ) ) {
335
  $query = '&' . $query;
336
  } else {
337
  $query = '?' . $query;
338
  }
339
-
340
  $uri = untrailingslashit( $uri ) . $query;
341
  }
342
 
@@ -350,7 +265,7 @@ abstract class SV_WC_API_Base {
350
  * @since 4.1.0
351
  *
352
  * @param string $uri current request URI
353
- * @param SV_WC_API_Base class instance
354
  */
355
  return apply_filters( 'wc_' . $this->get_api_id() . '_api_request_uri', $uri, $this );
356
  }
@@ -363,7 +278,6 @@ abstract class SV_WC_API_Base {
363
  * @return string
364
  */
365
  protected function get_request_path() {
366
-
367
  return ( $this->get_request() ) ? $this->get_request()->get_path() : '';
368
  }
369
 
@@ -376,19 +290,14 @@ abstract class SV_WC_API_Base {
376
  * @return string
377
  */
378
  protected function get_request_query() {
379
-
380
  $query = '';
381
  $request = $this->get_request();
382
-
383
- if ( $request && in_array( strtoupper( $this->get_request_method() ), array( 'GET', 'HEAD' ), true ) ) {
384
-
385
  $params = $request->get_params();
386
-
387
  if ( ! empty( $params ) ) {
388
  $query = http_build_query( $params, '', '&' );
389
  }
390
  }
391
-
392
  return $query;
393
  }
394
 
@@ -401,8 +310,7 @@ abstract class SV_WC_API_Base {
401
  * @return array
402
  */
403
  protected function get_request_args() {
404
-
405
- $args = array(
406
  'method' => $this->get_request_method(),
407
  'timeout' => MINUTE_IN_SECONDS,
408
  'redirection' => 0,
@@ -412,8 +320,8 @@ abstract class SV_WC_API_Base {
412
  'user-agent' => $this->get_request_user_agent(),
413
  'headers' => $this->get_request_headers(),
414
  'body' => $this->get_request_body(),
415
- 'cookies' => array(),
416
- );
417
 
418
  /**
419
  * Request arguments.
@@ -424,7 +332,7 @@ abstract class SV_WC_API_Base {
424
  *
425
  * @since 2.2.0
426
  * @param array $args request arguments
427
- * @param SV_WC_API_Base class instance
428
  */
429
  return apply_filters( 'wc_' . $this->get_api_id() . '_http_request_args', $args, $this );
430
  }
@@ -449,12 +357,10 @@ abstract class SV_WC_API_Base {
449
  * @return string
450
  */
451
  protected function get_request_body() {
452
-
453
  // GET & HEAD requests don't support a body
454
- if ( in_array( strtoupper( $this->get_request_method() ), array( 'GET', 'HEAD' ) ) ) {
455
  return '';
456
  }
457
-
458
  return ( $this->get_request() && $this->get_request()->to_string() ) ? $this->get_request()->to_string() : '';
459
  }
460
 
@@ -466,12 +372,10 @@ abstract class SV_WC_API_Base {
466
  * @return string
467
  */
468
  protected function get_sanitized_request_body() {
469
-
470
  // GET & HEAD requests don't support a body
471
- if ( in_array( strtoupper( $this->get_request_method() ), array( 'GET', 'HEAD' ) ) ) {
472
  return '';
473
  }
474
-
475
  return ( $this->get_request() && $this->get_request()->to_string_safe() ) ? $this->get_request()->to_string_safe() : '';
476
  }
477
 
@@ -483,7 +387,6 @@ abstract class SV_WC_API_Base {
483
  * @return string
484
  */
485
  protected function get_request_http_version() {
486
-
487
  return $this->request_http_version;
488
  }
489
 
@@ -512,13 +415,10 @@ abstract class SV_WC_API_Base {
512
  * @return array
513
  */
514
  protected function get_sanitized_request_headers() {
515
-
516
  $headers = $this->get_request_headers();
517
-
518
  if ( ! empty( $headers['Authorization'] ) ) {
519
  $headers['Authorization'] = str_repeat( '*', strlen( $headers['Authorization'] ) );
520
  }
521
-
522
  return $headers;
523
  }
524
 
@@ -532,7 +432,6 @@ abstract class SV_WC_API_Base {
532
  * @return string
533
  */
534
  protected function get_request_user_agent() {
535
-
536
  return sprintf( '%s/%s (WooCommerce/%s; WordPress/%s)', str_replace( ' ', '-', $this->get_plugin()->get_plugin_name() ), $this->get_plugin()->get_version(), WC_VERSION, $GLOBALS['wp_version'] );
537
  }
538
 
@@ -626,7 +525,7 @@ abstract class SV_WC_API_Base {
626
  *
627
  * @since 2.2.0
628
  *
629
- * @return SV_WC_API_Request|object the most recent request object
630
  */
631
  public function get_request() {
632
 
@@ -639,10 +538,9 @@ abstract class SV_WC_API_Base {
639
  *
640
  * @since 2.2.0
641
  *
642
- * @return SV_WC_API_Response|object the most recent response object
643
  */
644
  public function get_response() {
645
-
646
  return $this->response;
647
  }
648
 
@@ -655,7 +553,6 @@ abstract class SV_WC_API_Base {
655
  * @return string
656
  */
657
  protected function get_api_id() {
658
-
659
  return $this->get_plugin()->get_id();
660
  }
661
 
@@ -671,9 +568,9 @@ abstract class SV_WC_API_Base {
671
  * @since 2.2.0
672
  *
673
  * @param array $args optional request arguments
674
- * @return SV_WC_API_Request|object
675
  */
676
- abstract protected function get_new_request( $args = array() );
677
 
678
 
679
  /**
@@ -686,7 +583,7 @@ abstract class SV_WC_API_Base {
686
  *
687
  * @since 2.2.0
688
  *
689
- * @return SV_WC_Plugin
690
  */
691
  abstract protected function get_plugin();
692
 
@@ -703,7 +600,6 @@ abstract class SV_WC_API_Base {
703
  * @return string
704
  */
705
  protected function set_request_header( $name, $value ) {
706
-
707
  $this->request_headers[ $name ] = $value;
708
  }
709
 
@@ -715,9 +611,7 @@ abstract class SV_WC_API_Base {
715
  * @param array $headers
716
  */
717
  protected function set_request_headers( array $headers ) {
718
-
719
  foreach ( $headers as $name => $value ) {
720
-
721
  $this->request_headers[ $name ] = $value;
722
  }
723
  }
@@ -731,7 +625,6 @@ abstract class SV_WC_API_Base {
731
  * @param string $password
732
  */
733
  protected function set_http_basic_auth( $username, $password ) {
734
-
735
  $this->request_headers['Authorization'] = sprintf( 'Basic %s', base64_encode( "{$username}:{$password}" ) );
736
  }
737
 
@@ -769,7 +662,6 @@ abstract class SV_WC_API_Base {
769
  * @param string $handler handle class name
770
  */
771
  protected function set_response_handler( $handler ) {
772
-
773
  $this->response_handler = $handler;
774
  }
775
 
@@ -784,11 +676,9 @@ abstract class SV_WC_API_Base {
784
  * @param $url string the request URL
785
  */
786
  public function set_tls_1_2_request( $handle, $r, $url ) {
787
-
788
- if ( ! SV_WC_Helper::str_starts_with( $url, 'https://' ) ) {
789
  return;
790
  }
791
-
792
  curl_setopt( $handle, CURLOPT_SSLVERSION, 6 );
793
  }
794
 
@@ -802,10 +692,7 @@ abstract class SV_WC_API_Base {
802
  * @return bool
803
  */
804
  public function require_tls_1_2() {
805
-
806
- wc_deprecated_function( __METHOD__, '5.5.2', 'SV_WC_Plugin::require_tls_1_2()' );
807
-
808
- return $this->get_plugin()->require_tls_1_2();
809
  }
810
 
811
 
@@ -817,20 +704,14 @@ abstract class SV_WC_API_Base {
817
  * @return bool
818
  */
819
  public function is_tls_1_2_available() {
820
-
821
  /**
822
  * Filters whether TLS 1.2 is available.
823
  *
824
  * @since 4.7.1
825
  *
826
  * @param bool $is_available whether TLS 1.2 is available
827
- * @param SV_WC_API_Base $api API class instance
828
  */
829
  return (bool) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_api_is_tls_1_2_available', $this->get_plugin()->is_tls_1_2_available(), $this );
830
  }
831
-
832
-
833
  }
834
-
835
-
836
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework\Api;
 
 
8
 
9
+ use WooCommerce\Facebook\Framework\Helper;
10
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
11
+ use WooCommerce\Facebook\Framework\Plugin;
12
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
13
 
14
+ defined( 'ABSPATH' ) or exit;
15
 
16
  /**
17
  * # WooCommerce Plugin Framework API Base Class
21
  *
22
  * @version 2.2.0
23
  */
24
+ abstract class Base {
25
 
26
 
27
  /** @var string request method, defaults to POST */
31
  protected $request_uri;
32
 
33
  /** @var array request headers */
34
+ protected $request_headers = [];
35
 
36
  /** @var string request user-agent */
37
  protected $request_user_agent;
42
  /** @var string request duration */
43
  protected $request_duration;
44
 
45
+ /** @var Request|object request */
46
  protected $request;
47
 
48
  /** @var string response code */
60
  /** @var string response handler class name */
61
  protected $response_handler;
62
 
63
+ /** @var Response|object response */
64
  protected $response;
65
 
66
 
69
  *
70
  * @since 2.2.0
71
  *
72
+ * @param Request|object $request class instance which implements SV_WC_API_Request
73
+ * @return Response class instance which implements Api/Response
74
+ * @throws ApiException may be thrown in implementations
75
  */
76
  protected function perform_request( $request ) {
 
77
  // ensure API is in its default state
78
  $this->reset_response();
 
79
  // save the request object
80
  $this->request = $request;
 
81
  $start_time = microtime( true );
 
82
  // if this API requires TLS v1.2, force it
83
+ if ( $this->require_tls_1_2() ) {
84
  add_action( 'http_api_curl', array( $this, 'set_tls_1_2_request' ), 10, 3 );
85
  }
 
86
  // perform the request
87
  $response = $this->do_remote_request( $this->get_request_uri(), $this->get_request_args() );
 
88
  // calculate request duration
89
  $this->request_duration = round( microtime( true ) - $start_time, 5 );
 
90
  try {
 
91
  // parse & validate response
92
  $response = $this->handle_response( $response );
93
+ } catch ( PluginException $e ) {
 
 
94
  // alert other actors that a request has been made
95
  $this->broadcast_request();
 
96
  throw $e;
97
  }
 
98
  return $response;
99
  }
100
 
107
  * @since 2.2.0
108
  *
109
  * @param string $request_uri
110
+ * @param array $request_args
111
  * @return array|\WP_Error
112
  */
113
+ protected function do_remote_request( string $request_uri, array $request_args ) {
 
114
  return wp_safe_remote_request( $request_uri, $request_args );
115
  }
116
 
120
  *
121
  * @since 2.2.0
122
  * @param array|\WP_Error $response response data
123
+ * @throws ApiException network issues, timeouts, API errors, etc
124
+ * @return \WooCommerce\Facebook\API\Response Response class instance.
125
  */
126
+ protected function handle_response( $response ): \WooCommerce\Facebook\API\Response {
 
127
  // check for WP HTTP API specific errors (network timeout, etc)
128
  if ( is_wp_error( $response ) ) {
129
+ throw new ApiException( $response->get_error_message(), (int) $response->get_error_code() );
130
  }
131
 
132
  // set response data
143
 
144
  $this->response_headers = $response_headers;
145
 
 
 
 
 
146
  // parse the response body and tie it to the request
147
  $this->response = $this->get_parsed_response( $this->raw_response_body );
148
 
 
 
 
 
149
  // fire do_action() so other actors can act on request/response data,
150
  // primarily used for logging
151
  $this->broadcast_request();
154
  }
155
 
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  /**
158
  * Return the parsed response object for the request
159
  *
160
  * @since 2.2.0
161
  * @param string $raw_response_body
162
+ * @return object|Request response class instance which implements SV_WC_API_Request
163
  */
164
  protected function get_parsed_response( $raw_response_body ) {
 
165
  $handler_class = $this->get_response_handler();
 
166
  return new $handler_class( $raw_response_body );
167
  }
168
 
174
  * @since 2.2.0
175
  */
176
  protected function broadcast_request() {
177
+ $request_data = [
 
178
  'method' => $this->get_request_method(),
179
  'uri' => $this->get_request_uri(),
180
  'user-agent' => $this->get_request_user_agent(),
181
  'headers' => $this->get_sanitized_request_headers(),
182
  'body' => $this->get_sanitized_request_body(),
183
  'duration' => $this->get_request_duration() . 's', // seconds
184
+ ];
185
 
186
+ $response_data = [
187
  'code' => $this->get_response_code(),
188
  'message' => $this->get_response_message(),
189
  'headers' => $this->get_response_headers(),
190
  'body' => $this->get_sanitized_response_body() ? $this->get_sanitized_response_body() : $this->get_raw_response_body(),
191
+ ];
192
 
193
  /**
194
  * API Base Request Performed Action.
211
  * @type string $headers response HTTP headers
212
  * @type string $body response body
213
  * }
214
+ * @param \WooCommerce\Facebook\Framework\Api\Base $this instance
215
  */
216
  do_action( 'wc_' . $this->get_api_id() . '_api_request_performed', $request_data, $response_data, $this );
217
  }
223
  * @since 1.0.0
224
  */
225
  protected function reset_response() {
 
226
  $this->response_code = null;
227
  $this->response_message = null;
228
  $this->response_headers = null;
242
  * @return string
243
  */
244
  protected function get_request_uri() {
 
245
  $uri = $this->request_uri . $this->get_request_path();
 
246
  // append any query params to the URL when necessary
247
  if ( $query = $this->get_request_query() ) {
 
248
  $url_parts = parse_url( $uri );
 
249
  // if the URL already has some query params, add to them
250
  if ( ! empty( $url_parts['query'] ) ) {
251
  $query = '&' . $query;
252
  } else {
253
  $query = '?' . $query;
254
  }
 
255
  $uri = untrailingslashit( $uri ) . $query;
256
  }
257
 
265
  * @since 4.1.0
266
  *
267
  * @param string $uri current request URI
268
+ * @param Base class instance
269
  */
270
  return apply_filters( 'wc_' . $this->get_api_id() . '_api_request_uri', $uri, $this );
271
  }
278
  * @return string
279
  */
280
  protected function get_request_path() {
 
281
  return ( $this->get_request() ) ? $this->get_request()->get_path() : '';
282
  }
283
 
290
  * @return string
291
  */
292
  protected function get_request_query() {
 
293
  $query = '';
294
  $request = $this->get_request();
295
+ if ( $request && in_array( strtoupper( $this->get_request_method() ), [ 'GET', 'HEAD' ], true ) ) {
 
 
296
  $params = $request->get_params();
 
297
  if ( ! empty( $params ) ) {
298
  $query = http_build_query( $params, '', '&' );
299
  }
300
  }
 
301
  return $query;
302
  }
303
 
310
  * @return array
311
  */
312
  protected function get_request_args() {
313
+ $args = [
 
314
  'method' => $this->get_request_method(),
315
  'timeout' => MINUTE_IN_SECONDS,
316
  'redirection' => 0,
320
  'user-agent' => $this->get_request_user_agent(),
321
  'headers' => $this->get_request_headers(),
322
  'body' => $this->get_request_body(),
323
+ 'cookies' => [],
324
+ ];
325
 
326
  /**
327
  * Request arguments.
332
  *
333
  * @since 2.2.0
334
  * @param array $args request arguments
335
+ * @param Base class instance
336
  */
337
  return apply_filters( 'wc_' . $this->get_api_id() . '_http_request_args', $args, $this );
338
  }
357
  * @return string
358
  */
359
  protected function get_request_body() {
 
360
  // GET & HEAD requests don't support a body
361
+ if ( in_array( strtoupper( $this->get_request_method() ), [ 'GET', 'HEAD' ] ) ) {
362
  return '';
363
  }
 
364
  return ( $this->get_request() && $this->get_request()->to_string() ) ? $this->get_request()->to_string() : '';
365
  }
366
 
372
  * @return string
373
  */
374
  protected function get_sanitized_request_body() {
 
375
  // GET & HEAD requests don't support a body
376
+ if ( in_array( strtoupper( $this->get_request_method() ), [ 'GET', 'HEAD' ] ) ) {
377
  return '';
378
  }
 
379
  return ( $this->get_request() && $this->get_request()->to_string_safe() ) ? $this->get_request()->to_string_safe() : '';
380
  }
381
 
387
  * @return string
388
  */
389
  protected function get_request_http_version() {
 
390
  return $this->request_http_version;
391
  }
392
 
415
  * @return array
416
  */
417
  protected function get_sanitized_request_headers() {
 
418
  $headers = $this->get_request_headers();
 
419
  if ( ! empty( $headers['Authorization'] ) ) {
420
  $headers['Authorization'] = str_repeat( '*', strlen( $headers['Authorization'] ) );
421
  }
 
422
  return $headers;
423
  }
424
 
432
  * @return string
433
  */
434
  protected function get_request_user_agent() {
 
435
  return sprintf( '%s/%s (WooCommerce/%s; WordPress/%s)', str_replace( ' ', '-', $this->get_plugin()->get_plugin_name() ), $this->get_plugin()->get_version(), WC_VERSION, $GLOBALS['wp_version'] );
436
  }
437
 
525
  *
526
  * @since 2.2.0
527
  *
528
+ * @return Request|object the most recent request object
529
  */
530
  public function get_request() {
531
 
538
  *
539
  * @since 2.2.0
540
  *
541
+ * @return Response|object the most recent response object
542
  */
543
  public function get_response() {
 
544
  return $this->response;
545
  }
546
 
553
  * @return string
554
  */
555
  protected function get_api_id() {
 
556
  return $this->get_plugin()->get_id();
557
  }
558
 
568
  * @since 2.2.0
569
  *
570
  * @param array $args optional request arguments
571
+ * @return Request|object
572
  */
573
+ abstract protected function get_new_request( $args = [] );
574
 
575
 
576
  /**
583
  *
584
  * @since 2.2.0
585
  *
586
+ * @return Plugin
587
  */
588
  abstract protected function get_plugin();
589
 
600
  * @return string
601
  */
602
  protected function set_request_header( $name, $value ) {
 
603
  $this->request_headers[ $name ] = $value;
604
  }
605
 
611
  * @param array $headers
612
  */
613
  protected function set_request_headers( array $headers ) {
 
614
  foreach ( $headers as $name => $value ) {
 
615
  $this->request_headers[ $name ] = $value;
616
  }
617
  }
625
  * @param string $password
626
  */
627
  protected function set_http_basic_auth( $username, $password ) {
 
628
  $this->request_headers['Authorization'] = sprintf( 'Basic %s', base64_encode( "{$username}:{$password}" ) );
629
  }
630
 
662
  * @param string $handler handle class name
663
  */
664
  protected function set_response_handler( $handler ) {
 
665
  $this->response_handler = $handler;
666
  }
667
 
676
  * @param $url string the request URL
677
  */
678
  public function set_tls_1_2_request( $handle, $r, $url ) {
679
+ if ( ! Helper::str_starts_with( $url, 'https://' ) ) {
 
680
  return;
681
  }
 
682
  curl_setopt( $handle, CURLOPT_SSLVERSION, 6 );
683
  }
684
 
692
  * @return bool
693
  */
694
  public function require_tls_1_2() {
695
+ return false;
 
 
 
696
  }
697
 
698
 
704
  * @return bool
705
  */
706
  public function is_tls_1_2_available() {
 
707
  /**
708
  * Filters whether TLS 1.2 is available.
709
  *
710
  * @since 4.7.1
711
  *
712
  * @param bool $is_available whether TLS 1.2 is available
713
+ * @param Base $api API class instance
714
  */
715
  return (bool) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_api_is_tls_1_2_available', $this->get_plugin()->is_tls_1_2_available(), $this );
716
  }
 
 
717
  }
 
 
 
includes/Framework/Api/Exception.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Api;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Plugin Framework API Exception - generic API Exception
13
+ */
14
+ class Exception extends \WooCommerce\Facebook\Framework\Plugin\Exception { }
includes/Framework/Api/JSONRequest.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Api;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Base JSON API request class.
13
+ */
14
+ abstract class JSONRequest implements Request {
15
+
16
+ /** @var string The request method, one of HEAD, GET, PUT, PATCH, POST, DELETE */
17
+ protected $method;
18
+
19
+ /** @var string The request path */
20
+ protected $path;
21
+
22
+ /** @var array The request parameters, if any */
23
+ protected $params = [];
24
+
25
+ /** @var array the request data */
26
+ protected $data = [];
27
+
28
+ /**
29
+ * Get the request method.
30
+ *
31
+ * @since 4.3.0
32
+ * @see Request::get_method()
33
+ * @return string
34
+ */
35
+ public function get_method() {
36
+ return $this->method;
37
+ }
38
+
39
+ /**
40
+ * Get the request path.
41
+ *
42
+ * @since 4.3.0
43
+ * @see Request::get_path()
44
+ * @return string
45
+ */
46
+ public function get_path() {
47
+ return $this->path;
48
+ }
49
+
50
+ /**
51
+ * Get the request parameters.
52
+ *
53
+ * @since 4.3.0
54
+ * @see Request::get_params()
55
+ * @return array
56
+ */
57
+ public function get_params() {
58
+ return $this->params;
59
+ }
60
+
61
+ /**
62
+ * Get the request data.
63
+ *
64
+ * @since 4.5.0
65
+ * @return array
66
+ */
67
+ public function get_data() {
68
+ return $this->data;
69
+ }
70
+
71
+ /**
72
+ * Get the string representation of this request.
73
+ *
74
+ * @since 4.3.0
75
+ * @see Request::to_string()
76
+ * @return string
77
+ */
78
+ public function to_string() {
79
+ $data = $this->get_data();
80
+ return ! empty( $data ) ? wp_json_encode( $data ) : '';
81
+ }
82
+
83
+ /**
84
+ * Get the string representation of this request with any and all sensitive elements masked
85
+ * or removed.
86
+ *
87
+ * @since 4.3.0
88
+ * @see Request::to_string_safe()
89
+ * @return string
90
+ */
91
+ public function to_string_safe() {
92
+ return $this->to_string();
93
+ }
94
+ }
includes/Framework/Api/JSONResponse.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Api;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Base JSON API response class.
13
+ *
14
+ * @since 4.3.0
15
+ */
16
+ abstract class JSONResponse implements Response {
17
+
18
+
19
+ /** @var string string representation of this response */
20
+ protected $raw_response_json;
21
+
22
+ /** @var mixed decoded response data */
23
+ public $response_data;
24
+
25
+
26
+ /**
27
+ * Build the data object from the raw JSON.
28
+ *
29
+ * @since 4.3.0
30
+ * @param string $raw_response_json The raw JSON
31
+ */
32
+ public function __construct( string $raw_response_json ) {
33
+ $this->raw_response_json = $raw_response_json;
34
+ $this->response_data = json_decode( $raw_response_json, true );
35
+ }
36
+
37
+
38
+ /**
39
+ * Magic accessor for response data attributes
40
+ *
41
+ * @since 4.3.0
42
+ * @param string $name The attribute name to get.
43
+ * @return mixed The attribute value
44
+ */
45
+ public function __get( string $name ) {
46
+ // accessing the response_data object indirectly via attribute (useful when it's a class)
47
+ return $this->response_data[ $name ] ?? null;
48
+ }
49
+
50
+
51
+ /**
52
+ * Get the string representation of this response.
53
+ *
54
+ * @since 4.3.0
55
+ * @see SV_Response::to_string()
56
+ * @return string
57
+ */
58
+ public function to_string() {
59
+ return $this->raw_response_json;
60
+ }
61
+
62
+
63
+ /**
64
+ * Get the string representation of this response with any and all sensitive elements masked
65
+ * or removed.
66
+ *
67
+ * @since 4.3.0
68
+ * @see Response::to_string_safe()
69
+ * @return string
70
+ */
71
+ public function to_string_safe() {
72
+ return $this->to_string();
73
+ }
74
+ }
includes/Framework/Api/Request.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Api;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * API Request
13
+ */
14
+ interface Request {
15
+ /**
16
+ * Returns the method for this request: one of HEAD, GET, PUT, PATCH, POST, DELETE
17
+ *
18
+ * @since 4.0.0
19
+ * @return string the request method, or null to use the API default
20
+ */
21
+ public function get_method();
22
+
23
+
24
+ /**
25
+ * Returns the request path
26
+ *
27
+ * @since 4.0.0
28
+ * @return string the request path, or '' if none
29
+ */
30
+ public function get_path();
31
+
32
+
33
+ /**
34
+ * Gets the request query params.
35
+ *
36
+ * @since 5.0.0
37
+ *
38
+ * @return array
39
+ */
40
+ public function get_params();
41
+
42
+
43
+ /**
44
+ * Gets the request data.
45
+ *
46
+ * @since 5.0.0
47
+ *
48
+ * @return array
49
+ */
50
+ public function get_data();
51
+
52
+
53
+ /**
54
+ * Returns the string representation of this request
55
+ *
56
+ * @since 2.2.0
57
+ * @return string the request
58
+ */
59
+ public function to_string();
60
+
61
+
62
+ /**
63
+ * Returns the string representation of this request with any and all
64
+ * sensitive elements masked or removed
65
+ *
66
+ * @since 2.2.0
67
+ * @return string the request, safe for logging/displaying
68
+ */
69
+ public function to_string_safe();
70
+ }
includes/Framework/Api/Response.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Api;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * API Response
13
+ */
14
+ interface Response {
15
+
16
+
17
+ /**
18
+ * Returns the string representation of this request
19
+ *
20
+ * @since 2.2.0
21
+ * @return string the request
22
+ */
23
+ public function to_string();
24
+
25
+
26
+ /**
27
+ * Returns the string representation of this request with any and all
28
+ * sensitive elements masked or removed
29
+ *
30
+ * @since 2.2.0
31
+ * @return string the request, safe for logging/displaying
32
+ */
33
+ public function to_string_safe();
34
+
35
+ }
includes/Framework/Helper.php ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Facebook Helper Class
13
+ * The purpose of this class is to centralize common utility functions.
14
+ */
15
+ class Helper {
16
+
17
+ /** encoding used for mb_*() string functions */
18
+ const MB_ENCODING = 'UTF-8';
19
+
20
+ /** String manipulation functions (all multi-byte safe) ***************/
21
+
22
+ /**
23
+ * Returns true if the haystack string starts with needle
24
+ *
25
+ * Note: case-sensitive
26
+ *
27
+ * @since 2.2.0
28
+ * @param string $haystack
29
+ * @param string $needle
30
+ * @return bool
31
+ */
32
+ public static function str_starts_with( $haystack, $needle ) {
33
+ if ( self::multibyte_loaded() ) {
34
+ if ( '' === $needle ) {
35
+ return true;
36
+ }
37
+ return 0 === mb_strpos( $haystack, $needle, 0, self::MB_ENCODING );
38
+ } else {
39
+ $needle = self::str_to_ascii( $needle );
40
+ if ( '' === $needle ) {
41
+ return true;
42
+ }
43
+ return 0 === strpos( self::str_to_ascii( $haystack ), self::str_to_ascii( $needle ) );
44
+ }
45
+ }
46
+
47
+
48
+ /**
49
+ * Return true if the haystack string ends with needle
50
+ *
51
+ * Note: case-sensitive
52
+ *
53
+ * @since 2.2.0
54
+ * @param string $haystack
55
+ * @param string $needle
56
+ * @return bool
57
+ */
58
+ public static function str_ends_with( $haystack, $needle ) {
59
+ if ( '' === $needle ) {
60
+ return true;
61
+ }
62
+ if ( self::multibyte_loaded() ) {
63
+ return mb_substr( $haystack, -mb_strlen( $needle, self::MB_ENCODING ), null, self::MB_ENCODING ) === $needle;
64
+ } else {
65
+ $haystack = self::str_to_ascii( $haystack );
66
+ $needle = self::str_to_ascii( $needle );
67
+ return substr( $haystack, -strlen( $needle ) ) === $needle;
68
+ }
69
+ }
70
+
71
+
72
+ /**
73
+ * Returns true if the needle exists in haystack
74
+ *
75
+ * Note: case-sensitive
76
+ *
77
+ * @since 2.2.0
78
+ * @param string $haystack
79
+ * @param string $needle
80
+ * @return bool
81
+ */
82
+ public static function str_exists( $haystack, $needle ) {
83
+ if ( self::multibyte_loaded() ) {
84
+ if ( '' === $needle ) {
85
+ return false;
86
+ }
87
+ return false !== mb_strpos( $haystack, $needle, 0, self::MB_ENCODING );
88
+ } else {
89
+ $needle = self::str_to_ascii( $needle );
90
+ if ( '' === $needle ) {
91
+ return false;
92
+ }
93
+ return false !== strpos( self::str_to_ascii( $haystack ), self::str_to_ascii( $needle ) );
94
+ }
95
+ }
96
+
97
+
98
+ /**
99
+ * Truncates a given $string after a given $length if string is longer than
100
+ * $length. The last characters will be replaced with the $omission string
101
+ * for a total length not exceeding $length
102
+ *
103
+ * @since 2.2.0
104
+ * @param string $string text to truncate
105
+ * @param int $length total desired length of string, including omission
106
+ * @param string $omission omission text, defaults to '...'
107
+ * @return string
108
+ */
109
+ public static function str_truncate( $string, $length, $omission = '...' ) {
110
+ if ( self::multibyte_loaded() ) {
111
+ // bail if string doesn't need to be truncated
112
+ if ( mb_strlen( $string, self::MB_ENCODING ) <= $length ) {
113
+ return $string;
114
+ }
115
+ $length -= mb_strlen( $omission, self::MB_ENCODING );
116
+ return mb_substr( $string, 0, $length, self::MB_ENCODING ) . $omission;
117
+ } else {
118
+ $string = self::str_to_ascii( $string );
119
+ // bail if string doesn't need to be truncated
120
+ if ( strlen( $string ) <= $length ) {
121
+ return $string;
122
+ }
123
+ $length -= strlen( $omission );
124
+ return substr( $string, 0, $length ) . $omission;
125
+ }
126
+ }
127
+
128
+
129
+ /**
130
+ * Returns a string with all non-ASCII characters removed. This is useful
131
+ * for any string functions that expect only ASCII chars and can't
132
+ * safely handle UTF-8. Note this only allows ASCII chars in the range
133
+ * 33-126 (newlines/carriage returns are stripped)
134
+ *
135
+ * @since 2.2.0
136
+ * @param string $string string to make ASCII
137
+ * @return string
138
+ */
139
+ public static function str_to_ascii( $string ) {
140
+ // strip ASCII chars 32 and under
141
+ $string = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW );
142
+ // strip ASCII chars 127 and higher
143
+ return filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH );
144
+ }
145
+
146
+
147
+ /**
148
+ * Helper method to check if the multibyte extension is loaded, which
149
+ * indicates it's safe to use the mb_*() string methods
150
+ *
151
+ * @since 2.2.0
152
+ * @return bool
153
+ */
154
+ protected static function multibyte_loaded() {
155
+ return extension_loaded( 'mbstring' );
156
+ }
157
+
158
+
159
+ /** Array functions ***************************************************/
160
+
161
+
162
+ /**
163
+ * Insert the given element after the given key in the array
164
+ *
165
+ * Sample usage:
166
+ *
167
+ * given
168
+ *
169
+ * array( 'item_1' => 'foo', 'item_2' => 'bar' )
170
+ *
171
+ * array_insert_after( $array, 'item_1', array( 'item_1.5' => 'w00t' ) )
172
+ *
173
+ * becomes
174
+ *
175
+ * array( 'item_1' => 'foo', 'item_1.5' => 'w00t', 'item_2' => 'bar' )
176
+ *
177
+ * @since 2.2.0
178
+ * @param array $array array to insert the given element into
179
+ * @param string $insert_key key to insert given element after
180
+ * @param array $element element to insert into array
181
+ * @return array
182
+ */
183
+ public static function array_insert_after( Array $array, $insert_key, Array $element ) {
184
+ $new_array = [];
185
+ foreach ( $array as $key => $value ) {
186
+ $new_array[ $key ] = $value;
187
+ if ( $insert_key == $key ) {
188
+ foreach ( $element as $k => $v ) {
189
+ $new_array[ $k ] = $v;
190
+ }
191
+ }
192
+ }
193
+ return $new_array;
194
+ }
195
+
196
+
197
+ /** Number helper functions *******************************************/
198
+
199
+
200
+ /**
201
+ * Format a number with 2 decimal points, using a period for the decimal
202
+ * separator and no thousands separator.
203
+ *
204
+ * Commonly used for payment gateways which require amounts in this format.
205
+ *
206
+ * @since 3.0.0
207
+ * @param float $number
208
+ * @return string
209
+ */
210
+ public static function number_format( $number ) {
211
+ return number_format( (float) $number, 2, '.', '' );
212
+ }
213
+
214
+
215
+ /** WooCommerce helper functions **************************************/
216
+
217
+
218
+ /**
219
+ * Safely gets a value from $_POST.
220
+ *
221
+ * If the expected data is a string also trims it.
222
+ *
223
+ * @since 5.5.0
224
+ *
225
+ * @param string $key posted data key
226
+ * @param int|float|array|bool|null|string $default default data type to return (default empty string)
227
+ * @return int|float|array|bool|null|string posted data value if key found, or default
228
+ */
229
+ public static function get_posted_value( $key, $default = '' ) {
230
+
231
+ $value = $default;
232
+
233
+ if ( isset( $_POST[ $key ] ) ) {
234
+ $value = is_string( $_POST[ $key ] ) ? trim( $_POST[ $key ] ) : $_POST[ $key ];
235
+ }
236
+
237
+ return $value;
238
+ }
239
+
240
+
241
+ /**
242
+ * Safely gets a value from $_REQUEST.
243
+ *
244
+ * If the expected data is a string also trims it.
245
+ *
246
+ * @since 5.5.0
247
+ *
248
+ * @param string $key posted data key
249
+ * @param int|float|array|bool|null|string $default default data type to return (default empty string)
250
+ * @return int|float|array|bool|null|string posted data value if key found, or default
251
+ */
252
+ public static function get_requested_value( $key, $default = '' ) {
253
+
254
+ $value = $default;
255
+
256
+ if ( isset( $_REQUEST[ $key ] ) ) {
257
+ $value = is_string( $_REQUEST[ $key ] ) ? trim( $_REQUEST[ $key ] ) : $_REQUEST[ $key ];
258
+ }
259
+
260
+ return $value;
261
+ }
262
+
263
+
264
+ /**
265
+ * Get the count of notices added, either for all notices (default) or for one
266
+ * particular notice type specified by $notice_type.
267
+ *
268
+ * WC notice functions are not available in the admin
269
+ *
270
+ * @since 3.0.2
271
+ * @param string $notice_type The name of the notice type - either error, success or notice. [optional]
272
+ * @return int
273
+ */
274
+ public static function wc_notice_count( $notice_type = '' ) {
275
+
276
+ if ( function_exists( 'wc_notice_count' ) ) {
277
+ return wc_notice_count( $notice_type );
278
+ }
279
+
280
+ return 0;
281
+ }
282
+
283
+
284
+ /**
285
+ * Add and store a notice.
286
+ *
287
+ * WC notice functions are not available in the admin
288
+ *
289
+ * @since 3.0.2
290
+ * @param string $message The text to display in the notice.
291
+ * @param string $notice_type The singular name of the notice type - either error, success or notice. [optional]
292
+ */
293
+ public static function wc_add_notice( $message, $notice_type = 'success' ) {
294
+
295
+ if ( function_exists( 'wc_add_notice' ) ) {
296
+ wc_add_notice( $message, $notice_type );
297
+ }
298
+ }
299
+
300
+
301
+ /**
302
+ * Print a single notice immediately
303
+ *
304
+ * WC notice functions are not available in the admin
305
+ *
306
+ * @since 3.0.2
307
+ * @param string $message The text to display in the notice.
308
+ * @param string $notice_type The singular name of the notice type - either error, success or notice. [optional]
309
+ */
310
+ public static function wc_print_notice( $message, $notice_type = 'success' ) {
311
+
312
+ if ( function_exists( 'wc_print_notice' ) ) {
313
+ wc_print_notice( $message, $notice_type );
314
+ }
315
+ }
316
+
317
+
318
+ /**
319
+ * Gets the current WordPress site name.
320
+ *
321
+ * This is helpful for retrieving the actual site name instead of the
322
+ * network name on multisite installations.
323
+ *
324
+ * @since 4.6.0
325
+ * @return string
326
+ */
327
+ public static function get_site_name() {
328
+
329
+ return ( is_multisite() ) ? get_blog_details()->blogname : get_bloginfo( 'name' );
330
+ }
331
+
332
+
333
+ /** Misc functions ****************************************************/
334
+
335
+
336
+ /**
337
+ * Gets the WordPress current screen.
338
+ *
339
+ * @see get_current_screen() replacement which is always available, unlike the WordPress core function
340
+ *
341
+ * @since 5.4.2
342
+ *
343
+ * @return \WP_Screen|null
344
+ */
345
+ public static function get_current_screen() {
346
+ global $current_screen;
347
+
348
+ return $current_screen ?: null;
349
+ }
350
+
351
+
352
+ /**
353
+ * Checks if the current screen matches a specified ID.
354
+ *
355
+ * This helps avoiding using the get_current_screen() function which is not always available,
356
+ * or setting the substitute global $current_screen every time a check needs to be performed.
357
+ *
358
+ * @since 5.4.2
359
+ *
360
+ * @param string $id id (or property) to compare
361
+ * @param string $prop optional property to compare, defaults to screen id
362
+ * @return bool
363
+ */
364
+ public static function is_current_screen( $id, $prop = 'id' ) {
365
+ global $current_screen;
366
+
367
+ return isset( $current_screen->$prop ) && $id === $current_screen->$prop;
368
+ }
369
+
370
+
371
+ /**
372
+ * Determines if the current request is for a WC REST API endpoint.
373
+ *
374
+ * @see \WooCommerce::is_rest_api_request()
375
+ *
376
+ * @since 5.9.0
377
+ *
378
+ * @return bool
379
+ */
380
+ public static function is_rest_api_request() {
381
+
382
+ if ( is_callable( 'WC' ) && is_callable( [ WC(), 'is_rest_api_request' ] ) ) {
383
+ return (bool) WC()->is_rest_api_request();
384
+ }
385
+
386
+ if ( empty( $_SERVER['REQUEST_URI'] ) || ! function_exists( 'rest_get_url_prefix' ) ) {
387
+ return false;
388
+ }
389
+
390
+ $rest_prefix = trailingslashit( rest_get_url_prefix() );
391
+ $is_rest_api_request = false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix );
392
+
393
+ /* applies WooCommerce core filter */
394
+ return (bool) apply_filters( 'woocommerce_is_rest_api_request', $is_rest_api_request );
395
+ }
396
+
397
+
398
+ /**
399
+ * Triggers a PHP error.
400
+ *
401
+ * This wrapper method ensures AJAX isn't broken in the process.
402
+ *
403
+ * @since 4.6.0
404
+ * @param string $message the error message
405
+ * @param int $type Optional. The error type. Defaults to E_USER_NOTICE
406
+ */
407
+ public static function trigger_error( $message, $type = E_USER_NOTICE ) {
408
+
409
+ if ( is_callable( 'is_ajax' ) && is_ajax() ) {
410
+
411
+ switch ( $type ) {
412
+
413
+ case E_USER_NOTICE:
414
+ $prefix = 'Notice: ';
415
+ break;
416
+
417
+ case E_USER_WARNING:
418
+ $prefix = 'Warning: ';
419
+ break;
420
+
421
+ default:
422
+ $prefix = '';
423
+ }
424
+
425
+ error_log( $prefix . $message );
426
+
427
+ } else {
428
+
429
+ trigger_error( $message, $type );
430
+ }
431
+ }
432
+
433
+
434
+ }
{vendor/skyverge/wc-plugin-framework/woocommerce → includes/Framework}/Lifecycle.php RENAMED
@@ -1,71 +1,38 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Plugin/Classes
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0\Plugin;
26
-
27
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\Admin\Notes_Helper;
28
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Payment_Gateway_Plugin;
29
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Plugin;
30
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Plugin_Compatibility;
31
 
32
  defined( 'ABSPATH' ) or exit;
33
 
34
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\Plugin\\Lifecycle' ) ) :
35
-
36
-
37
  /**
38
  * Plugin lifecycle handler.
39
  *
40
  * Registers and displays milestone notice prompts and eventually the plugin
41
  * install, upgrade, activation, and deactivation routines.
42
- *
43
- * @since 5.1.0
44
  */
45
  class Lifecycle {
46
-
47
-
48
  /** @var array the version numbers that have an upgrade routine */
49
  protected $upgrade_versions = [];
50
 
51
  /** @var string minimum milestone version */
52
  private $milestone_version;
53
 
54
- /** @var SV_WC_Plugin plugin instance */
55
  private $plugin;
56
 
57
-
58
  /**
59
  * Constructs the class.
60
  *
61
  * @since 5.1.0
62
  *
63
- * @param SV_WC_Plugin $plugin plugin instance
64
  */
65
- public function __construct( SV_WC_Plugin $plugin ) {
66
-
67
  $this->plugin = $plugin;
68
-
69
  $this->add_hooks();
70
  }
71
 
@@ -76,22 +43,16 @@ class Lifecycle {
76
  * @since 5.1.0
77
  */
78
  protected function add_hooks() {
79
-
80
  // handle activation
81
  add_action( 'admin_init', array( $this, 'handle_activation' ) );
82
-
83
  // handle deactivation
84
  add_action( 'deactivate_' . $this->get_plugin()->get_plugin_file(), array( $this, 'handle_deactivation' ) );
85
-
86
  if ( is_admin() && ! is_ajax() ) {
87
-
88
  // initialize the plugin lifecycle
89
  add_action( 'wp_loaded', array( $this, 'init' ) );
90
-
91
  // add the admin notices
92
  add_action( 'init', array( $this, 'add_admin_notices' ) );
93
  }
94
-
95
  // catch any milestones triggered by action
96
  add_action( 'wc_' . $this->get_plugin()->get_id() . '_milestone_reached', array( $this, 'trigger_milestone' ), 10, 3 );
97
  }
@@ -103,42 +64,29 @@ class Lifecycle {
103
  * @since 5.2.0
104
  */
105
  public function init() {
106
-
107
  // potentially handle a new activation
108
  $this->handle_activation();
109
-
110
  $installed_version = $this->get_installed_version();
111
  $plugin_version = $this->get_plugin()->get_version();
112
-
113
  // installed version lower than plugin version?
114
  if ( version_compare( $installed_version, $plugin_version, '<' ) ) {
115
-
116
  if ( ! $installed_version ) {
117
-
118
- $this->install();
119
-
120
  // store the upgrade event regardless if there was a routine for it
121
  $this->store_event( 'install' );
122
-
123
  /**
124
  * Fires after the plugin has been installed.
125
  *
126
  * @since 5.1.0
127
  */
128
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_installed' );
129
-
130
  } else {
131
-
132
  $this->upgrade( $installed_version );
133
-
134
  // store the upgrade event regardless if there was a routine for it
135
  $this->add_upgrade_event( $installed_version );
136
-
137
  // if the plugin never had any previous milestones, consider them all reached so their notices aren't displayed
138
  if ( ! $this->get_milestone_version() ) {
139
  $this->set_milestone_version( $plugin_version );
140
  }
141
-
142
  /**
143
  * Fires after the plugin has been updated.
144
  *
@@ -148,7 +96,6 @@ class Lifecycle {
148
  */
149
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_updated', $installed_version );
150
  }
151
-
152
  // new version number
153
  $this->set_installed_version( $plugin_version );
154
  }
@@ -169,18 +116,13 @@ class Lifecycle {
169
  * @since 5.2.0
170
  */
171
  public function handle_activation() {
172
-
173
  if ( ! get_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active', false ) ) {
174
-
175
- $this->activate();
176
-
177
  /**
178
  * Fires when the plugin is activated.
179
  *
180
  * @since 5.2.0
181
  */
182
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_activated' );
183
-
184
  update_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active', 'yes' );
185
  }
186
  }
@@ -194,64 +136,16 @@ class Lifecycle {
194
  * @since 5.2.0
195
  */
196
  public function handle_deactivation() {
197
-
198
- // if the enhanced admin is available, delete all of this plugin's notes on deactivation
199
- if ( SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
200
-
201
- Notes_Helper::delete_notes_with_source( $this->get_plugin()->get_id_dasherized() );
202
-
203
- // if this is a gateway plugin, also delete the plugin's individual gateway notes
204
- if ( $this->get_plugin() instanceof SV_WC_Payment_Gateway_Plugin ) {
205
-
206
- foreach ( $this->get_plugin()->get_gateways() as $gateway ) {
207
- Notes_Helper::delete_notes_with_source( $gateway->get_id_dasherized() );
208
- }
209
- }
210
- }
211
-
212
- $this->deactivate();
213
-
214
  /**
215
  * Fires when the plugin is deactivated.
216
  *
217
  * @since 5.2.0
218
  */
219
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_deactivated' );
220
-
221
  delete_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active' );
222
  }
223
 
224
 
225
- /**
226
- * Handles plugin activation.
227
- *
228
- * Plugins can override this to run their own activation tasks.
229
- *
230
- * Important Note: operations here should never be destructive for existing
231
- * data. Since we rely on an option to track activation, it's possible for
232
- * this to run outside of genuine activations.
233
- *
234
- * @since 5.2.0
235
- */
236
- public function activate() {
237
-
238
- // stub
239
- }
240
-
241
-
242
- /**
243
- * Handles plugin deactivation.
244
- *
245
- * Plugins can override this to run their own deactivation tasks.
246
- *
247
- * @since 5.2.0
248
- */
249
- public function deactivate() {
250
-
251
- // stub
252
- }
253
-
254
-
255
  /**
256
  * Helper method to install default settings for a plugin.
257
  *
@@ -260,9 +154,7 @@ class Lifecycle {
260
  * @param array $settings settings in format required by WC_Admin_Settings
261
  */
262
  public function install_default_settings( array $settings ) {
263
-
264
  foreach ( $settings as $setting ) {
265
-
266
  if ( isset( $setting['id'], $setting['default'] ) ) {
267
  update_option( $setting['id'], $setting['default'] );
268
  }
@@ -270,17 +162,6 @@ class Lifecycle {
270
  }
271
 
272
 
273
- /**
274
- * Performs any install tasks.
275
- *
276
- * @since 5.2.0
277
- */
278
- protected function install() {
279
-
280
- // stub
281
- }
282
-
283
-
284
  /**
285
  * Performs any upgrade tasks based on the provided installed version.
286
  *
@@ -289,17 +170,11 @@ class Lifecycle {
289
  * @param string $installed_version installed version
290
  */
291
  protected function upgrade( $installed_version ) {
292
-
293
  foreach ( $this->upgrade_versions as $upgrade_version ) {
294
-
295
  $upgrade_method = 'upgrade_to_' . str_replace( array( '.', '-' ), '_', $upgrade_version );
296
-
297
  if ( version_compare( $installed_version, $upgrade_version, '<' ) && is_callable( array( $this, $upgrade_method ) ) ) {
298
-
299
  $this->get_plugin()->log( "Starting upgrade to v{$upgrade_version}" );
300
-
301
  $this->$upgrade_method( $installed_version );
302
-
303
  $this->get_plugin()->log( "Upgrade to v{$upgrade_version} complete" );
304
  }
305
  }
@@ -312,15 +187,12 @@ class Lifecycle {
312
  * @since 5.1.0
313
  */
314
  public function add_admin_notices() {
315
-
316
  // display any milestone notices
317
  foreach ( $this->get_milestone_messages() as $id => $message ) {
318
-
319
  // bail if this notice was already dismissed
320
  if ( ! $this->get_plugin()->get_admin_notice_handler()->should_display_notice( $id ) ) {
321
  continue;
322
  }
323
-
324
  /**
325
  * Filters a milestone notice message.
326
  *
@@ -329,14 +201,14 @@ class Lifecycle {
329
  * @param string $message message text to be used for the milestone notice
330
  * @param string $id milestone ID
331
  */
332
- $message = apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_milestone_message', $this->generate_milestone_notice_message( $message ), $id );
333
-
 
 
334
  if ( $message ) {
335
-
336
- $this->get_plugin()->get_admin_notice_handler()->add_admin_notice( $message, $id, array(
337
- 'always_show_on_settings' => false,
338
- ) );
339
-
340
  // only display one notice at a time
341
  break;
342
  }
@@ -365,12 +237,10 @@ class Lifecycle {
365
  * @return bool
366
  */
367
  public function trigger_milestone( $id, $message, $since = '1.0.0' ) {
368
-
369
  // if the plugin was had milestones before this milestone was added, don't trigger it
370
  if ( version_compare( $this->get_milestone_version(), $since, '>' ) ) {
371
  return false;
372
  }
373
-
374
  return $this->register_milestone_message( $id, $message );
375
  }
376
 
@@ -384,31 +254,25 @@ class Lifecycle {
384
  * @return string
385
  */
386
  protected function generate_milestone_notice_message( $custom_message ) {
387
-
388
  $message = '';
389
-
390
  if ( $this->get_plugin()->get_reviews_url() ) {
391
-
392
  // to be prepended at random to each milestone notice
393
  $exclamations = array(
394
- __( 'Awesome', 'woocommerce-plugin-framework' ),
395
- __( 'Fantastic', 'woocommerce-plugin-framework' ),
396
- __( 'Cowabunga', 'woocommerce-plugin-framework' ),
397
- __( 'Congratulations', 'woocommerce-plugin-framework' ),
398
- __( 'Hot dog', 'woocommerce-plugin-framework' ),
399
  );
400
-
401
  $message = $exclamations[ array_rand( $exclamations ) ] . ', ' . esc_html( $custom_message ) . ' ';
402
-
403
  $message .= sprintf(
404
  /* translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a> tag, %4$s - <a> tag, %5$s - </a> tag */
405
- __( 'Are you having a great experience with %1$s so far? Please consider %2$sleaving a review%3$s! If things aren\'t going quite as expected, we\'re happy to help -- please %4$sreach out to our support team%5$s.', 'woocommerce-plugin-framework' ),
406
  '<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>',
407
  '<a href="' . esc_url( $this->get_plugin()->get_reviews_url() ) . '">', '</a>',
408
  '<a href="' . esc_url( $this->get_plugin()->get_support_url() ) . '">', '</a>'
409
  );
410
  }
411
-
412
  return $message;
413
  }
414
 
@@ -424,20 +288,15 @@ class Lifecycle {
424
  * @return bool whether the message was successfully registered
425
  */
426
  public function register_milestone_message( $id, $message ) {
427
-
428
  $milestone_messages = $this->get_milestone_messages();
429
  $dismissed_notices = array_keys( $this->get_plugin()->get_admin_notice_handler()->get_dismissed_notices() );
430
-
431
  // get the total number of dismissed milestone messages
432
  $dismissed_milestone_messages = array_intersect( array_keys( $milestone_messages ), $dismissed_notices );
433
-
434
  // if the user has dismissed more than three milestone messages already, don't add any more
435
  if ( count( $dismissed_milestone_messages ) > 3 ) {
436
  return false;
437
  }
438
-
439
  $milestone_messages[ $id ] = $message;
440
-
441
  return update_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_messages', $milestone_messages );
442
  }
443
 
@@ -454,12 +313,10 @@ class Lifecycle {
454
  * @param array $data extra data to add
455
  * @return false|int
456
  */
457
- public function add_upgrade_event( $from_version, array $data = array() ) {
458
-
459
  $data = array_merge( array(
460
  'from_version' => $from_version,
461
  ), $data );
462
-
463
  return $this->store_event( 'upgrade', $data );
464
  }
465
 
@@ -474,13 +331,11 @@ class Lifecycle {
474
  * @param array $data extra data to add
475
  * @return false|int
476
  */
477
- public function add_migrate_event( $from_plugin, $from_version = '', array $data = array() ) {
478
-
479
  $data = array_merge( array(
480
  'from_plugin' => $from_plugin,
481
  'from_version' => $from_version,
482
  ), $data );
483
-
484
  return $this->store_event( 'migrate', $data );
485
  }
486
 
@@ -498,26 +353,20 @@ class Lifecycle {
498
  * @param array $data any extra data to store
499
  * @return false|int
500
  */
501
- public function store_event( $name, array $data = array() ) {
502
  global $wpdb;
503
-
504
  $history = $this->get_event_history();
505
-
506
  $event = array(
507
  'name' => wc_clean( $name ),
508
  'time' => (int) current_time( 'timestamp' ),
509
  'version' => wc_clean( $this->get_plugin()->get_version() ),
510
  );
511
-
512
  if ( ! empty( $data ) ) {
513
  $event['data'] = wc_clean( $data );
514
  }
515
-
516
  array_unshift( $history, $event );
517
-
518
  // limit to the last 30 events
519
  $history = array_slice( $history, 0, 29 );
520
-
521
  return $wpdb->replace(
522
  $wpdb->options,
523
  array(
@@ -544,20 +393,16 @@ class Lifecycle {
544
  */
545
  public function get_event_history() {
546
  global $wpdb;
547
-
548
- $history = array();
549
-
550
  $results = $wpdb->get_var( $wpdb->prepare( "
551
  SELECT option_value
552
  FROM {$wpdb->options}
553
  WHERE option_name = %s
554
  ", $this->get_event_history_option_name() ) );
555
-
556
  if ( $results ) {
557
  $history = json_decode( $results, true );
558
  }
559
-
560
- return is_array( $history ) ? $history : array();
561
  }
562
 
563
 
@@ -569,7 +414,6 @@ class Lifecycle {
569
  * @return string
570
  */
571
  protected function get_event_history_option_name() {
572
-
573
  return 'wc_' . $this->get_plugin()->get_id() . '_lifecycle_events';
574
  }
575
 
@@ -585,8 +429,7 @@ class Lifecycle {
585
  * @return array
586
  */
587
  protected function get_milestone_messages() {
588
-
589
- return get_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_messages', array() );
590
  }
591
 
592
 
@@ -599,9 +442,7 @@ class Lifecycle {
599
  * @return bool
600
  */
601
  public function set_milestone_version( $version ) {
602
-
603
  $this->milestone_version = $version;
604
-
605
  return update_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_version', $version );
606
  }
607
 
@@ -614,11 +455,9 @@ class Lifecycle {
614
  * @return string
615
  */
616
  public function get_milestone_version() {
617
-
618
  if ( ! $this->milestone_version ) {
619
  $this->milestone_version = get_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_version', '' );
620
  }
621
-
622
  return $this->milestone_version;
623
  }
624
 
@@ -631,7 +470,6 @@ class Lifecycle {
631
  * @return string
632
  */
633
  protected function get_installed_version() {
634
-
635
  return get_option( $this->get_plugin()->get_plugin_version_name() );
636
  }
637
 
@@ -644,7 +482,6 @@ class Lifecycle {
644
  * @param string $version version to set
645
  */
646
  protected function set_installed_version( $version ) {
647
-
648
  update_option( $this->get_plugin()->get_plugin_version_name(), $version );
649
  }
650
 
@@ -652,33 +489,9 @@ class Lifecycle {
652
  /**
653
  * Gets the plugin instance.
654
  *
655
- * @since 5.1.0
656
- *
657
- * @return SV_WC_Plugin|SV_WC_Payment_Gateway_Plugin
658
  */
659
  protected function get_plugin() {
660
-
661
  return $this->plugin;
662
  }
663
-
664
-
665
- /** Deprecated methods ****************************************************/
666
-
667
-
668
- /**
669
- * Handles tasks after the plugin has been updated.
670
- *
671
- * @internal
672
- *
673
- * @since 5.1.0
674
- */
675
- public function do_update() {
676
-
677
- wc_deprecated_function( __METHOD__, '5.2.0' );
678
- }
679
-
680
-
681
  }
682
-
683
-
684
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework;
 
 
 
 
 
8
 
9
  defined( 'ABSPATH' ) or exit;
10
 
 
 
 
11
  /**
12
  * Plugin lifecycle handler.
13
  *
14
  * Registers and displays milestone notice prompts and eventually the plugin
15
  * install, upgrade, activation, and deactivation routines.
 
 
16
  */
17
  class Lifecycle {
 
 
18
  /** @var array the version numbers that have an upgrade routine */
19
  protected $upgrade_versions = [];
20
 
21
  /** @var string minimum milestone version */
22
  private $milestone_version;
23
 
24
+ /** @var Plugin plugin instance */
25
  private $plugin;
26
 
 
27
  /**
28
  * Constructs the class.
29
  *
30
  * @since 5.1.0
31
  *
32
+ * @param Plugin $plugin plugin instance
33
  */
34
+ public function __construct( Plugin $plugin ) {
 
35
  $this->plugin = $plugin;
 
36
  $this->add_hooks();
37
  }
38
 
43
  * @since 5.1.0
44
  */
45
  protected function add_hooks() {
 
46
  // handle activation
47
  add_action( 'admin_init', array( $this, 'handle_activation' ) );
 
48
  // handle deactivation
49
  add_action( 'deactivate_' . $this->get_plugin()->get_plugin_file(), array( $this, 'handle_deactivation' ) );
 
50
  if ( is_admin() && ! is_ajax() ) {
 
51
  // initialize the plugin lifecycle
52
  add_action( 'wp_loaded', array( $this, 'init' ) );
 
53
  // add the admin notices
54
  add_action( 'init', array( $this, 'add_admin_notices' ) );
55
  }
 
56
  // catch any milestones triggered by action
57
  add_action( 'wc_' . $this->get_plugin()->get_id() . '_milestone_reached', array( $this, 'trigger_milestone' ), 10, 3 );
58
  }
64
  * @since 5.2.0
65
  */
66
  public function init() {
 
67
  // potentially handle a new activation
68
  $this->handle_activation();
 
69
  $installed_version = $this->get_installed_version();
70
  $plugin_version = $this->get_plugin()->get_version();
 
71
  // installed version lower than plugin version?
72
  if ( version_compare( $installed_version, $plugin_version, '<' ) ) {
 
73
  if ( ! $installed_version ) {
 
 
 
74
  // store the upgrade event regardless if there was a routine for it
75
  $this->store_event( 'install' );
 
76
  /**
77
  * Fires after the plugin has been installed.
78
  *
79
  * @since 5.1.0
80
  */
81
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_installed' );
 
82
  } else {
 
83
  $this->upgrade( $installed_version );
 
84
  // store the upgrade event regardless if there was a routine for it
85
  $this->add_upgrade_event( $installed_version );
 
86
  // if the plugin never had any previous milestones, consider them all reached so their notices aren't displayed
87
  if ( ! $this->get_milestone_version() ) {
88
  $this->set_milestone_version( $plugin_version );
89
  }
 
90
  /**
91
  * Fires after the plugin has been updated.
92
  *
96
  */
97
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_updated', $installed_version );
98
  }
 
99
  // new version number
100
  $this->set_installed_version( $plugin_version );
101
  }
116
  * @since 5.2.0
117
  */
118
  public function handle_activation() {
 
119
  if ( ! get_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active', false ) ) {
 
 
 
120
  /**
121
  * Fires when the plugin is activated.
122
  *
123
  * @since 5.2.0
124
  */
125
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_activated' );
 
126
  update_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active', 'yes' );
127
  }
128
  }
136
  * @since 5.2.0
137
  */
138
  public function handle_deactivation() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  /**
140
  * Fires when the plugin is deactivated.
141
  *
142
  * @since 5.2.0
143
  */
144
  do_action( 'wc_' . $this->get_plugin()->get_id() . '_deactivated' );
 
145
  delete_option( 'wc_' . $this->get_plugin()->get_id() . '_is_active' );
146
  }
147
 
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  /**
150
  * Helper method to install default settings for a plugin.
151
  *
154
  * @param array $settings settings in format required by WC_Admin_Settings
155
  */
156
  public function install_default_settings( array $settings ) {
 
157
  foreach ( $settings as $setting ) {
 
158
  if ( isset( $setting['id'], $setting['default'] ) ) {
159
  update_option( $setting['id'], $setting['default'] );
160
  }
162
  }
163
 
164
 
 
 
 
 
 
 
 
 
 
 
 
165
  /**
166
  * Performs any upgrade tasks based on the provided installed version.
167
  *
170
  * @param string $installed_version installed version
171
  */
172
  protected function upgrade( $installed_version ) {
 
173
  foreach ( $this->upgrade_versions as $upgrade_version ) {
 
174
  $upgrade_method = 'upgrade_to_' . str_replace( array( '.', '-' ), '_', $upgrade_version );
 
175
  if ( version_compare( $installed_version, $upgrade_version, '<' ) && is_callable( array( $this, $upgrade_method ) ) ) {
 
176
  $this->get_plugin()->log( "Starting upgrade to v{$upgrade_version}" );
 
177
  $this->$upgrade_method( $installed_version );
 
178
  $this->get_plugin()->log( "Upgrade to v{$upgrade_version} complete" );
179
  }
180
  }
187
  * @since 5.1.0
188
  */
189
  public function add_admin_notices() {
 
190
  // display any milestone notices
191
  foreach ( $this->get_milestone_messages() as $id => $message ) {
 
192
  // bail if this notice was already dismissed
193
  if ( ! $this->get_plugin()->get_admin_notice_handler()->should_display_notice( $id ) ) {
194
  continue;
195
  }
 
196
  /**
197
  * Filters a milestone notice message.
198
  *
201
  * @param string $message message text to be used for the milestone notice
202
  * @param string $id milestone ID
203
  */
204
+ $message = apply_filters(
205
+ 'wc_' . $this->get_plugin()->get_id() . '_milestone_message',
206
+ $this->generate_milestone_notice_message( $message ), $id
207
+ );
208
  if ( $message ) {
209
+ $this->get_plugin()
210
+ ->get_admin_notice_handler()
211
+ ->add_admin_notice( $message, $id, array( 'always_show_on_settings' => false, ) );
 
 
212
  // only display one notice at a time
213
  break;
214
  }
237
  * @return bool
238
  */
239
  public function trigger_milestone( $id, $message, $since = '1.0.0' ) {
 
240
  // if the plugin was had milestones before this milestone was added, don't trigger it
241
  if ( version_compare( $this->get_milestone_version(), $since, '>' ) ) {
242
  return false;
243
  }
 
244
  return $this->register_milestone_message( $id, $message );
245
  }
246
 
254
  * @return string
255
  */
256
  protected function generate_milestone_notice_message( $custom_message ) {
 
257
  $message = '';
 
258
  if ( $this->get_plugin()->get_reviews_url() ) {
 
259
  // to be prepended at random to each milestone notice
260
  $exclamations = array(
261
+ __( 'Awesome', 'facebook-for-woocommerce' ),
262
+ __( 'Fantastic', 'facebook-for-woocommerce' ),
263
+ __( 'Cowabunga', 'facebook-for-woocommerce' ),
264
+ __( 'Congratulations', 'facebook-for-woocommerce' ),
265
+ __( 'Hot dog', 'facebook-for-woocommerce' ),
266
  );
 
267
  $message = $exclamations[ array_rand( $exclamations ) ] . ', ' . esc_html( $custom_message ) . ' ';
 
268
  $message .= sprintf(
269
  /* translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a> tag, %4$s - <a> tag, %5$s - </a> tag */
270
+ __( 'Are you having a great experience with %1$s so far? Please consider %2$sleaving a review%3$s! If things aren\'t going quite as expected, we\'re happy to help -- please %4$sreach out to our support team%5$s.', 'facebook-for-woocommerce' ),
271
  '<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>',
272
  '<a href="' . esc_url( $this->get_plugin()->get_reviews_url() ) . '">', '</a>',
273
  '<a href="' . esc_url( $this->get_plugin()->get_support_url() ) . '">', '</a>'
274
  );
275
  }
 
276
  return $message;
277
  }
278
 
288
  * @return bool whether the message was successfully registered
289
  */
290
  public function register_milestone_message( $id, $message ) {
 
291
  $milestone_messages = $this->get_milestone_messages();
292
  $dismissed_notices = array_keys( $this->get_plugin()->get_admin_notice_handler()->get_dismissed_notices() );
 
293
  // get the total number of dismissed milestone messages
294
  $dismissed_milestone_messages = array_intersect( array_keys( $milestone_messages ), $dismissed_notices );
 
295
  // if the user has dismissed more than three milestone messages already, don't add any more
296
  if ( count( $dismissed_milestone_messages ) > 3 ) {
297
  return false;
298
  }
 
299
  $milestone_messages[ $id ] = $message;
 
300
  return update_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_messages', $milestone_messages );
301
  }
302
 
313
  * @param array $data extra data to add
314
  * @return false|int
315
  */
316
+ public function add_upgrade_event( $from_version, array $data = [] ) {
 
317
  $data = array_merge( array(
318
  'from_version' => $from_version,
319
  ), $data );
 
320
  return $this->store_event( 'upgrade', $data );
321
  }
322
 
331
  * @param array $data extra data to add
332
  * @return false|int
333
  */
334
+ public function add_migrate_event( $from_plugin, $from_version = '', array $data = [] ) {
 
335
  $data = array_merge( array(
336
  'from_plugin' => $from_plugin,
337
  'from_version' => $from_version,
338
  ), $data );
 
339
  return $this->store_event( 'migrate', $data );
340
  }
341
 
353
  * @param array $data any extra data to store
354
  * @return false|int
355
  */
356
+ public function store_event( $name, array $data = [] ) {
357
  global $wpdb;
 
358
  $history = $this->get_event_history();
 
359
  $event = array(
360
  'name' => wc_clean( $name ),
361
  'time' => (int) current_time( 'timestamp' ),
362
  'version' => wc_clean( $this->get_plugin()->get_version() ),
363
  );
 
364
  if ( ! empty( $data ) ) {
365
  $event['data'] = wc_clean( $data );
366
  }
 
367
  array_unshift( $history, $event );
 
368
  // limit to the last 30 events
369
  $history = array_slice( $history, 0, 29 );
 
370
  return $wpdb->replace(
371
  $wpdb->options,
372
  array(
393
  */
394
  public function get_event_history() {
395
  global $wpdb;
396
+ $history = [];
 
 
397
  $results = $wpdb->get_var( $wpdb->prepare( "
398
  SELECT option_value
399
  FROM {$wpdb->options}
400
  WHERE option_name = %s
401
  ", $this->get_event_history_option_name() ) );
 
402
  if ( $results ) {
403
  $history = json_decode( $results, true );
404
  }
405
+ return is_array( $history ) ? $history : [];
 
406
  }
407
 
408
 
414
  * @return string
415
  */
416
  protected function get_event_history_option_name() {
 
417
  return 'wc_' . $this->get_plugin()->get_id() . '_lifecycle_events';
418
  }
419
 
429
  * @return array
430
  */
431
  protected function get_milestone_messages() {
432
+ return get_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_messages', [] );
 
433
  }
434
 
435
 
442
  * @return bool
443
  */
444
  public function set_milestone_version( $version ) {
 
445
  $this->milestone_version = $version;
 
446
  return update_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_version', $version );
447
  }
448
 
455
  * @return string
456
  */
457
  public function get_milestone_version() {
 
458
  if ( ! $this->milestone_version ) {
459
  $this->milestone_version = get_option( 'wc_' . $this->get_plugin()->get_id() . '_milestone_version', '' );
460
  }
 
461
  return $this->milestone_version;
462
  }
463
 
470
  * @return string
471
  */
472
  protected function get_installed_version() {
 
473
  return get_option( $this->get_plugin()->get_plugin_version_name() );
474
  }
475
 
482
  * @param string $version version to set
483
  */
484
  protected function set_installed_version( $version ) {
 
485
  update_option( $this->get_plugin()->get_plugin_version_name(), $version );
486
  }
487
 
489
  /**
490
  * Gets the plugin instance.
491
  *
492
+ * @return Plugin
 
 
493
  */
494
  protected function get_plugin() {
 
495
  return $this->plugin;
496
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  }
 
 
 
vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin.php → includes/Framework/Plugin.php RENAMED
@@ -1,33 +1,16 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Plugin/Classes
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
26
 
27
- defined( 'ABSPATH' ) or exit;
28
-
29
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_Plugin' ) ) :
30
 
 
31
 
32
  /**
33
  * # WooCommerce Plugin Framework
@@ -39,11 +22,7 @@ if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_P
39
  *
40
  * @version 5.8.0
41
  */
42
- abstract class SV_WC_Plugin {
43
-
44
-
45
- /** Plugin Framework Version */
46
- const VERSION = '5.10.0';
47
 
48
  /** @var object single instance of plugin */
49
  protected static $instance;
@@ -63,10 +42,10 @@ abstract class SV_WC_Plugin {
63
  /** @var string template path, without trailing slash */
64
  private $template_path;
65
 
66
- /** @var \WC_Logger instance */
67
  private $logger;
68
 
69
- /** @var SV_WP_Admin_Message_Handler instance */
70
  private $message_handler;
71
 
72
  /** @var string the plugin text domain */
@@ -78,22 +57,13 @@ abstract class SV_WC_Plugin {
78
  /** @var int|float minimum supported WooCommerce versions before the latest (units for major releases, decimals for minor) */
79
  private $min_wc_semver;
80
 
81
- /** @var SV_WC_Plugin_Dependencies dependency handler instance */
82
  private $dependency_handler;
83
 
84
- /** @var SV_WC_Hook_Deprecator hook deprecator instance */
85
- private $hook_deprecator;
86
-
87
- /** @var Plugin\Lifecycle lifecycle handler instance */
88
  protected $lifecycle_handler;
89
 
90
- /** @var REST_API REST API handler instance */
91
- protected $rest_api_handler;
92
-
93
- /** @var Admin\Setup_Wizard handler instance */
94
- protected $setup_wizard_handler;
95
-
96
- /** @var SV_WC_Admin_Notice_Handler the admin notice handler class */
97
  private $admin_notice_handler;
98
 
99
 
@@ -121,7 +91,6 @@ abstract class SV_WC_Plugin {
121
  * }
122
  */
123
  public function __construct( $id, $version, $args = [] ) {
124
-
125
  // required params
126
  $this->id = $id;
127
  $this->version = $version;
@@ -135,9 +104,6 @@ abstract class SV_WC_Plugin {
135
  $this->min_wc_semver = is_numeric( $args['min_wc_semver'] ) ? abs( $args['min_wc_semver'] ) : null;
136
  $this->text_domain = $args['text_domain'];
137
 
138
- // includes that are required to be available at all times
139
- $this->includes();
140
-
141
  // initialize the dependencies manager
142
  $this->init_dependencies( $args['dependencies'] );
143
 
@@ -147,18 +113,9 @@ abstract class SV_WC_Plugin {
147
  // build the admin notice handler instance
148
  $this->init_admin_notice_handler();
149
 
150
- // build the hook deprecator instance
151
- $this->init_hook_deprecator();
152
-
153
  // build the lifecycle handler instance
154
  $this->init_lifecycle_handler();
155
 
156
- // build the REST API handler instance
157
- $this->init_rest_api_handler();
158
-
159
- // build the setup handler instance
160
- $this->init_setup_wizard_handler();
161
-
162
  // add the action & filter hooks
163
  $this->add_hooks();
164
  }
@@ -181,8 +138,7 @@ abstract class SV_WC_Plugin {
181
  * }
182
  */
183
  protected function init_dependencies( $dependencies ) {
184
-
185
- $this->dependency_handler = new SV_WC_Plugin_Dependencies( $this, $dependencies );
186
  }
187
 
188
 
@@ -194,8 +150,7 @@ abstract class SV_WC_Plugin {
194
  * @since 5.2.0
195
  */
196
  protected function init_admin_message_handler() {
197
-
198
- $this->message_handler = new SV_WP_Admin_Message_Handler( $this->get_id() );
199
  }
200
 
201
 
@@ -207,21 +162,7 @@ abstract class SV_WC_Plugin {
207
  * @since 5.2.0
208
  */
209
  protected function init_admin_notice_handler() {
210
-
211
- $this->admin_notice_handler = new SV_WC_Admin_Notice_Handler( $this );
212
- }
213
-
214
-
215
- /**
216
- * Builds the hook deprecator instance.
217
- *
218
- * Plugins can override this with their own handler.
219
- *
220
- * @since 5.2.0
221
- */
222
- protected function init_hook_deprecator() {
223
-
224
- $this->hook_deprecator = new SV_WC_Hook_Deprecator( $this->get_plugin_name(), array_merge( $this->get_framework_deprecated_hooks(), $this->get_deprecated_hooks() ) );
225
  }
226
 
227
 
@@ -230,38 +171,9 @@ abstract class SV_WC_Plugin {
230
  *
231
  * Plugins can override this with their own handler to perform install and
232
  * upgrade routines.
233
- *
234
- * @since 5.2.0
235
  */
236
  protected function init_lifecycle_handler() {
237
-
238
- $this->lifecycle_handler = new Plugin\Lifecycle( $this );
239
- }
240
-
241
-
242
- /**
243
- * Builds the REST API handler instance.
244
- *
245
- * Plugins can override this to add their own data and/or routes.
246
- *
247
- * @since 5.2.0
248
- */
249
- protected function init_rest_api_handler() {
250
-
251
- $this->rest_api_handler = new REST_API( $this );
252
- }
253
-
254
-
255
- /**
256
- * Builds the Setup Wizard handler instance.
257
- *
258
- * Plugins can override and extend this method to add their own setup wizard.
259
- *
260
- * @since 5.3.0
261
- */
262
- protected function init_setup_wizard_handler() {
263
-
264
- require_once( $this->get_framework_path() . '/admin/abstract-sv-wc-plugin-admin-setup-wizard.php' );
265
  }
266
 
267
 
@@ -271,19 +183,11 @@ abstract class SV_WC_Plugin {
271
  * @since 5.2.0
272
  */
273
  private function add_hooks() {
274
-
275
- // initialize the plugin
276
- add_action( 'plugins_loaded', array( $this, 'init_plugin' ), 15 );
277
-
278
- // initialize the plugin admin
279
- add_action( 'admin_init', array( $this, 'init_admin' ), 0 );
280
-
281
  // hook for translations separately to ensure they're loaded
282
  add_action( 'init', array( $this, 'load_translations' ) );
283
 
284
  // add the admin notices
285
  add_action( 'admin_notices', array( $this, 'add_admin_notices' ) );
286
- add_action( 'admin_footer', array( $this, 'add_delayed_admin_notices' ) );
287
 
288
  // add a 'Configure' link to the plugin action links
289
  add_filter( 'plugin_action_links_' . plugin_basename( $this->get_plugin_file() ), array( $this, 'plugin_action_links' ) );
@@ -303,7 +207,7 @@ abstract class SV_WC_Plugin {
303
  */
304
  public function __clone() {
305
  /* translators: Placeholders: %s - plugin name */
306
- _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot clone instances of %s.', 'woocommerce-plugin-framework' ), esc_html( $this->get_plugin_name() ) ), '3.1.0' );
307
  }
308
 
309
 
@@ -314,7 +218,7 @@ abstract class SV_WC_Plugin {
314
  */
315
  public function __wakeup() {
316
  /* translators: Placeholders: %s - plugin name */
317
- _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot unserialize instances of %s.', 'woocommerce-plugin-framework' ), esc_html( $this->get_plugin_name() ) ), '3.1.0' );
318
  }
319
 
320
 
@@ -326,9 +230,7 @@ abstract class SV_WC_Plugin {
326
  * @since 4.2.0
327
  */
328
  public function load_translations() {
329
-
330
  $this->load_framework_textdomain();
331
-
332
  // if this plugin passes along its text domain, load its translation files
333
  if ( $this->text_domain ) {
334
  $this->load_plugin_textdomain();
@@ -342,7 +244,7 @@ abstract class SV_WC_Plugin {
342
  * @since 4.5.0
343
  */
344
  protected function load_framework_textdomain() {
345
- $this->load_textdomain( 'woocommerce-plugin-framework', dirname( plugin_basename( $this->get_framework_file() ) ) );
346
  }
347
 
348
 
@@ -364,7 +266,6 @@ abstract class SV_WC_Plugin {
364
  * @param string $path the i18n path
365
  */
366
  protected function load_textdomain( $textdomain, $path ) {
367
-
368
  // user's locale if in the admin for WP 4.7+, or the site locale otherwise
369
  $locale = is_admin() && is_callable( 'get_user_locale' ) ? get_user_locale() : get_locale();
370
 
@@ -375,162 +276,6 @@ abstract class SV_WC_Plugin {
375
  load_plugin_textdomain( $textdomain, false, untrailingslashit( $path ) . '/i18n/languages' );
376
  }
377
 
378
-
379
- /**
380
- * Initializes the plugin.
381
- *
382
- * Plugins can override this to set up any handlers after WordPress is ready.
383
- *
384
- * @since 5.2.0
385
- */
386
- public function init_plugin() {
387
-
388
- // stub
389
- }
390
-
391
-
392
- /**
393
- * Initializes the plugin admin.
394
- *
395
- * Plugins can override this to set up any handlers after the WordPress admin is ready.
396
- *
397
- * @since 5.2.0
398
- */
399
- public function init_admin() {
400
-
401
- // stub
402
- }
403
-
404
-
405
- /**
406
- * Include any critical files which must be available as early as possible,
407
- *
408
- * @since 2.0.0
409
- */
410
- private function includes() {
411
-
412
- $framework_path = $this->get_framework_path();
413
-
414
- // common exception class
415
- require_once( $framework_path . '/class-sv-wc-plugin-exception.php' );
416
-
417
- // addresses
418
- require_once( $framework_path . '/Addresses/Address.php' );
419
- require_once( $framework_path . '/Addresses/Customer_Address.php' );
420
-
421
- // Settings API
422
- require_once( $framework_path . '/Settings_API/Abstract_Settings.php' );
423
- require_once( $framework_path . '/Settings_API/Setting.php' );
424
- require_once( $framework_path . '/Settings_API/Control.php' );
425
-
426
- // common utility methods
427
- require_once( $framework_path . '/class-sv-wc-helper.php' );
428
- require_once( $framework_path . '/Country_Helper.php' );
429
- require_once( $framework_path . '/admin/Notes_Helper.php' );
430
-
431
- // backwards compatibility for older WC versions
432
- require_once( $framework_path . '/class-sv-wc-plugin-compatibility.php' );
433
- require_once( $framework_path . '/compatibility/abstract-sv-wc-data-compatibility.php' );
434
- require_once( $framework_path . '/compatibility/class-sv-wc-order-compatibility.php' );
435
- require_once( $framework_path . '/compatibility/class-sv-wc-product-compatibility.php' );
436
-
437
- // TODO: Remove this when WC 3.x can be required {CW 2017-03-16}
438
- require_once( $framework_path . '/compatibility/class-sv-wc-datetime.php' );
439
-
440
- // generic API base
441
- require_once( $framework_path . '/api/class-sv-wc-api-exception.php' );
442
- require_once( $framework_path . '/api/class-sv-wc-api-base.php' );
443
- require_once( $framework_path . '/api/interface-sv-wc-api-request.php' );
444
- require_once( $framework_path . '/api/interface-sv-wc-api-response.php' );
445
-
446
- // XML API base
447
- require_once( $framework_path . '/api/abstract-sv-wc-api-xml-request.php' );
448
- require_once( $framework_path . '/api/abstract-sv-wc-api-xml-response.php' );
449
-
450
- // JSON API base
451
- require_once( $framework_path . '/api/abstract-sv-wc-api-json-request.php' );
452
- require_once( $framework_path . '/api/abstract-sv-wc-api-json-response.php' );
453
-
454
- // REST API Controllers
455
- require_once( $framework_path . '/rest-api/Controllers/Settings.php' );
456
-
457
- // Handlers
458
- require_once( $framework_path . '/Handlers/Script_Handler.php' );
459
- require_once( $framework_path . '/class-sv-wc-plugin-dependencies.php' );
460
- require_once( $framework_path . '/class-sv-wc-hook-deprecator.php' );
461
- require_once( $framework_path . '/class-sv-wp-admin-message-handler.php' );
462
- require_once( $framework_path . '/class-sv-wc-admin-notice-handler.php' );
463
- require_once( $framework_path . '/Lifecycle.php' );
464
- require_once( $framework_path . '/rest-api/class-sv-wc-plugin-rest-api.php' );
465
- }
466
-
467
-
468
- /**
469
- * Gets a list of framework deprecated/removed hooks.
470
- *
471
- * @see SV_WC_Plugin::init_hook_deprecator()
472
- * @see SV_WC_Plugin::get_deprecated_hooks()
473
- *
474
- * @since 5.8.0
475
- *
476
- * @return array associative array
477
- */
478
- private function get_framework_deprecated_hooks() {
479
-
480
- $plugin_id = $this->get_id();
481
- $deprecated_hooks = [];
482
- $deprecated_filters = [
483
- /** @see SV_WC_Payment_Gateway_My_Payment_Methods handler - once migrated to WC core tokens UI, we removed these and have no replacement */
484
- // TODO: remove deprecated hooks handling by version 6.0.0 or by 2021-02-25 {FN 2020-02-25}
485
- "wc_{$plugin_id}_my_payment_methods_table_html",
486
- "wc_{$plugin_id}_my_payment_methods_table_head_html",
487
- "wc_{$plugin_id}_my_payment_methods_table_title",
488
- "wc_{$plugin_id}_my_payment_methods_table_title_html",
489
- "wc_{$plugin_id}_my_payment_methods_table_row_html",
490
- "wc_{$plugin_id}_my_payment_methods_table_body_html",
491
- "wc_{$plugin_id}_my_payment_methods_table_body_row_data",
492
- "wc_{$plugin_id}_my_payment_methods_table_method_expiry_html",
493
- "wc_{$plugin_id}_my_payment_methods_table_actions_html",
494
- ];
495
-
496
- foreach ( $deprecated_filters as $deprecated_filter ) {
497
- $deprecated_hooks[ $deprecated_filter ] = [
498
- 'removed' => true,
499
- 'replacement' => false,
500
- 'version' => '5.8.1'
501
- ];
502
- }
503
-
504
- return $deprecated_hooks;
505
- }
506
-
507
-
508
- /**
509
- * Gets a list of the plugin's deprecated/removed hooks.
510
- *
511
- * Implementing classes should override this and return an array of deprecated/removed hooks in the following format:
512
- *
513
- * $old_hook_name = array {
514
- * @type string $version version the hook was deprecated/removed in
515
- * @type bool $removed if present and true, the message will indicate the hook was removed instead of deprecated
516
- * @type string|bool $replacement if present and a string, the message will indicate the replacement hook to use,
517
- * otherwise (if bool and false) the message will indicate there is no replacement available.
518
- * }
519
- *
520
- * @since 4.3.0
521
- *
522
- * @return array
523
- */
524
- protected function get_deprecated_hooks() {
525
-
526
- // stub method
527
- return [];
528
- }
529
-
530
-
531
- /** Admin methods ******************************************************/
532
-
533
-
534
  /**
535
  * Returns true if on the admin plugin settings page, if any
536
  *
@@ -551,14 +296,13 @@ abstract class SV_WC_Plugin {
551
  * @since 3.0.0
552
  */
553
  public function add_admin_notices() {
554
-
555
  // bail if there's no defined versions to compare
556
  if ( empty( $this->min_wc_semver ) || ! is_numeric( $this->min_wc_semver ) ) {
557
  return;
558
  }
559
 
560
- $latest_wc_versions = SV_WC_Plugin_Compatibility::get_latest_wc_versions();
561
- $current_wc_version = SV_WC_Plugin_Compatibility::get_wc_version();
562
 
563
  // bail if the latest WooCommerce version or the current WooCommerce versions can't be determined
564
  if ( empty( $latest_wc_versions ) || empty( $current_wc_version ) ) {
@@ -612,7 +356,7 @@ abstract class SV_WC_Plugin {
612
  $this->get_admin_notice_handler()->add_admin_notice(
613
  sprintf(
614
  /* translators: Placeholders: %1$s - plugin name, %2$s - WooCommerce version number, %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag */
615
- __( 'Heads up! %1$s will soon discontinue support for WooCommerce %2$s. Please %3$supdate WooCommerce%4$s to take advantage of the latest updates and features.', 'woocommerce-plugin-framework' ),
616
  $this->get_plugin_name(),
617
  $current_wc_version,
618
  '<a href="' . esc_url( admin_url( 'update-core.php' ) ) .'">', '</a>'
@@ -624,17 +368,6 @@ abstract class SV_WC_Plugin {
624
  }
625
 
626
 
627
- /**
628
- * Convenience method to add delayed admin notices, which may depend upon
629
- * some setting being saved prior to determining whether to render
630
- *
631
- * @since 3.0.0
632
- */
633
- public function add_delayed_admin_notices() {
634
- // stub method
635
- }
636
-
637
-
638
  /**
639
  * Return the plugin action links. This will only be called if the plugin
640
  * is active.
@@ -644,8 +377,7 @@ abstract class SV_WC_Plugin {
644
  * @return array associative array of plugin action links
645
  */
646
  public function plugin_action_links( $actions ) {
647
-
648
- $custom_actions = array();
649
 
650
  // settings url(s)
651
  if ( $this->get_settings_link( $this->get_id() ) ) {
@@ -655,17 +387,17 @@ abstract class SV_WC_Plugin {
655
  // documentation url if any
656
  if ( $this->get_documentation_url() ) {
657
  /* translators: Docs as in Documentation */
658
- $custom_actions['docs'] = sprintf( '<a href="%s" target="_blank">%s</a>', $this->get_documentation_url(), esc_html__( 'Docs', 'woocommerce-plugin-framework' ) );
659
  }
660
 
661
  // support url if any
662
  if ( $this->get_support_url() ) {
663
- $custom_actions['support'] = sprintf( '<a href="%s">%s</a>', $this->get_support_url(), esc_html_x( 'Support', 'noun', 'woocommerce-plugin-framework' ) );
664
  }
665
 
666
  // review url if any
667
  if ( $this->get_reviews_url() ) {
668
- $custom_actions['review'] = sprintf( '<a href="%s">%s</a>', $this->get_reviews_url(), esc_html_x( 'Review', 'verb', 'woocommerce-plugin-framework' ) );
669
  }
670
 
671
  // add the links to the front of the actions list
@@ -673,17 +405,13 @@ abstract class SV_WC_Plugin {
673
  }
674
 
675
 
676
- /** Helper methods ******************************************************/
677
-
678
-
679
  /**
680
  * Automatically log API requests/responses when using SV_WC_API_Base
681
  *
682
  * @since 2.2.0
683
- * @see SV_WC_API_Base::broadcast_request()
684
  */
685
  public function add_api_request_logging() {
686
-
687
  if ( ! has_action( 'wc_' . $this->get_id() . '_api_request_performed' ) ) {
688
  add_action( 'wc_' . $this->get_id() . '_api_request_performed', array( $this, 'log_api_request' ), 10, 2 );
689
  }
@@ -699,9 +427,7 @@ abstract class SV_WC_Plugin {
699
  * @param string|null $log_id log to write data to
700
  */
701
  public function log_api_request( $request, $response, $log_id = null ) {
702
-
703
  $this->log( "Request\n" . $this->get_api_log_message( $request ), $log_id );
704
-
705
  if ( ! empty( $response ) ) {
706
  $this->log( "Response\n" . $this->get_api_log_message( $response ), $log_id );
707
  }
@@ -716,15 +442,11 @@ abstract class SV_WC_Plugin {
716
  * @return string
717
  */
718
  public function get_api_log_message( $data ) {
719
-
720
- $messages = array();
721
-
722
  $messages[] = isset( $data['uri'] ) && $data['uri'] ? 'Request' : 'Response';
723
-
724
  foreach ( (array) $data as $key => $value ) {
725
  $messages[] = trim( sprintf( '%s: %s', $key, is_array( $value ) || ( is_object( $value ) && 'stdClass' == get_class( $value ) ) ? print_r( (array) $value, true ) : $value ) );
726
  }
727
-
728
  return implode( "\n", $messages ) . "\n";
729
  }
730
 
@@ -738,7 +460,6 @@ abstract class SV_WC_Plugin {
738
  * @return array
739
  */
740
  public function add_system_status_php_information( $rows ) {
741
-
742
  foreach ( $this->get_dependency_handler()->get_incompatible_php_settings() as $setting => $values ) {
743
 
744
  if ( isset( $values['type'] ) && 'min' === $values['type'] ) {
@@ -748,7 +469,7 @@ abstract class SV_WC_Plugin {
748
  continue;
749
  }
750
 
751
- $note = __( '%1$s - A minimum of %2$s is required.', 'woocommerce-plugin-framework' );
752
 
753
  } else {
754
 
@@ -757,7 +478,7 @@ abstract class SV_WC_Plugin {
757
  continue;
758
  }
759
 
760
- $note = __( 'Set as %1$s - %2$s is required.', 'woocommerce-plugin-framework' );
761
  }
762
 
763
  $note = sprintf( $note, $values['actual'], $values['expected'] );
@@ -769,7 +490,6 @@ abstract class SV_WC_Plugin {
769
  'expected' => $values['expected'], // WC doesn't use this, but it's useful for us
770
  );
771
  }
772
-
773
  return $rows;
774
  }
775
 
@@ -782,77 +502,16 @@ abstract class SV_WC_Plugin {
782
  * @param string $log_id optional log id to segment the files by, defaults to plugin id
783
  */
784
  public function log( $message, $log_id = null ) {
785
-
786
  if ( is_null( $log_id ) ) {
787
  $log_id = $this->get_id();
788
  }
789
-
790
  if ( ! is_object( $this->logger ) ) {
791
  $this->logger = new \WC_Logger();
792
  }
793
-
794
  $this->logger->add( $log_id, $message );
795
  }
796
 
797
 
798
- /**
799
- * Require and instantiate a class
800
- *
801
- * @since 4.2.0
802
- * @param string $local_path path to class file in plugin, e.g. '/includes/class-wc-foo.php'
803
- * @param string $class_name class to instantiate
804
- * @return object instantiated class instance
805
- */
806
- public function load_class( $local_path, $class_name ) {
807
-
808
- require_once( $this->get_plugin_path() . $local_path );
809
-
810
- return new $class_name;
811
- }
812
-
813
-
814
- /**
815
- * Determines if TLS v1.2 is required for API requests.
816
- *
817
- * Subclasses should override this to return true if TLS v1.2 is required.
818
- *
819
- * @since 5.5.2
820
- *
821
- * @return bool
822
- */
823
- public function require_tls_1_2() {
824
-
825
- return false;
826
- }
827
-
828
-
829
- /**
830
- * Determines if TLS 1.2 is available.
831
- *
832
- * @since 5.5.2
833
- *
834
- * @return bool
835
- */
836
- public function is_tls_1_2_available() {
837
-
838
- // assume availability to avoid notices for unknown SSL types
839
- $is_available = true;
840
-
841
- // check the cURL version if installed
842
- if ( is_callable( 'curl_version' ) ) {
843
-
844
- $versions = curl_version();
845
-
846
- // cURL 7.34.0 is considered the minimum version that supports TLS 1.2
847
- if ( version_compare( $versions['version'], '7.34.0', '<' ) ) {
848
- $is_available = false;
849
- }
850
- }
851
-
852
- return $is_available;
853
- }
854
-
855
-
856
  /** Getter methods ******************************************************/
857
 
858
 
@@ -864,9 +523,7 @@ abstract class SV_WC_Plugin {
864
  * @return string
865
  */
866
  public function get_plugin_file() {
867
-
868
  $slug = dirname( plugin_basename( $this->get_file() ) );
869
-
870
  return trailingslashit( $slug ) . $slug . '.php';
871
  }
872
 
@@ -915,18 +572,14 @@ abstract class SV_WC_Plugin {
915
  abstract public function get_plugin_name();
916
 
917
 
918
- /** Handler methods *******************************************************/
919
-
920
-
921
  /**
922
  * Gets the dependency handler.
923
  *
924
  * @since 5.2.0.1
925
  *
926
- * @return SV_WC_Plugin_Dependencies
927
  */
928
  public function get_dependency_handler() {
929
-
930
  return $this->dependency_handler;
931
  }
932
 
@@ -936,36 +589,19 @@ abstract class SV_WC_Plugin {
936
  *
937
  * @since 5.1.0
938
  *
939
- * @return Plugin\Lifecycle
940
  */
941
  public function get_lifecycle_handler() {
942
-
943
  return $this->lifecycle_handler;
944
  }
945
 
946
 
947
- /**
948
- * Gets the Setup Wizard handler instance.
949
- *
950
- * @since 5.3.0
951
- *
952
- * @return null|Admin\Setup_Wizard
953
- */
954
- public function get_setup_wizard_handler() {
955
-
956
- return $this->setup_wizard_handler;
957
- }
958
-
959
-
960
  /**
961
  * Gets the admin message handler.
962
  *
963
- * @since 2.0.0
964
- *
965
- * @return SV_WP_Admin_Message_Handler
966
  */
967
  public function get_message_handler() {
968
-
969
  return $this->message_handler;
970
  }
971
 
@@ -975,29 +611,13 @@ abstract class SV_WC_Plugin {
975
  *
976
  * @since 3.0.0
977
  *
978
- * @return SV_WC_Admin_Notice_Handler
979
  */
980
  public function get_admin_notice_handler() {
981
-
982
  return $this->admin_notice_handler;
983
  }
984
 
985
 
986
- /**
987
- * Gets the settings API handler instance.
988
- *
989
- * Plugins can use this to init the settings API handler.
990
- *
991
- * @since 5.7.0
992
- *
993
- * @return void|Settings_API\Abstract_Settings
994
- */
995
- public function get_settings_handler() {
996
-
997
- return;
998
- }
999
-
1000
-
1001
  /**
1002
  * Returns the plugin version name. Defaults to wc_{plugin id}_version
1003
  *
@@ -1005,7 +625,6 @@ abstract class SV_WC_Plugin {
1005
  * @return string the plugin version name
1006
  */
1007
  public function get_plugin_version_name() {
1008
-
1009
  return 'wc_' . $this->get_id() . '_version';
1010
  }
1011
 
@@ -1033,13 +652,10 @@ abstract class SV_WC_Plugin {
1033
  * @return string plugin configure link
1034
  */
1035
  public function get_settings_link( $plugin_id = null ) {
1036
-
1037
  $settings_url = $this->get_settings_url( $plugin_id );
1038
-
1039
  if ( $settings_url ) {
1040
- return sprintf( '<a href="%s">%s</a>', $settings_url, esc_html__( 'Configure', 'woocommerce-plugin-framework' ) );
1041
  }
1042
-
1043
  // no settings
1044
  return '';
1045
  }
@@ -1056,24 +672,11 @@ abstract class SV_WC_Plugin {
1056
  * @return string plugin settings URL
1057
  */
1058
  public function get_settings_url( $plugin_id = null ) {
1059
-
1060
  // stub method
1061
  return '';
1062
  }
1063
 
1064
 
1065
- /**
1066
- * Returns true if the current page is the admin general configuration page
1067
- *
1068
- * @since 3.0.0
1069
- * @return boolean true if the current page is the admin general configuration page
1070
- */
1071
- public function is_general_configuration_page() {
1072
-
1073
- return isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] && ( ! isset( $_GET['tab'] ) || 'general' === $_GET['tab'] );
1074
- }
1075
-
1076
-
1077
  /**
1078
  * Returns the admin configuration url for the admin general configuration page
1079
  *
@@ -1081,7 +684,6 @@ abstract class SV_WC_Plugin {
1081
  * @return string admin configuration url for the admin general configuration page
1082
  */
1083
  public function get_general_configuration_url() {
1084
-
1085
  return admin_url( 'admin.php?page=wc-settings&tab=general' );
1086
  }
1087
 
@@ -1093,7 +695,6 @@ abstract class SV_WC_Plugin {
1093
  * @return string documentation URL
1094
  */
1095
  public function get_documentation_url() {
1096
-
1097
  return null;
1098
  }
1099
 
@@ -1105,7 +706,6 @@ abstract class SV_WC_Plugin {
1105
  * @return string support url
1106
  */
1107
  public function get_support_url() {
1108
-
1109
  return null;
1110
  }
1111
 
@@ -1118,7 +718,6 @@ abstract class SV_WC_Plugin {
1118
  * @return string
1119
  */
1120
  public function get_sales_page_url() {
1121
-
1122
  return '';
1123
  }
1124
 
@@ -1133,7 +732,6 @@ abstract class SV_WC_Plugin {
1133
  * @return string
1134
  */
1135
  public function get_reviews_url() {
1136
-
1137
  return $this->get_sales_page_url() ? $this->get_sales_page_url() . '#comments' : '';
1138
  }
1139
 
@@ -1148,11 +746,9 @@ abstract class SV_WC_Plugin {
1148
  * @return string
1149
  */
1150
  public function get_plugin_path() {
1151
-
1152
  if ( null === $this->plugin_path ) {
1153
  $this->plugin_path = untrailingslashit( plugin_dir_path( $this->get_file() ) );
1154
  }
1155
-
1156
  return $this->plugin_path;
1157
  }
1158
 
@@ -1167,32 +763,13 @@ abstract class SV_WC_Plugin {
1167
  * @return string
1168
  */
1169
  public function get_plugin_url() {
1170
-
1171
  if ( null === $this->plugin_url ) {
1172
  $this->plugin_url = untrailingslashit( plugins_url( '/', $this->get_file() ) );
1173
  }
1174
-
1175
  return $this->plugin_url;
1176
  }
1177
 
1178
 
1179
- /**
1180
- * Gets the woocommerce uploads path, without trailing slash.
1181
- *
1182
- * Oddly WooCommerce core does not provide a way to get this.
1183
- *
1184
- * @since 2.0.0
1185
- *
1186
- * @return string
1187
- */
1188
- public static function get_woocommerce_uploads_path() {
1189
-
1190
- $upload_dir = wp_upload_dir();
1191
-
1192
- return $upload_dir['basedir'] . '/woocommerce_uploads';
1193
- }
1194
-
1195
-
1196
  /**
1197
  * Returns the loaded framework __FILE__
1198
  *
@@ -1200,7 +777,6 @@ abstract class SV_WC_Plugin {
1200
  * @return string
1201
  */
1202
  public function get_framework_file() {
1203
-
1204
  return __FILE__;
1205
  }
1206
 
@@ -1214,7 +790,6 @@ abstract class SV_WC_Plugin {
1214
  * @return string
1215
  */
1216
  public function get_framework_path() {
1217
-
1218
  return untrailingslashit( plugin_dir_path( $this->get_framework_file() ) );
1219
  }
1220
 
@@ -1227,7 +802,6 @@ abstract class SV_WC_Plugin {
1227
  * @return string
1228
  */
1229
  public function get_framework_assets_path() {
1230
-
1231
  return $this->get_framework_path() . '/assets';
1232
  }
1233
 
@@ -1240,50 +814,9 @@ abstract class SV_WC_Plugin {
1240
  * @return string
1241
  */
1242
  public function get_framework_assets_url() {
1243
-
1244
  return untrailingslashit( plugins_url( '/assets', $this->get_framework_file() ) );
1245
  }
1246
 
1247
-
1248
- /**
1249
- * Gets the plugin default template path, without a trailing slash.
1250
- *
1251
- * @since 5.5.0
1252
- *
1253
- * @return string
1254
- */
1255
- public function get_template_path() {
1256
-
1257
- if ( null === $this->template_path ) {
1258
- $this->template_path = $this->get_plugin_path() . '/templates';
1259
- }
1260
-
1261
- return $this->template_path;
1262
- }
1263
-
1264
-
1265
- /**
1266
- * Loads and outputs a template file HTML.
1267
- *
1268
- * @see \wc_get_template() except we define automatically the default path
1269
- *
1270
- * @since 5.5.0
1271
- *
1272
- * @param string $template template name/part
1273
- * @param array $args associative array of optional template arguments
1274
- * @param string $path optional template path, can be empty, as themes can override this
1275
- * @param string $default_path optional default template path, will normally use the plugin's own template path unless overridden
1276
- */
1277
- public function load_template( $template, array $args = [], $path = '', $default_path = '' ) {
1278
-
1279
- if ( '' === $default_path || ! is_string( $default_path ) ) {
1280
- $default_path = trailingslashit( $this->get_template_path() );
1281
- }
1282
-
1283
- wc_get_template( $template, $args, $path, $default_path );
1284
- }
1285
-
1286
-
1287
  /**
1288
  * Determines whether a plugin is active.
1289
  *
@@ -1293,232 +826,28 @@ abstract class SV_WC_Plugin {
1293
  * @return boolean true if the named plugin is installed and active
1294
  */
1295
  public function is_plugin_active( $plugin_name ) {
1296
-
1297
  $is_active = false;
1298
-
1299
  if ( is_string( $plugin_name ) ) {
1300
-
1301
  if ( ! array_key_exists( $plugin_name, $this->active_plugins ) ) {
1302
-
1303
- $active_plugins = (array) get_option( 'active_plugins', array() );
1304
-
1305
  if ( is_multisite() ) {
1306
- $active_plugins = array_merge( $active_plugins, array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) );
1307
  }
1308
-
1309
- $plugin_filenames = array();
1310
-
1311
  foreach ( $active_plugins as $plugin ) {
1312
-
1313
- if ( SV_WC_Helper::str_exists( $plugin, '/' ) ) {
1314
-
1315
  // normal plugin name (plugin-dir/plugin-filename.php)
1316
  list( , $filename ) = explode( '/', $plugin );
1317
-
1318
  } else {
1319
-
1320
  // no directory, just plugin file
1321
  $filename = $plugin;
1322
  }
1323
-
1324
  $plugin_filenames[] = $filename;
1325
  }
1326
-
1327
  $this->active_plugins[ $plugin_name ] = in_array( $plugin_name, $plugin_filenames, true );
1328
  }
1329
-
1330
  $is_active = (bool) $this->active_plugins[ $plugin_name ];
1331
  }
1332
-
1333
  return $is_active;
1334
  }
1335
-
1336
-
1337
- /** Deprecated methods ****************************************************/
1338
-
1339
-
1340
- /**
1341
- * Handles version checking.
1342
- *
1343
- * @since 2.0.0
1344
- * @deprecated 5.2.0
1345
- */
1346
- public function do_install() {
1347
-
1348
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_lifecycle_handler() ) . '::init()' );
1349
-
1350
- $this->get_lifecycle_handler()->init();
1351
- }
1352
-
1353
-
1354
- /**
1355
- * Helper method to install default settings for a plugin.
1356
- *
1357
- * @since 4.2.0
1358
- * @deprecated 5.2.0
1359
- *
1360
- * @param array $settings array of settings in format required by WC_Admin_Settings
1361
- */
1362
- public function install_default_settings( array $settings ) {
1363
-
1364
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_lifecycle_handler() ) . '::install_default_settings()' );
1365
-
1366
- $this->get_lifecycle_handler()->install_default_settings( $settings );
1367
- }
1368
-
1369
-
1370
- /**
1371
- * Plugin activated method. Perform any activation tasks here.
1372
- * Note that this _does not_ run during upgrades.
1373
- *
1374
- * @since 4.2.0
1375
- * @deprecated 5.2.0
1376
- */
1377
- public function activate() {
1378
-
1379
- wc_deprecated_function( __METHOD__, '5.2.0' );
1380
- }
1381
-
1382
-
1383
- /**
1384
- * Plugin deactivation method. Perform any deactivation tasks here.
1385
- *
1386
- * @since 4.2.0
1387
- * @deprecated 5.2.0
1388
- */
1389
- public function deactivate() {
1390
-
1391
- wc_deprecated_function( __METHOD__, '5.2.0' );
1392
- }
1393
-
1394
-
1395
- /**
1396
- * Gets the string name of any required PHP extensions that are not loaded.
1397
- *
1398
- * @since 4.5.0
1399
- * @deprecated 5.2.0
1400
- *
1401
- * @return array
1402
- */
1403
- public function get_missing_extension_dependencies() {
1404
-
1405
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_missing_php_extensions()' );
1406
-
1407
- return $this->get_dependency_handler()->get_missing_php_extensions();
1408
- }
1409
-
1410
-
1411
- /**
1412
- * Gets the string name of any required PHP functions that are not loaded.
1413
- *
1414
- * @since 2.1.0
1415
- * @deprecated 5.2.0
1416
- *
1417
- * @return array
1418
- */
1419
- public function get_missing_function_dependencies() {
1420
-
1421
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_missing_php_functions()' );
1422
-
1423
- return $this->get_dependency_handler()->get_missing_php_functions();
1424
- }
1425
-
1426
-
1427
- /**
1428
- * Gets the string name of any required PHP extensions that are not loaded.
1429
- *
1430
- * @since 4.5.0
1431
- * @deprecated 5.2.0
1432
- *
1433
- * @return array
1434
- */
1435
- public function get_incompatible_php_settings() {
1436
-
1437
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_incompatible_php_settings()' );
1438
-
1439
- return $this->get_dependency_handler()->get_incompatible_php_settings();
1440
- }
1441
-
1442
-
1443
- /**
1444
- * Gets the PHP dependencies.
1445
- *
1446
- * @since 2.0.0
1447
- * @deprecated 5.2.0
1448
- *
1449
- * @return array
1450
- */
1451
- protected function get_dependencies() {
1452
-
1453
- wc_deprecated_function( __METHOD__, '5.2.0' );
1454
-
1455
- return array();
1456
- }
1457
-
1458
-
1459
- /**
1460
- * Gets the PHP extension dependencies.
1461
- *
1462
- * @since 4.5.0
1463
- * @deprecated 5.2.0
1464
- *
1465
- * @return array
1466
- */
1467
- protected function get_extension_dependencies() {
1468
-
1469
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_php_extensions()' );
1470
-
1471
- return $this->get_dependency_handler()->get_php_extensions();
1472
- }
1473
-
1474
-
1475
- /**
1476
- * Gets the PHP function dependencies.
1477
- *
1478
- * @since 2.1.0
1479
- * @deprecated 5.2.0
1480
- *
1481
- * @return array
1482
- */
1483
- protected function get_function_dependencies() {
1484
-
1485
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_php_functions()' );
1486
-
1487
- return $this->get_dependency_handler()->get_php_functions();
1488
- }
1489
-
1490
-
1491
- /**
1492
- * Gets the PHP settings dependencies.
1493
- *
1494
- * @since 4.5.0
1495
- * @deprecated 5.2.0
1496
- *
1497
- * @return array
1498
- */
1499
- protected function get_php_settings_dependencies() {
1500
-
1501
- wc_deprecated_function( __METHOD__, '5.2.0', get_class( $this->get_dependency_handler() ) . '::get_php_settings()' );
1502
-
1503
- return $this->get_dependency_handler()->get_php_settings();
1504
- }
1505
-
1506
-
1507
- /**
1508
- * Sets the plugin dependencies.
1509
- *
1510
- * @since 4.5.0
1511
- * @deprecated 5.2.0
1512
- *
1513
- * @param array $dependencies the environment dependencies
1514
- */
1515
- protected function set_dependencies( $dependencies = [] ) {
1516
-
1517
- wc_deprecated_function( __METHOD__, '5.2.0' );
1518
- }
1519
-
1520
-
1521
  }
1522
-
1523
-
1524
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework;
8
 
9
+ use WC_Logger;
10
+ use WooCommerce\Facebook\Framework\Plugin\Compatibility;
11
+ use WooCommerce\Facebook\Framework\Plugin\Dependencies;
12
 
13
+ defined( 'ABSPATH' ) or exit;
14
 
15
  /**
16
  * # WooCommerce Plugin Framework
22
  *
23
  * @version 5.8.0
24
  */
25
+ abstract class Plugin {
 
 
 
 
26
 
27
  /** @var object single instance of plugin */
28
  protected static $instance;
42
  /** @var string template path, without trailing slash */
43
  private $template_path;
44
 
45
+ /** @var WC_Logger instance */
46
  private $logger;
47
 
48
+ /** @var AdminMessageHandler instance */
49
  private $message_handler;
50
 
51
  /** @var string the plugin text domain */
57
  /** @var int|float minimum supported WooCommerce versions before the latest (units for major releases, decimals for minor) */
58
  private $min_wc_semver;
59
 
60
+ /** @var Dependencies dependency handler instance */
61
  private $dependency_handler;
62
 
63
+ /** @var \WooCommerce\Facebook\Lifecycle lifecycle handler instance */
 
 
 
64
  protected $lifecycle_handler;
65
 
66
+ /** @var AdminNoticeHandler the admin notice handler class */
 
 
 
 
 
 
67
  private $admin_notice_handler;
68
 
69
 
91
  * }
92
  */
93
  public function __construct( $id, $version, $args = [] ) {
 
94
  // required params
95
  $this->id = $id;
96
  $this->version = $version;
104
  $this->min_wc_semver = is_numeric( $args['min_wc_semver'] ) ? abs( $args['min_wc_semver'] ) : null;
105
  $this->text_domain = $args['text_domain'];
106
 
 
 
 
107
  // initialize the dependencies manager
108
  $this->init_dependencies( $args['dependencies'] );
109
 
113
  // build the admin notice handler instance
114
  $this->init_admin_notice_handler();
115
 
 
 
 
116
  // build the lifecycle handler instance
117
  $this->init_lifecycle_handler();
118
 
 
 
 
 
 
 
119
  // add the action & filter hooks
120
  $this->add_hooks();
121
  }
138
  * }
139
  */
140
  protected function init_dependencies( $dependencies ) {
141
+ $this->dependency_handler = new Dependencies( $this, $dependencies );
 
142
  }
143
 
144
 
150
  * @since 5.2.0
151
  */
152
  protected function init_admin_message_handler() {
153
+ $this->message_handler = new AdminMessageHandler( $this->get_id() );
 
154
  }
155
 
156
 
162
  * @since 5.2.0
163
  */
164
  protected function init_admin_notice_handler() {
165
+ $this->admin_notice_handler = new AdminNoticeHandler( $this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
 
168
 
171
  *
172
  * Plugins can override this with their own handler to perform install and
173
  * upgrade routines.
 
 
174
  */
175
  protected function init_lifecycle_handler() {
176
+ $this->lifecycle_handler = new \WooCommerce\Facebook\Lifecycle( $this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
 
179
 
183
  * @since 5.2.0
184
  */
185
  private function add_hooks() {
 
 
 
 
 
 
 
186
  // hook for translations separately to ensure they're loaded
187
  add_action( 'init', array( $this, 'load_translations' ) );
188
 
189
  // add the admin notices
190
  add_action( 'admin_notices', array( $this, 'add_admin_notices' ) );
 
191
 
192
  // add a 'Configure' link to the plugin action links
193
  add_filter( 'plugin_action_links_' . plugin_basename( $this->get_plugin_file() ), array( $this, 'plugin_action_links' ) );
207
  */
208
  public function __clone() {
209
  /* translators: Placeholders: %s - plugin name */
210
+ _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot clone instances of %s.', 'facebook-for-woocommerce' ), esc_html( $this->get_plugin_name() ) ), '3.1.0' );
211
  }
212
 
213
 
218
  */
219
  public function __wakeup() {
220
  /* translators: Placeholders: %s - plugin name */
221
+ _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot unserialize instances of %s.', 'facebook-for-woocommerce' ), esc_html( $this->get_plugin_name() ) ), '3.1.0' );
222
  }
223
 
224
 
230
  * @since 4.2.0
231
  */
232
  public function load_translations() {
 
233
  $this->load_framework_textdomain();
 
234
  // if this plugin passes along its text domain, load its translation files
235
  if ( $this->text_domain ) {
236
  $this->load_plugin_textdomain();
244
  * @since 4.5.0
245
  */
246
  protected function load_framework_textdomain() {
247
+ $this->load_textdomain( 'facebook-for-woocommerce', dirname( plugin_basename( $this->get_framework_file() ) ) );
248
  }
249
 
250
 
266
  * @param string $path the i18n path
267
  */
268
  protected function load_textdomain( $textdomain, $path ) {
 
269
  // user's locale if in the admin for WP 4.7+, or the site locale otherwise
270
  $locale = is_admin() && is_callable( 'get_user_locale' ) ? get_user_locale() : get_locale();
271
 
276
  load_plugin_textdomain( $textdomain, false, untrailingslashit( $path ) . '/i18n/languages' );
277
  }
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  /**
280
  * Returns true if on the admin plugin settings page, if any
281
  *
296
  * @since 3.0.0
297
  */
298
  public function add_admin_notices() {
 
299
  // bail if there's no defined versions to compare
300
  if ( empty( $this->min_wc_semver ) || ! is_numeric( $this->min_wc_semver ) ) {
301
  return;
302
  }
303
 
304
+ $latest_wc_versions = Compatibility::get_latest_wc_versions();
305
+ $current_wc_version = Compatibility::get_wc_version();
306
 
307
  // bail if the latest WooCommerce version or the current WooCommerce versions can't be determined
308
  if ( empty( $latest_wc_versions ) || empty( $current_wc_version ) ) {
356
  $this->get_admin_notice_handler()->add_admin_notice(
357
  sprintf(
358
  /* translators: Placeholders: %1$s - plugin name, %2$s - WooCommerce version number, %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag */
359
+ __( 'Heads up! %1$s will soon discontinue support for WooCommerce %2$s. Please %3$supdate WooCommerce%4$s to take advantage of the latest updates and features.', 'facebook-for-woocommerce' ),
360
  $this->get_plugin_name(),
361
  $current_wc_version,
362
  '<a href="' . esc_url( admin_url( 'update-core.php' ) ) .'">', '</a>'
368
  }
369
 
370
 
 
 
 
 
 
 
 
 
 
 
 
371
  /**
372
  * Return the plugin action links. This will only be called if the plugin
373
  * is active.
377
  * @return array associative array of plugin action links
378
  */
379
  public function plugin_action_links( $actions ) {
380
+ $custom_actions = [];
 
381
 
382
  // settings url(s)
383
  if ( $this->get_settings_link( $this->get_id() ) ) {
387
  // documentation url if any
388
  if ( $this->get_documentation_url() ) {
389
  /* translators: Docs as in Documentation */
390
+ $custom_actions['docs'] = sprintf( '<a href="%s" target="_blank">%s</a>', $this->get_documentation_url(), esc_html__( 'Docs', 'facebook-for-woocommerce' ) );
391
  }
392
 
393
  // support url if any
394
  if ( $this->get_support_url() ) {
395
+ $custom_actions['support'] = sprintf( '<a href="%s">%s</a>', $this->get_support_url(), esc_html_x( 'Support', 'noun', 'facebook-for-woocommerce' ) );
396
  }
397
 
398
  // review url if any
399
  if ( $this->get_reviews_url() ) {
400
+ $custom_actions['review'] = sprintf( '<a href="%s">%s</a>', $this->get_reviews_url(), esc_html_x( 'Review', 'verb', 'facebook-for-woocommerce' ) );
401
  }
402
 
403
  // add the links to the front of the actions list
405
  }
406
 
407
 
 
 
 
408
  /**
409
  * Automatically log API requests/responses when using SV_WC_API_Base
410
  *
411
  * @since 2.2.0
412
+ * @see Base::broadcast_request()
413
  */
414
  public function add_api_request_logging() {
 
415
  if ( ! has_action( 'wc_' . $this->get_id() . '_api_request_performed' ) ) {
416
  add_action( 'wc_' . $this->get_id() . '_api_request_performed', array( $this, 'log_api_request' ), 10, 2 );
417
  }
427
  * @param string|null $log_id log to write data to
428
  */
429
  public function log_api_request( $request, $response, $log_id = null ) {
 
430
  $this->log( "Request\n" . $this->get_api_log_message( $request ), $log_id );
 
431
  if ( ! empty( $response ) ) {
432
  $this->log( "Response\n" . $this->get_api_log_message( $response ), $log_id );
433
  }
442
  * @return string
443
  */
444
  public function get_api_log_message( $data ) {
445
+ $messages = [];
 
 
446
  $messages[] = isset( $data['uri'] ) && $data['uri'] ? 'Request' : 'Response';
 
447
  foreach ( (array) $data as $key => $value ) {
448
  $messages[] = trim( sprintf( '%s: %s', $key, is_array( $value ) || ( is_object( $value ) && 'stdClass' == get_class( $value ) ) ? print_r( (array) $value, true ) : $value ) );
449
  }
 
450
  return implode( "\n", $messages ) . "\n";
451
  }
452
 
460
  * @return array
461
  */
462
  public function add_system_status_php_information( $rows ) {
 
463
  foreach ( $this->get_dependency_handler()->get_incompatible_php_settings() as $setting => $values ) {
464
 
465
  if ( isset( $values['type'] ) && 'min' === $values['type'] ) {
469
  continue;
470
  }
471
 
472
+ $note = __( '%1$s - A minimum of %2$s is required.', 'facebook-for-woocommerce' );
473
 
474
  } else {
475
 
478
  continue;
479
  }
480
 
481
+ $note = __( 'Set as %1$s - %2$s is required.', 'facebook-for-woocommerce' );
482
  }
483
 
484
  $note = sprintf( $note, $values['actual'], $values['expected'] );
490
  'expected' => $values['expected'], // WC doesn't use this, but it's useful for us
491
  );
492
  }
 
493
  return $rows;
494
  }
495
 
502
  * @param string $log_id optional log id to segment the files by, defaults to plugin id
503
  */
504
  public function log( $message, $log_id = null ) {
 
505
  if ( is_null( $log_id ) ) {
506
  $log_id = $this->get_id();
507
  }
 
508
  if ( ! is_object( $this->logger ) ) {
509
  $this->logger = new \WC_Logger();
510
  }
 
511
  $this->logger->add( $log_id, $message );
512
  }
513
 
514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  /** Getter methods ******************************************************/
516
 
517
 
523
  * @return string
524
  */
525
  public function get_plugin_file() {
 
526
  $slug = dirname( plugin_basename( $this->get_file() ) );
 
527
  return trailingslashit( $slug ) . $slug . '.php';
528
  }
529
 
572
  abstract public function get_plugin_name();
573
 
574
 
 
 
 
575
  /**
576
  * Gets the dependency handler.
577
  *
578
  * @since 5.2.0.1
579
  *
580
+ * @return Dependencies
581
  */
582
  public function get_dependency_handler() {
 
583
  return $this->dependency_handler;
584
  }
585
 
589
  *
590
  * @since 5.1.0
591
  *
592
+ * @return \WooCommerce\Facebook\Lifecycle
593
  */
594
  public function get_lifecycle_handler() {
 
595
  return $this->lifecycle_handler;
596
  }
597
 
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  /**
600
  * Gets the admin message handler.
601
  *
602
+ * @return AdminMessageHandler
 
 
603
  */
604
  public function get_message_handler() {
 
605
  return $this->message_handler;
606
  }
607
 
611
  *
612
  * @since 3.0.0
613
  *
614
+ * @return AdminNoticeHandler
615
  */
616
  public function get_admin_notice_handler() {
 
617
  return $this->admin_notice_handler;
618
  }
619
 
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  /**
622
  * Returns the plugin version name. Defaults to wc_{plugin id}_version
623
  *
625
  * @return string the plugin version name
626
  */
627
  public function get_plugin_version_name() {
 
628
  return 'wc_' . $this->get_id() . '_version';
629
  }
630
 
652
  * @return string plugin configure link
653
  */
654
  public function get_settings_link( $plugin_id = null ) {
 
655
  $settings_url = $this->get_settings_url( $plugin_id );
 
656
  if ( $settings_url ) {
657
+ return sprintf( '<a href="%s">%s</a>', $settings_url, esc_html__( 'Configure', 'facebook-for-woocommerce' ) );
658
  }
 
659
  // no settings
660
  return '';
661
  }
672
  * @return string plugin settings URL
673
  */
674
  public function get_settings_url( $plugin_id = null ) {
 
675
  // stub method
676
  return '';
677
  }
678
 
679
 
 
 
 
 
 
 
 
 
 
 
 
 
680
  /**
681
  * Returns the admin configuration url for the admin general configuration page
682
  *
684
  * @return string admin configuration url for the admin general configuration page
685
  */
686
  public function get_general_configuration_url() {
 
687
  return admin_url( 'admin.php?page=wc-settings&tab=general' );
688
  }
689
 
695
  * @return string documentation URL
696
  */
697
  public function get_documentation_url() {
 
698
  return null;
699
  }
700
 
706
  * @return string support url
707
  */
708
  public function get_support_url() {
 
709
  return null;
710
  }
711
 
718
  * @return string
719
  */
720
  public function get_sales_page_url() {
 
721
  return '';
722
  }
723
 
732
  * @return string
733
  */
734
  public function get_reviews_url() {
 
735
  return $this->get_sales_page_url() ? $this->get_sales_page_url() . '#comments' : '';
736
  }
737
 
746
  * @return string
747
  */
748
  public function get_plugin_path() {
 
749
  if ( null === $this->plugin_path ) {
750
  $this->plugin_path = untrailingslashit( plugin_dir_path( $this->get_file() ) );
751
  }
 
752
  return $this->plugin_path;
753
  }
754
 
763
  * @return string
764
  */
765
  public function get_plugin_url() {
 
766
  if ( null === $this->plugin_url ) {
767
  $this->plugin_url = untrailingslashit( plugins_url( '/', $this->get_file() ) );
768
  }
 
769
  return $this->plugin_url;
770
  }
771
 
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  /**
774
  * Returns the loaded framework __FILE__
775
  *
777
  * @return string
778
  */
779
  public function get_framework_file() {
 
780
  return __FILE__;
781
  }
782
 
790
  * @return string
791
  */
792
  public function get_framework_path() {
 
793
  return untrailingslashit( plugin_dir_path( $this->get_framework_file() ) );
794
  }
795
 
802
  * @return string
803
  */
804
  public function get_framework_assets_path() {
 
805
  return $this->get_framework_path() . '/assets';
806
  }
807
 
814
  * @return string
815
  */
816
  public function get_framework_assets_url() {
 
817
  return untrailingslashit( plugins_url( '/assets', $this->get_framework_file() ) );
818
  }
819
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
820
  /**
821
  * Determines whether a plugin is active.
822
  *
826
  * @return boolean true if the named plugin is installed and active
827
  */
828
  public function is_plugin_active( $plugin_name ) {
 
829
  $is_active = false;
 
830
  if ( is_string( $plugin_name ) ) {
 
831
  if ( ! array_key_exists( $plugin_name, $this->active_plugins ) ) {
832
+ $active_plugins = (array) get_option( 'active_plugins', [] );
 
 
833
  if ( is_multisite() ) {
834
+ $active_plugins = array_merge( $active_plugins, array_keys( get_site_option( 'active_sitewide_plugins', [] ) ) );
835
  }
836
+ $plugin_filenames = [];
 
 
837
  foreach ( $active_plugins as $plugin ) {
838
+ if ( Helper::str_exists( $plugin, '/' ) ) {
 
 
839
  // normal plugin name (plugin-dir/plugin-filename.php)
840
  list( , $filename ) = explode( '/', $plugin );
 
841
  } else {
 
842
  // no directory, just plugin file
843
  $filename = $plugin;
844
  }
 
845
  $plugin_filenames[] = $filename;
846
  }
 
847
  $this->active_plugins[ $plugin_name ] = in_array( $plugin_name, $plugin_filenames, true );
848
  }
 
849
  $is_active = (bool) $this->active_plugins[ $plugin_name ];
850
  }
 
851
  return $is_active;
852
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
  }
 
 
 
includes/Framework/Plugin/Compatibility.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Plugin;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * WooCommerce Compatibility Utility Class
13
+ *
14
+ * The unfortunate purpose of this class is to provide a single point of
15
+ * compatibility functions for dealing with supporting multiple versions
16
+ * of WooCommerce and various extensions.
17
+ *
18
+ * The expected procedure is to remove methods from this class, using the
19
+ * latest ones directly in code, as support for older versions of WooCommerce
20
+ * are dropped.
21
+ *
22
+ * Current Compatibility
23
+ * + Core 3.0.9 - 3.7.x
24
+ * + Subscriptions 2.2.x
25
+ *
26
+ * // TODO: move to /compatibility
27
+ *
28
+ * @since 2.0.0
29
+ */
30
+ class Compatibility {
31
+
32
+ /**
33
+ * Retrieves a list of the latest available WooCommerce versions.
34
+ *
35
+ * Excludes betas, release candidates and development versions.
36
+ * Versions are sorted from most recent to least recent.
37
+ *
38
+ * @since 5.4.1
39
+ *
40
+ * @return string[] array of semver strings
41
+ */
42
+ public static function get_latest_wc_versions() {
43
+ $latest_wc_versions = get_transient( 'sv_wc_plugin_wc_versions' );
44
+ if ( ! is_array( $latest_wc_versions ) ) {
45
+ /** @link https://codex.wordpress.org/WordPress.org_API */
46
+ $wp_org_request = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/woocommerce.json', [ 'timeout' => 1 ] );
47
+ if ( is_array( $wp_org_request ) && isset( $wp_org_request['body'] ) ) {
48
+ $plugin_info = json_decode( $wp_org_request['body'], true );
49
+ if ( is_array( $plugin_info ) && ! empty( $plugin_info['versions'] ) && is_array( $plugin_info['versions'] ) ) {
50
+ $latest_wc_versions = [];
51
+ // reverse array as WordPress supplies oldest version first, newest last
52
+ foreach ( array_keys( array_reverse( $plugin_info['versions'] ) ) as $wc_version ) {
53
+ // skip trunk, release candidates, betas and other non-final or irregular versions
54
+ if (
55
+ is_string( $wc_version )
56
+ && '' !== $wc_version
57
+ && is_numeric( $wc_version[0] )
58
+ && false === strpos( $wc_version, '-' )
59
+ ) {
60
+ $latest_wc_versions[] = $wc_version;
61
+ }
62
+ }
63
+ set_transient( 'sv_wc_plugin_wc_versions', $latest_wc_versions, WEEK_IN_SECONDS );
64
+ }
65
+ }
66
+ }
67
+ return is_array( $latest_wc_versions ) ? $latest_wc_versions : [];
68
+ }
69
+
70
+ /**
71
+ * Gets the version of the currently installed WooCommerce.
72
+ *
73
+ * @since 3.0.0
74
+ *
75
+ * @return string|null Woocommerce version number or null if undetermined
76
+ */
77
+ public static function get_wc_version() {
78
+
79
+ return defined( 'WC_VERSION' ) && WC_VERSION ? WC_VERSION : null;
80
+ }
81
+
82
+
83
+ /**
84
+ * Determines if the installed version of WooCommerce is equal or greater than a given version.
85
+ *
86
+ * @since 4.7.3
87
+ *
88
+ * @param string $version version number to compare
89
+ * @return bool
90
+ */
91
+ public static function is_wc_version_gte( $version ) {
92
+
93
+ $wc_version = self::get_wc_version();
94
+
95
+ return $wc_version && version_compare( $wc_version, $version, '>=' );
96
+ }
97
+
98
+
99
+ /**
100
+ * Determines whether the enhanced admin is available.
101
+ *
102
+ * This checks both for WooCommerce v4.0+ and the underlying package availability.
103
+ *
104
+ * @since 5.6.0
105
+ *
106
+ * @return bool
107
+ */
108
+ public static function is_enhanced_admin_available() {
109
+ return self::is_wc_version_gte( '4.0' ) && function_exists( 'wc_admin_url' );
110
+ }
111
+
112
+
113
+ /**
114
+ * Converts a shorthand byte value to an integer byte value.
115
+ *
116
+ * Wrapper for wp_convert_hr_to_bytes(), moved to load.php in WordPress 4.6 from media.php
117
+ *
118
+ * Based on ActionScheduler's compat wrapper for the same function:
119
+ * ActionScheduler_Compatibility::convert_hr_to_bytes()
120
+ *
121
+ * @link https://secure.php.net/manual/en/function.ini-get.php
122
+ * @link https://secure.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
123
+ *
124
+ * @since 5.3.1
125
+ *
126
+ * @param string $value A (PHP ini) byte value, either shorthand or ordinary.
127
+ * @return int An integer byte value.
128
+ */
129
+ public static function convert_hr_to_bytes( $value ) {
130
+
131
+ if ( function_exists( 'wp_convert_hr_to_bytes' ) ) {
132
+
133
+ return wp_convert_hr_to_bytes( $value );
134
+ }
135
+
136
+ $value = strtolower( trim( $value ) );
137
+ $bytes = (int) $value;
138
+
139
+ if ( false !== strpos( $value, 'g' ) ) {
140
+
141
+ $bytes *= GB_IN_BYTES;
142
+
143
+ } elseif ( false !== strpos( $value, 'm' ) ) {
144
+
145
+ $bytes *= MB_IN_BYTES;
146
+
147
+ } elseif ( false !== strpos( $value, 'k' ) ) {
148
+
149
+ $bytes *= KB_IN_BYTES;
150
+ }
151
+
152
+ // deal with large (float) values which run into the maximum integer size
153
+ return min( $bytes, PHP_INT_MAX );
154
+ }
155
+ }
vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin-dependencies.php → includes/Framework/Plugin/Dependencies.php RENAMED
@@ -1,41 +1,19 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Plugin/Classes
20
- * @author SkyVerge
21
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
22
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
23
  */
24
 
25
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
26
 
27
- defined( 'ABSPATH' ) or exit;
28
-
29
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WC_Plugin_Dependencies' ) ) :
30
 
 
31
 
32
  /**
33
  * Plugin dependencies handler.
34
- *
35
- * @since 5.2.0
36
  */
37
- class SV_WC_Plugin_Dependencies {
38
-
39
 
40
  /** @var array required PHP extensions */
41
  protected $php_extensions = array();
@@ -46,7 +24,7 @@ class SV_WC_Plugin_Dependencies {
46
  /** @var array required PHP settings */
47
  protected $php_settings = array();
48
 
49
- /** @var SV_WC_Plugin plugin instance */
50
  protected $plugin;
51
 
52
 
@@ -55,7 +33,7 @@ class SV_WC_Plugin_Dependencies {
55
  *
56
  * @since 5.2.0
57
  *
58
- * @param SV_WC_Plugin $plugin plugin instance
59
  * @param array $args {
60
  * PHP extension, function, and settings dependencies
61
  *
@@ -64,16 +42,12 @@ class SV_WC_Plugin_Dependencies {
64
  * @type array $php_settings PHP settings dependencies
65
  * }
66
  */
67
- public function __construct( SV_WC_Plugin $plugin, $args = array() ) {
68
-
69
- $this->plugin = $plugin;
70
-
71
- $dependencies = $this->parse_dependencies( $args );
72
-
73
  $this->php_extensions = (array) $dependencies['php_extensions'];
74
  $this->php_functions = (array) $dependencies['php_functions'];
75
  $this->php_settings = (array) $dependencies['php_settings'];
76
-
77
  // add the action & filter hooks
78
  $this->add_hooks();
79
  }
@@ -88,13 +62,11 @@ class SV_WC_Plugin_Dependencies {
88
  * @return array
89
  */
90
  private function parse_dependencies( $args ) {
91
-
92
  $dependencies = wp_parse_args( $args, array(
93
  'php_extensions' => array(),
94
  'php_functions' => array(),
95
  'php_settings' => array(),
96
  ) );
97
-
98
  $default_settings = array(
99
  'suhosin.post.max_array_index_length' => 256,
100
  'suhosin.post.max_totalname_length' => 65535,
@@ -103,12 +75,10 @@ class SV_WC_Plugin_Dependencies {
103
  'suhosin.request.max_totalname_length' => 65535,
104
  'suhosin.request.max_vars' => 1024,
105
  );
106
-
107
  // override any default settings requirements if the plugin specifies them
108
  if ( ! empty( $dependencies['php_settings'] ) ) {
109
  $dependencies['php_settings'] = array_merge( $default_settings, $dependencies['php_settings'] );
110
  }
111
-
112
  return $dependencies;
113
  }
114
 
@@ -119,7 +89,6 @@ class SV_WC_Plugin_Dependencies {
119
  * @since 5.2.0
120
  */
121
  protected function add_hooks() {
122
-
123
  // add the admin dependency notices
124
  add_action( 'admin_init', array( $this, 'add_admin_notices' ) );
125
  }
@@ -131,11 +100,9 @@ class SV_WC_Plugin_Dependencies {
131
  * @since 5.2.0
132
  */
133
  public function add_admin_notices() {
134
-
135
  $this->add_php_extension_notices();
136
  $this->add_php_function_notices();
137
  $this->add_php_settings_notices();
138
-
139
  $this->add_deprecated_notices();
140
  }
141
 
@@ -146,23 +113,19 @@ class SV_WC_Plugin_Dependencies {
146
  * @since 5.2.0
147
  */
148
  public function add_php_extension_notices() {
149
-
150
  $missing_extensions = $this->get_missing_php_extensions();
151
-
152
  if ( count( $missing_extensions ) > 0 ) {
153
-
154
  $message = sprintf(
155
  /* translators: Placeholders: %1$s - plugin name, %2$s - a PHP extension/comma-separated list of PHP extensions */
156
  _n(
157
  '%1$s requires the %2$s PHP extension to function. Contact your host or server administrator to install and configure the missing extension.',
158
  '%1$s requires the following PHP extensions to function: %2$s. Contact your host or server administrator to install and configure the missing extensions.',
159
  count( $missing_extensions ),
160
- 'woocommerce-plugin-framework'
161
  ),
162
  esc_html( $this->get_plugin()->get_plugin_name() ),
163
  '<strong>' . implode( ', ', $missing_extensions ) . '</strong>'
164
  );
165
-
166
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-extensions', $message, 'error' );
167
  }
168
  }
@@ -174,23 +137,19 @@ class SV_WC_Plugin_Dependencies {
174
  * @since 5.2.0
175
  */
176
  public function add_php_function_notices() {
177
-
178
  $missing_functions = $this->get_missing_php_functions();
179
-
180
  if ( count( $missing_functions ) > 0 ) {
181
-
182
  $message = sprintf(
183
  /* translators: Placeholders: %1$s - plugin name, %2$s - a PHP function/comma-separated list of PHP functions */
184
  _n(
185
  '%1$s requires the %2$s PHP function to exist. Contact your host or server administrator to install and configure the missing function.',
186
  '%1$s requires the following PHP functions to exist: %2$s. Contact your host or server administrator to install and configure the missing functions.',
187
  count( $missing_functions ),
188
- 'woocommerce-plugin-framework'
189
  ),
190
  esc_html( $this->get_plugin()->get_plugin_name() ),
191
  '<strong>' . implode( ', ', $missing_functions ) . '</strong>'
192
  );
193
-
194
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-functions', $message, 'error' );
195
  }
196
  }
@@ -202,41 +161,28 @@ class SV_WC_Plugin_Dependencies {
202
  * @since 5.2.0
203
  */
204
  public function add_php_settings_notices() {
205
-
206
  if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) {
207
-
208
  $bad_settings = $this->get_incompatible_php_settings();
209
-
210
  if ( count( $bad_settings ) > 0 ) {
211
-
212
  $message = sprintf(
213
  /* translators: Placeholders: %s - plugin name */
214
  __( '%s may behave unexpectedly because the following PHP configuration settings are required:' ),
215
  '<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>'
216
  );
217
-
218
  $message .= '<ul>';
219
-
220
  foreach ( $bad_settings as $setting => $values ) {
221
-
222
  $setting_message = '<code>' . $setting . ' = ' . $values['expected'] . '</code>';
223
-
224
  if ( ! empty( $values['type'] ) && 'min' === $values['type'] ) {
225
-
226
  $setting_message = sprintf(
227
  /** translators: Placeholders: %s - a PHP setting value */
228
- __( '%s or higher', 'woocommerce-plugin-framework' ),
229
  $setting_message
230
  );
231
  }
232
-
233
  $message .= '<li>' . $setting_message . '</li>';
234
  }
235
-
236
  $message .= '</ul>';
237
-
238
- $message .= __( 'Please contact your hosting provider or server administrator to configure these settings.', 'woocommerce-plugin-framework' );
239
-
240
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-incompatibile-php-settings', $message, 'warning' );
241
  }
242
  }
@@ -249,25 +195,20 @@ class SV_WC_Plugin_Dependencies {
249
  * @since 5.2.0
250
  */
251
  protected function add_deprecated_notices() {
252
-
253
  // add a notice for PHP < 5.6
254
  if ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
255
-
256
  $message = '<p>';
257
-
258
  $message .= sprintf(
259
  /* translators: Placeholders: %1$s - <strong>, %2$s - </strong> */
260
  __( 'Hey there! We\'ve noticed that your server is running %1$san outdated version of PHP%2$s, which is the programming language that WooCommerce and its extensions are built on.
261
  The PHP version that is currently used for your site is no longer maintained, nor %1$sreceives security updates%2$s; newer versions are faster and more secure.
262
  As a result, %3$s no longer supports this version and you should upgrade PHP as soon as possible.
263
- Your hosting provider can do this for you. %4$sHere are some resources to help you upgrade%5$s and to explain PHP versions further.', 'woocommerce-plugin-framework' ),
264
  '<strong>', '</strong>',
265
  esc_html( $this->get_plugin()->get_plugin_name() ),
266
  '<a href="http://skyver.ge/upgradephp">', '</a>'
267
  );
268
-
269
  $message .= '</p>';
270
-
271
  $this->add_admin_notice( 'sv-wc-deprecated-php-version', $message, 'error' );
272
  }
273
  }
@@ -283,29 +224,7 @@ class SV_WC_Plugin_Dependencies {
283
  * @param string $type notice type
284
  */
285
  protected function add_admin_notice( $id, $message, $type = 'info' ) {
286
-
287
- $notice_class = $type;
288
-
289
- // translate the types into WP notice classes
290
- switch ( $type ) {
291
-
292
- case 'error':
293
- $notice_class = 'notice-error';
294
- break;
295
-
296
- case 'warning':
297
- $notice_class = 'notice-warning';
298
- break;
299
-
300
- case 'info':
301
- $notice_class = 'notice-info';
302
- break;
303
-
304
- case 'success':
305
- $notice_class = 'notice-success';
306
- break;
307
- }
308
-
309
  $this->get_plugin()->get_admin_notice_handler()->add_admin_notice( $message, $id, array(
310
  'notice_class' => $notice_class,
311
  ) );
@@ -322,7 +241,6 @@ class SV_WC_Plugin_Dependencies {
322
  * @return array
323
  */
324
  public function get_active_scripts_optimization_plugins() {
325
-
326
  /**
327
  * Filters script optimization plugins to look for.
328
  *
@@ -339,17 +257,12 @@ class SV_WC_Plugin_Dependencies {
339
  'wpFastestCache.php' => 'WP Fastest Cache',
340
  'wp-rocket.php' => 'WP Rocket',
341
  ] );
342
-
343
  $active_plugins = [];
344
-
345
  foreach ( $plugins as $filename => $plugin_name ) {
346
-
347
  if ( $this->get_plugin()->is_plugin_active( $filename ) ) {
348
-
349
  $active_plugins[ $filename ] = $plugin_name;
350
  }
351
  }
352
-
353
  return $active_plugins;
354
  }
355
 
@@ -362,7 +275,6 @@ class SV_WC_Plugin_Dependencies {
362
  * @return bool
363
  */
364
  public function is_scripts_optimization_plugin_active() {
365
-
366
  return ! empty( $this->get_active_scripts_optimization_plugins() );
367
  }
368
 
@@ -378,16 +290,12 @@ class SV_WC_Plugin_Dependencies {
378
  * @return array
379
  */
380
  public function get_missing_php_extensions() {
381
-
382
  $missing_extensions = [];
383
-
384
  foreach ( $this->get_php_extensions() as $extension ) {
385
-
386
  if ( ! extension_loaded( $extension ) ) {
387
  $missing_extensions[] = $extension;
388
  }
389
  }
390
-
391
  return $missing_extensions;
392
  }
393
 
@@ -400,7 +308,6 @@ class SV_WC_Plugin_Dependencies {
400
  * @return array
401
  */
402
  public function get_php_extensions() {
403
-
404
  return $this->php_extensions;
405
  }
406
 
@@ -413,16 +320,12 @@ class SV_WC_Plugin_Dependencies {
413
  * @return array
414
  */
415
  public function get_missing_php_functions() {
416
-
417
  $missing_functions = [];
418
-
419
  foreach ( $this->get_php_functions() as $function ) {
420
-
421
  if ( ! extension_loaded( $function ) ) {
422
  $missing_functions[] = $function;
423
  }
424
  }
425
-
426
  return $missing_functions;
427
  }
428
 
@@ -435,7 +338,6 @@ class SV_WC_Plugin_Dependencies {
435
  * @return array
436
  */
437
  public function get_php_functions() {
438
-
439
  return $this->php_functions;
440
  }
441
 
@@ -448,37 +350,25 @@ class SV_WC_Plugin_Dependencies {
448
  * @return array
449
  */
450
  public function get_incompatible_php_settings() {
451
-
452
  $incompatible_settings = [];
453
-
454
  if ( function_exists( 'ini_get' ) ) {
455
-
456
  foreach ( $this->get_php_settings() as $setting => $expected ) {
457
-
458
  $actual = ini_get( $setting );
459
-
460
  if ( ! $actual ) {
461
  continue;
462
  }
463
-
464
  if ( is_int( $expected ) ) {
465
-
466
  // determine if this is a size string, like "10MB"
467
- $is_size = ! is_numeric( substr( $actual, -1 ) );
468
-
469
  $actual_num = $is_size ? wc_let_to_num( $actual ) : $actual;
470
-
471
  if ( $actual_num < $expected ) {
472
-
473
  $incompatible_settings[ $setting ] = [
474
  'expected' => $is_size ? size_format( $expected ) : $expected,
475
  'actual' => $is_size ? size_format( $actual_num ) : $actual,
476
  'type' => 'min',
477
  ];
478
  }
479
-
480
  } elseif ( $actual !== $expected ) {
481
-
482
  $incompatible_settings[ $setting ] = [
483
  'expected' => $expected,
484
  'actual' => $actual,
@@ -486,7 +376,6 @@ class SV_WC_Plugin_Dependencies {
486
  }
487
  }
488
  }
489
-
490
  return $incompatible_settings;
491
  }
492
 
@@ -499,7 +388,6 @@ class SV_WC_Plugin_Dependencies {
499
  * @return array
500
  */
501
  public function get_php_settings() {
502
-
503
  return $this->php_settings;
504
  }
505
 
@@ -509,15 +397,9 @@ class SV_WC_Plugin_Dependencies {
509
  *
510
  * @since 5.2.0
511
  *
512
- * @return SV_WC_Plugin
513
  */
514
  protected function get_plugin() {
515
-
516
  return $this->plugin;
517
  }
518
-
519
-
520
  }
521
-
522
-
523
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework\Plugin;
8
 
9
+ use WooCommerce\Facebook\Framework\Plugin;
 
 
10
 
11
+ defined( 'ABSPATH' ) or exit;
12
 
13
  /**
14
  * Plugin dependencies handler.
 
 
15
  */
16
+ class Dependencies {
 
17
 
18
  /** @var array required PHP extensions */
19
  protected $php_extensions = array();
24
  /** @var array required PHP settings */
25
  protected $php_settings = array();
26
 
27
+ /** @var Plugin plugin instance */
28
  protected $plugin;
29
 
30
 
33
  *
34
  * @since 5.2.0
35
  *
36
+ * @param Plugin $plugin plugin instance
37
  * @param array $args {
38
  * PHP extension, function, and settings dependencies
39
  *
42
  * @type array $php_settings PHP settings dependencies
43
  * }
44
  */
45
+ public function __construct( Plugin $plugin, $args = array() ) {
46
+ $this->plugin = $plugin;
47
+ $dependencies = $this->parse_dependencies( $args );
 
 
 
48
  $this->php_extensions = (array) $dependencies['php_extensions'];
49
  $this->php_functions = (array) $dependencies['php_functions'];
50
  $this->php_settings = (array) $dependencies['php_settings'];
 
51
  // add the action & filter hooks
52
  $this->add_hooks();
53
  }
62
  * @return array
63
  */
64
  private function parse_dependencies( $args ) {
 
65
  $dependencies = wp_parse_args( $args, array(
66
  'php_extensions' => array(),
67
  'php_functions' => array(),
68
  'php_settings' => array(),
69
  ) );
 
70
  $default_settings = array(
71
  'suhosin.post.max_array_index_length' => 256,
72
  'suhosin.post.max_totalname_length' => 65535,
75
  'suhosin.request.max_totalname_length' => 65535,
76
  'suhosin.request.max_vars' => 1024,
77
  );
 
78
  // override any default settings requirements if the plugin specifies them
79
  if ( ! empty( $dependencies['php_settings'] ) ) {
80
  $dependencies['php_settings'] = array_merge( $default_settings, $dependencies['php_settings'] );
81
  }
 
82
  return $dependencies;
83
  }
84
 
89
  * @since 5.2.0
90
  */
91
  protected function add_hooks() {
 
92
  // add the admin dependency notices
93
  add_action( 'admin_init', array( $this, 'add_admin_notices' ) );
94
  }
100
  * @since 5.2.0
101
  */
102
  public function add_admin_notices() {
 
103
  $this->add_php_extension_notices();
104
  $this->add_php_function_notices();
105
  $this->add_php_settings_notices();
 
106
  $this->add_deprecated_notices();
107
  }
108
 
113
  * @since 5.2.0
114
  */
115
  public function add_php_extension_notices() {
 
116
  $missing_extensions = $this->get_missing_php_extensions();
 
117
  if ( count( $missing_extensions ) > 0 ) {
 
118
  $message = sprintf(
119
  /* translators: Placeholders: %1$s - plugin name, %2$s - a PHP extension/comma-separated list of PHP extensions */
120
  _n(
121
  '%1$s requires the %2$s PHP extension to function. Contact your host or server administrator to install and configure the missing extension.',
122
  '%1$s requires the following PHP extensions to function: %2$s. Contact your host or server administrator to install and configure the missing extensions.',
123
  count( $missing_extensions ),
124
+ 'facebook-for-woocommerce'
125
  ),
126
  esc_html( $this->get_plugin()->get_plugin_name() ),
127
  '<strong>' . implode( ', ', $missing_extensions ) . '</strong>'
128
  );
 
129
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-extensions', $message, 'error' );
130
  }
131
  }
137
  * @since 5.2.0
138
  */
139
  public function add_php_function_notices() {
 
140
  $missing_functions = $this->get_missing_php_functions();
 
141
  if ( count( $missing_functions ) > 0 ) {
 
142
  $message = sprintf(
143
  /* translators: Placeholders: %1$s - plugin name, %2$s - a PHP function/comma-separated list of PHP functions */
144
  _n(
145
  '%1$s requires the %2$s PHP function to exist. Contact your host or server administrator to install and configure the missing function.',
146
  '%1$s requires the following PHP functions to exist: %2$s. Contact your host or server administrator to install and configure the missing functions.',
147
  count( $missing_functions ),
148
+ 'facebook-for-woocommerce'
149
  ),
150
  esc_html( $this->get_plugin()->get_plugin_name() ),
151
  '<strong>' . implode( ', ', $missing_functions ) . '</strong>'
152
  );
 
153
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-functions', $message, 'error' );
154
  }
155
  }
161
  * @since 5.2.0
162
  */
163
  public function add_php_settings_notices() {
 
164
  if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) {
 
165
  $bad_settings = $this->get_incompatible_php_settings();
 
166
  if ( count( $bad_settings ) > 0 ) {
 
167
  $message = sprintf(
168
  /* translators: Placeholders: %s - plugin name */
169
  __( '%s may behave unexpectedly because the following PHP configuration settings are required:' ),
170
  '<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>'
171
  );
 
172
  $message .= '<ul>';
 
173
  foreach ( $bad_settings as $setting => $values ) {
 
174
  $setting_message = '<code>' . $setting . ' = ' . $values['expected'] . '</code>';
 
175
  if ( ! empty( $values['type'] ) && 'min' === $values['type'] ) {
 
176
  $setting_message = sprintf(
177
  /** translators: Placeholders: %s - a PHP setting value */
178
+ __( '%s or higher', 'facebook-for-woocommerce' ),
179
  $setting_message
180
  );
181
  }
 
182
  $message .= '<li>' . $setting_message . '</li>';
183
  }
 
184
  $message .= '</ul>';
185
+ $message .= __( 'Please contact your hosting provider or server administrator to configure these settings.', 'facebook-for-woocommerce' );
 
 
186
  $this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-incompatibile-php-settings', $message, 'warning' );
187
  }
188
  }
195
  * @since 5.2.0
196
  */
197
  protected function add_deprecated_notices() {
 
198
  // add a notice for PHP < 5.6
199
  if ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
 
200
  $message = '<p>';
 
201
  $message .= sprintf(
202
  /* translators: Placeholders: %1$s - <strong>, %2$s - </strong> */
203
  __( 'Hey there! We\'ve noticed that your server is running %1$san outdated version of PHP%2$s, which is the programming language that WooCommerce and its extensions are built on.
204
  The PHP version that is currently used for your site is no longer maintained, nor %1$sreceives security updates%2$s; newer versions are faster and more secure.
205
  As a result, %3$s no longer supports this version and you should upgrade PHP as soon as possible.
206
+ Your hosting provider can do this for you. %4$sHere are some resources to help you upgrade%5$s and to explain PHP versions further.', 'facebook-for-woocommerce' ),
207
  '<strong>', '</strong>',
208
  esc_html( $this->get_plugin()->get_plugin_name() ),
209
  '<a href="http://skyver.ge/upgradephp">', '</a>'
210
  );
 
211
  $message .= '</p>';
 
212
  $this->add_admin_notice( 'sv-wc-deprecated-php-version', $message, 'error' );
213
  }
214
  }
224
  * @param string $type notice type
225
  */
226
  protected function add_admin_notice( $id, $message, $type = 'info' ) {
227
+ $notice_class = 'notice-' . $type;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  $this->get_plugin()->get_admin_notice_handler()->add_admin_notice( $message, $id, array(
229
  'notice_class' => $notice_class,
230
  ) );
241
  * @return array
242
  */
243
  public function get_active_scripts_optimization_plugins() {
 
244
  /**
245
  * Filters script optimization plugins to look for.
246
  *
257
  'wpFastestCache.php' => 'WP Fastest Cache',
258
  'wp-rocket.php' => 'WP Rocket',
259
  ] );
 
260
  $active_plugins = [];
 
261
  foreach ( $plugins as $filename => $plugin_name ) {
 
262
  if ( $this->get_plugin()->is_plugin_active( $filename ) ) {
 
263
  $active_plugins[ $filename ] = $plugin_name;
264
  }
265
  }
 
266
  return $active_plugins;
267
  }
268
 
275
  * @return bool
276
  */
277
  public function is_scripts_optimization_plugin_active() {
 
278
  return ! empty( $this->get_active_scripts_optimization_plugins() );
279
  }
280
 
290
  * @return array
291
  */
292
  public function get_missing_php_extensions() {
 
293
  $missing_extensions = [];
 
294
  foreach ( $this->get_php_extensions() as $extension ) {
 
295
  if ( ! extension_loaded( $extension ) ) {
296
  $missing_extensions[] = $extension;
297
  }
298
  }
 
299
  return $missing_extensions;
300
  }
301
 
308
  * @return array
309
  */
310
  public function get_php_extensions() {
 
311
  return $this->php_extensions;
312
  }
313
 
320
  * @return array
321
  */
322
  public function get_missing_php_functions() {
 
323
  $missing_functions = [];
 
324
  foreach ( $this->get_php_functions() as $function ) {
 
325
  if ( ! extension_loaded( $function ) ) {
326
  $missing_functions[] = $function;
327
  }
328
  }
 
329
  return $missing_functions;
330
  }
331
 
338
  * @return array
339
  */
340
  public function get_php_functions() {
 
341
  return $this->php_functions;
342
  }
343
 
350
  * @return array
351
  */
352
  public function get_incompatible_php_settings() {
 
353
  $incompatible_settings = [];
 
354
  if ( function_exists( 'ini_get' ) ) {
 
355
  foreach ( $this->get_php_settings() as $setting => $expected ) {
 
356
  $actual = ini_get( $setting );
 
357
  if ( ! $actual ) {
358
  continue;
359
  }
 
360
  if ( is_int( $expected ) ) {
 
361
  // determine if this is a size string, like "10MB"
362
+ $is_size = ! is_numeric( substr( $actual, -1 ) );
 
363
  $actual_num = $is_size ? wc_let_to_num( $actual ) : $actual;
 
364
  if ( $actual_num < $expected ) {
 
365
  $incompatible_settings[ $setting ] = [
366
  'expected' => $is_size ? size_format( $expected ) : $expected,
367
  'actual' => $is_size ? size_format( $actual_num ) : $actual,
368
  'type' => 'min',
369
  ];
370
  }
 
371
  } elseif ( $actual !== $expected ) {
 
372
  $incompatible_settings[ $setting ] = [
373
  'expected' => $expected,
374
  'actual' => $actual,
376
  }
377
  }
378
  }
 
379
  return $incompatible_settings;
380
  }
381
 
388
  * @return array
389
  */
390
  public function get_php_settings() {
 
391
  return $this->php_settings;
392
  }
393
 
397
  *
398
  * @since 5.2.0
399
  *
400
+ * @return Plugin
401
  */
402
  protected function get_plugin() {
 
403
  return $this->plugin;
404
  }
 
 
405
  }
 
 
 
includes/Framework/Plugin/Exception.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+ /**
4
+ * Facebook for WooCommerce.
5
+ */
6
+
7
+ namespace WooCommerce\Facebook\Framework\Plugin;
8
+
9
+ defined( 'ABSPATH' ) or exit;
10
+
11
+ /**
12
+ * Plugin Framework Exception - generic Exception
13
+ */
14
+ class Exception extends \Exception {}
vendor/skyverge/wc-plugin-framework/woocommerce/utilities/class-sv-wp-async-request.php → includes/Framework/Utilities/AsyncRequest.php RENAMED
@@ -1,35 +1,13 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Utilities
20
- * @author SkyVerge / Delicious Brains
21
- * @copyright Copyright (c) 2015-2020 Delicious Brains Inc.
22
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
23
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
24
  */
25
 
26
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
27
 
28
  defined( 'ABSPATH' ) or exit;
29
 
30
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_Async_Request' ) ) :
31
-
32
-
33
  /**
34
  * SkyVerge Wordpress Async Request class
35
  *
@@ -43,7 +21,7 @@ if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_A
43
  *
44
  * @since 4.4.0
45
  */
46
- abstract class SV_WP_Async_Request {
47
 
48
 
49
  /** @var string request prefix */
@@ -56,7 +34,7 @@ abstract class SV_WP_Async_Request {
56
  protected $identifier;
57
 
58
  /** @var array request data */
59
- protected $data = array();
60
 
61
 
62
  /**
@@ -77,7 +55,7 @@ abstract class SV_WP_Async_Request {
77
  *
78
  * @since 4.4.0
79
  * @param array $data
80
- * @return SV_WP_Async_Request
81
  */
82
  public function set_data( $data ) {
83
  $this->data = $data;
@@ -184,9 +162,4 @@ abstract class SV_WP_Async_Request {
184
  * @since 4.4.0
185
  */
186
  abstract protected function handle();
187
-
188
-
189
  }
190
-
191
-
192
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework\Utilities;
8
 
9
  defined( 'ABSPATH' ) or exit;
10
 
 
 
 
11
  /**
12
  * SkyVerge Wordpress Async Request class
13
  *
21
  *
22
  * @since 4.4.0
23
  */
24
+ abstract class AsyncRequest {
25
 
26
 
27
  /** @var string request prefix */
34
  protected $identifier;
35
 
36
  /** @var array request data */
37
+ protected $data = [];
38
 
39
 
40
  /**
55
  *
56
  * @since 4.4.0
57
  * @param array $data
58
+ * @return AsyncRequest
59
  */
60
  public function set_data( $data ) {
61
  $this->data = $data;
162
  * @since 4.4.0
163
  */
164
  abstract protected function handle();
 
 
165
  }
 
 
 
vendor/skyverge/wc-plugin-framework/woocommerce/utilities/class-sv-wp-background-job-handler.php → includes/Framework/Utilities/BackgroundJobHandler.php RENAMED
@@ -1,34 +1,14 @@
1
  <?php
 
2
  /**
3
- * WooCommerce Plugin Framework
4
- *
5
- * This source file is subject to the GNU General Public License v3.0
6
- * that is bundled with this package in the file license.txt.
7
- * It is also available through the world-wide-web at this URL:
8
- * http://www.gnu.org/licenses/gpl-3.0.html
9
- * If you did not receive a copy of the license and are unable to
10
- * obtain it through the world-wide-web, please send an email
11
- * to license@skyverge.com so we can send you a copy immediately.
12
- *
13
- * DISCLAIMER
14
- *
15
- * Do not edit or add to this file if you wish to upgrade the plugin to newer
16
- * versions in the future. If you wish to customize the plugin for your
17
- * needs please refer to http://www.skyverge.com
18
- *
19
- * @package SkyVerge/WooCommerce/Utilities
20
- * @author SkyVerge / Delicious Brains
21
- * @copyright Copyright (c) 2015-2020 Delicious Brains Inc.
22
- * @copyright Copyright (c) 2013-2020, SkyVerge, Inc.
23
- * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
24
  */
25
 
26
- namespace SkyVerge\WooCommerce\PluginFramework\v5_10_0;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
-
30
- if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_Background_Job_Handler' ) ) :
31
 
 
32
 
33
  /**
34
  * SkyVerge WordPress Background Job Handler class
@@ -54,7 +34,7 @@ if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_10_0\\SV_WP_B
54
  *
55
  * @since 4.4.0
56
  */
57
- abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
58
 
59
 
60
  /** @var string async request prefix */
@@ -85,12 +65,9 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
85
  * @since 4.4.0
86
  */
87
  public function __construct() {
88
-
89
  parent::__construct();
90
-
91
  $this->cron_hook_identifier = $this->identifier . '_cron';
92
  $this->cron_interval_identifier = $this->identifier . '_cron_interval';
93
-
94
  $this->add_hooks();
95
  }
96
 
@@ -117,7 +94,7 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
117
  * Dispatch
118
  *
119
  * @since 4.4.0
120
- * @return array|WP_Error
121
  */
122
  public function dispatch() {
123
 
@@ -207,12 +184,10 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
207
  * @return bool True if processing is running, false otherwise
208
  */
209
  protected function is_process_running() {
210
-
211
  // add a random artificial delay to prevent a race condition if 2 or more processes are trying to
212
  // process the job queue at the very same moment in time and neither of them have yet set the lock
213
  // before the others are calling this method
214
  usleep( rand( 100000, 300000 ) );
215
-
216
  return (bool) get_transient( "{$this->identifier}_process_lock" );
217
  }
218
 
@@ -227,13 +202,10 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
227
  * @since 4.4.0
228
  */
229
  protected function lock_process() {
230
-
231
  // set start time of current process
232
  $this->start_time = time();
233
-
234
  // set lock duration to 1 minute by default
235
  $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60;
236
-
237
  /**
238
  * Filter the queue lock time
239
  *
@@ -241,7 +213,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
241
  * @param int $lock_duration Lock duration in seconds
242
  */
243
  $lock_duration = apply_filters( "{$this->identifier}_queue_lock_time", $lock_duration );
244
-
245
  set_transient( "{$this->identifier}_process_lock", microtime(), $lock_duration );
246
  }
247
 
@@ -252,12 +223,10 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
252
  * Unlock the process so that other instances can spawn.
253
  *
254
  * @since 4.4.0
255
- * @return SV_WP_Background_Job_Handler
256
  */
257
  protected function unlock_process() {
258
-
259
  delete_transient( "{$this->identifier}_process_lock" );
260
-
261
  return $this;
262
  }
263
 
@@ -314,7 +283,7 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
314
  $memory_limit = '32G';
315
  }
316
 
317
- return SV_WC_Plugin_Compatibility::convert_hr_to_bytes( $memory_limit );
318
  }
319
 
320
 
@@ -641,11 +610,11 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
641
  $data_key = $this->data_key;
642
 
643
  if ( ! isset( $job->{$data_key} ) ) {
644
- throw new \Exception( sprintf( __( 'Job data key "%s" not set', 'woocommerce-plugin-framework' ), $data_key ) );
645
  }
646
 
647
  if ( ! is_array( $job->{$data_key} ) ) {
648
- throw new \Exception( sprintf( __( 'Job data key "%s" is not an array', 'woocommerce-plugin-framework' ), $data_key ) );
649
  }
650
 
651
  $data = $job->{$data_key};
@@ -705,19 +674,14 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
705
  * @return \stdClass|object|false on failure
706
  */
707
  public function update_job( $job ) {
708
-
709
  if ( is_string( $job ) ) {
710
  $job = $this->get_job( $job );
711
  }
712
-
713
  if ( ! $job ) {
714
  return false;
715
  }
716
-
717
  $job->updated_at = current_time( 'mysql' );
718
-
719
  $this->update_job_option( $job );
720
-
721
  /**
722
  * Runs when a job is updated.
723
  *
@@ -726,7 +690,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
726
  * @param \stdClass|object $job the updated job
727
  */
728
  do_action( "{$this->identifier}_job_updated", $job );
729
-
730
  return $job;
731
  }
732
 
@@ -740,20 +703,15 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
740
  * @return \stdClass|object|false on failure
741
  */
742
  public function complete_job( $job ) {
743
-
744
  if ( is_string( $job ) ) {
745
  $job = $this->get_job( $job );
746
  }
747
-
748
  if ( ! $job ) {
749
  return false;
750
  }
751
-
752
  $job->status = 'completed';
753
  $job->completed_at = current_time( 'mysql' );
754
-
755
  $this->update_job_option( $job );
756
-
757
  /**
758
  * Runs when a job is completed.
759
  *
@@ -762,7 +720,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
762
  * @param \stdClass|object $job the completed job
763
  */
764
  do_action( "{$this->identifier}_job_complete", $job );
765
-
766
  return $job;
767
  }
768
 
@@ -781,24 +738,18 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
781
  * @return \stdClass|false on failure
782
  */
783
  public function fail_job( $job, $reason = '' ) {
784
-
785
  if ( is_string( $job ) ) {
786
  $job = $this->get_job( $job );
787
  }
788
-
789
  if ( ! $job ) {
790
  return false;
791
  }
792
-
793
  $job->status = 'failed';
794
  $job->failed_at = current_time( 'mysql' );
795
-
796
  if ( $reason ) {
797
  $job->failure_reason = $reason;
798
  }
799
-
800
  $this->update_job_option( $job );
801
-
802
  /**
803
  * Runs when a job is failed.
804
  *
@@ -807,7 +758,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
807
  * @param \stdClass|object $job the failed job
808
  */
809
  do_action( "{$this->identifier}_job_failed", $job );
810
-
811
  return $job;
812
  }
813
 
@@ -822,17 +772,13 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
822
  */
823
  public function delete_job( $job ) {
824
  global $wpdb;
825
-
826
  if ( is_string( $job ) ) {
827
  $job = $this->get_job( $job );
828
  }
829
-
830
  if ( ! $job ) {
831
  return false;
832
  }
833
-
834
  $wpdb->delete( $wpdb->options, array( 'option_name' => "{$this->identifier}_job_{$job->id}" ) );
835
-
836
  /**
837
  * Runs after a job is deleted.
838
  *
@@ -853,7 +799,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
853
  * @since 4.4.0
854
  */
855
  protected function complete() {
856
-
857
  // unschedule the cron healthcheck
858
  $this->clear_scheduled_event();
859
  }
@@ -867,9 +812,7 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
867
  * @return array
868
  */
869
  public function schedule_cron_healthcheck( $schedules ) {
870
-
871
  $interval = property_exists( $this, 'cron_interval' ) ? $this->cron_interval : 5;
872
-
873
  /**
874
  * Filter cron health check interval
875
  *
@@ -877,13 +820,11 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
877
  * @param int $interval Interval in minutes
878
  */
879
  $interval = apply_filters( "{$this->identifier}_cron_interval", $interval );
880
-
881
  // adds every 5 minutes to the existing schedules.
882
  $schedules[ $this->identifier . '_cron_interval' ] = array(
883
  'interval' => MINUTE_IN_SECONDS * $interval,
884
  'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
885
  );
886
-
887
  return $schedules;
888
  }
889
 
@@ -897,18 +838,15 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
897
  * @since 4.4.0
898
  */
899
  public function handle_cron_healthcheck() {
900
-
901
  if ( $this->is_process_running() ) {
902
  // background process already running
903
  exit;
904
  }
905
-
906
  if ( $this->is_queue_empty() ) {
907
  // no data to process
908
  $this->clear_scheduled_event();
909
  exit;
910
  }
911
-
912
  $this->dispatch();
913
  }
914
 
@@ -919,9 +857,7 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
919
  * @since 4.4.0
920
  */
921
  protected function schedule_event() {
922
-
923
  if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
924
-
925
  // schedule the health check to fire after 30 seconds from now, as to not create a race condition
926
  // with job process lock on servers that fire & handle cron events instantly
927
  wp_schedule_event( time() + 30, $this->cron_interval_identifier, $this->cron_hook_identifier );
@@ -935,9 +871,7 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
935
  * @since 4.4.0
936
  */
937
  protected function clear_scheduled_event() {
938
-
939
  $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
940
-
941
  if ( $timestamp ) {
942
  wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
943
  }
@@ -967,14 +901,10 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
967
  * @param \stdClass|object $job the job being processed
968
  */
969
  public function handle_shutdown( $job ) {
970
-
971
  $error = error_get_last();
972
-
973
  // if shutting down because of a fatal error, fail the job
974
  if ( $error && E_ERROR === $error['type'] ) {
975
-
976
  $this->fail_job( $job, $error['message'] );
977
-
978
  $this->unlock_process();
979
  }
980
  }
@@ -1010,14 +940,12 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
1010
  * @return bool
1011
  */
1012
  public function test_connection() {
1013
-
1014
  $test_url = add_query_arg( 'action', "{$this->identifier}_test", admin_url( 'admin-ajax.php' ) );
1015
  $result = wp_safe_remote_get( $test_url );
1016
  $body = ! is_wp_error( $result ) ? wp_remote_retrieve_body( $result ) : null;
1017
-
1018
  // some servers may add a UTF8-BOM at the beginning of the response body, so we check if our test
1019
  // string is included in the body, as an equal check would produce a false negative test result
1020
- return $body && SV_WC_Helper::str_exists( $body, '[TEST_LOOPBACK]' );
1021
  }
1022
 
1023
 
@@ -1045,9 +973,9 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
1045
 
1046
  // this key is not unique to the plugin to avoid duplicate tools
1047
  $tools['sv_wc_background_job_test'] = array(
1048
- 'name' => __( 'Background Processing Test', 'woocommerce-plugin-framework' ),
1049
- 'button' => __( 'Run Test', 'woocommerce-plugin-framework' ),
1050
- 'desc' => __( 'This tool will test whether your server is capable of processing background jobs.', 'woocommerce-plugin-framework' ),
1051
  'callback' => array( $this, 'run_debug_tool' ),
1052
  );
1053
 
@@ -1065,10 +993,10 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
1065
  public function run_debug_tool() {
1066
 
1067
  if ( $this->test_connection() ) {
1068
- $this->debug_message = esc_html__( 'Success! You should be able to process background jobs.', 'woocommerce-plugin-framework' );
1069
  $result = true;
1070
  } else {
1071
- $this->debug_message = esc_html__( 'Could not connect. Please ask your hosting company to ensure your server has loopback connections enabled.', 'woocommerce-plugin-framework' );
1072
  $result = false;
1073
  }
1074
 
@@ -1090,11 +1018,9 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
1090
  * @return string the updated text
1091
  */
1092
  public function translate_success_message( $translated, $original, $domain ) {
1093
-
1094
  if ( 'woocommerce' === $domain && ( 'Tool ran.' === $original || 'There was an error calling %s' === $original ) ) {
1095
  $translated = $this->debug_message;
1096
  }
1097
-
1098
  return $translated;
1099
  }
1100
 
@@ -1110,12 +1036,6 @@ abstract class SV_WP_Background_Job_Handler extends SV_WP_Async_Request {
1110
  * @return string
1111
  */
1112
  public function get_identifier() {
1113
-
1114
  return $this->identifier;
1115
  }
1116
-
1117
-
1118
  }
1119
-
1120
-
1121
- endif;
1
  <?php
2
+ // phpcs:ignoreFile
3
  /**
4
+ * Facebook for WooCommerce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  */
6
 
7
+ namespace WooCommerce\Facebook\Framework\Utilities;
8
 
9
+ use WooCommerce\Facebook\Framework\Plugin\Compatibility;
 
 
10
 
11
+ defined( 'ABSPATH' ) or exit;
12
 
13
  /**
14
  * SkyVerge WordPress Background Job Handler class
34
  *
35
  * @since 4.4.0
36
  */
37
+ abstract class BackgroundJobHandler extends AsyncRequest {
38
 
39
 
40
  /** @var string async request prefix */
65
  * @since 4.4.0
66
  */
67
  public function __construct() {
 
68
  parent::__construct();
 
69
  $this->cron_hook_identifier = $this->identifier . '_cron';
70
  $this->cron_interval_identifier = $this->identifier . '_cron_interval';
 
71
  $this->add_hooks();
72
  }
73
 
94
  * Dispatch
95
  *
96
  * @since 4.4.0
97
+ * @return array|\WP_Error
98
  */
99
  public function dispatch() {
100
 
184
  * @return bool True if processing is running, false otherwise
185
  */
186
  protected function is_process_running() {
 
187
  // add a random artificial delay to prevent a race condition if 2 or more processes are trying to
188
  // process the job queue at the very same moment in time and neither of them have yet set the lock
189
  // before the others are calling this method
190
  usleep( rand( 100000, 300000 ) );
 
191
  return (bool) get_transient( "{$this->identifier}_process_lock" );
192
  }
193
 
202
  * @since 4.4.0
203
  */
204
  protected function lock_process() {
 
205
  // set start time of current process
206
  $this->start_time = time();
 
207
  // set lock duration to 1 minute by default
208
  $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60;
 
209
  /**
210
  * Filter the queue lock time
211
  *
213
  * @param int $lock_duration Lock duration in seconds
214
  */
215
  $lock_duration = apply_filters( "{$this->identifier}_queue_lock_time", $lock_duration );
 
216
  set_transient( "{$this->identifier}_process_lock", microtime(), $lock_duration );
217
  }
218
 
223
  * Unlock the process so that other instances can spawn.
224
  *
225
  * @since 4.4.0
226
+ * @return BackgroundJobHandler
227
  */
228
  protected function unlock_process() {
 
229
  delete_transient( "{$this->identifier}_process_lock" );
 
230
  return $this;
231
  }
232
 
283
  $memory_limit = '32G';
284
  }
285
 
286
+ return Compatibility::convert_hr_to_bytes( $memory_limit );
287
  }
288
 
289
 
610
  $data_key = $this->data_key;
611
 
612
  if ( ! isset( $job->{$data_key} ) ) {
613
+ throw new \Exception( sprintf( __( 'Job data key "%s" not set', 'facebook-for-woocommerce' ), $data_key ) );
614
  }
615
 
616
  if ( ! is_array( $job->{$data_key} ) ) {
617
+ throw new \Exception( sprintf( __( 'Job data key "%s" is not an array', 'facebook-for-woocommerce' ), $data_key ) );
618
  }
619
 
620
  $data = $job->{$data_key};
674
  * @return \stdClass|object|false on failure
675
  */
676
  public function update_job( $job ) {
 
677
  if ( is_string( $job ) ) {
678
  $job = $this->get_job( $job );
679
  }
 
680
  if ( ! $job ) {
681
  return false;
682
  }
 
683
  $job->updated_at = current_time( 'mysql' );
 
684
  $this->update_job_option( $job );
 
685
  /**
686
  * Runs when a job is updated.
687
  *
690
  * @param \stdClass|object $job the updated job
691
  */
692
  do_action( "{$this->identifier}_job_updated", $job );
 
693
  return $job;
694
  }
695
 
703
  * @return \stdClass|object|false on failure
704
  */
705
  public function complete_job( $job ) {
 
706
  if ( is_string( $job ) ) {
707
  $job = $this->get_job( $job );
708
  }
 
709
  if ( ! $job ) {
710
  return false;
711
  }
 
712
  $job->status = 'completed';
713
  $job->completed_at = current_time( 'mysql' );
 
714
  $this->update_job_option( $job );
 
715
  /**
716
  * Runs when a job is completed.
717
  *
720
  * @param \stdClass|object $job the completed job
721
  */
722
  do_action( "{$this->identifier}_job_complete", $job );
 
723
  return $job;
724
  }
725
 
738
  * @return \stdClass|false on failure
739
  */
740
  public function fail_job( $job, $reason = '' ) {
 
741
  if ( is_string( $job ) ) {
742
  $job = $this->get_job( $job );
743
  }
 
744
  if ( ! $job ) {
745
  return false;
746
  }
 
747
  $job->status = 'failed';
748
  $job->failed_at = current_time( 'mysql' );
 
749
  if ( $reason ) {
750
  $job->failure_reason = $reason;
751
  }
 
752
  $this->update_job_option( $job );
 
753
  /**
754
  * Runs when a job is failed.
755
  *
758
  * @param \stdClass|object $job the failed job
759
  */
760
  do_action( "{$this->identifier}_job_failed", $job );
 
761
  return $job;
762
  }
763
 
772
  */
773
  public function delete_job( $job ) {
774
  global $wpdb;
 
775
  if ( is_string( $job ) ) {
776
  $job = $this->get_job( $job );
777
  }
 
778
  if ( ! $job ) {
779
  return false;
780
  }
 
781
  $wpdb->delete( $wpdb->options, array( 'option_name' => "{$this->identifier}_job_{$job->id}" ) );
 
782
  /**
783
  * Runs after a job is deleted.
784
  *
799
  * @since 4.4.0
800
  */
801
  protected function complete() {
 
802
  // unschedule the cron healthcheck
803
  $this->clear_scheduled_event();
804
  }
812
  * @return array
813
  */
814
  public function schedule_cron_healthcheck( $schedules ) {
 
815
  $interval = property_exists( $this, 'cron_interval' ) ? $this->cron_interval : 5;
 
816
  /**
817
  * Filter cron health check interval
818
  *
820
  * @param int $interval Interval in minutes
821
  */
822
  $interval = apply_filters( "{$this->identifier}_cron_interval", $interval );
 
823
  // adds every 5 minutes to the existing schedules.
824
  $schedules[ $this->identifier . '_cron_interval' ] = array(
825
  'interval' => MINUTE_IN_SECONDS * $interval,
826
  'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
827
  );
 
828
  return $schedules;
829
  }
830
 
838
  * @since 4.4.0
839
  */
840
  public function handle_cron_healthcheck() {
 
841
  if ( $this->is_process_running() ) {
842
  // background process already running
843
  exit;
844
  }
 
845
  if ( $this->is_queue_empty() ) {
846
  // no data to process
847
  $this->clear_scheduled_event();
848
  exit;
849
  }
 
850
  $this->dispatch();
851
  }
852
 
857
  * @since 4.4.0
858
  */
859
  protected function schedule_event() {
 
860
  if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
 
861
  // schedule the health check to fire after 30 seconds from now, as to not create a race condition
862
  // with job process lock on servers that fire & handle cron events instantly
863
  wp_schedule_event( time() + 30, $this->cron_interval_identifier, $this->cron_hook_identifier );
871
  * @since 4.4.0
872
  */
873
  protected function clear_scheduled_event() {
 
874
  $timestamp = wp_next_scheduled( $this->cron_hook_identifier );
 
875
  if ( $timestamp ) {
876
  wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
877
  }
901
  * @param \stdClass|object $job the job being processed
902
  */
903
  public function handle_shutdown( $job ) {
 
904
  $error = error_get_last();
 
905
  // if shutting down because of a fatal error, fail the job
906
  if ( $error && E_ERROR === $error['type'] ) {
 
907
  $this->fail_job( $job, $error['message'] );
 
908
  $this->unlock_process();
909
  }
910
  }
940
  * @return bool
941
  */
942
  public function test_connection() {
 
943
  $test_url = add_query_arg( 'action', "{$this->identifier}_test", admin_url( 'admin-ajax.php' ) );
944
  $result = wp_safe_remote_get( $test_url );
945
  $body = ! is_wp_error( $result ) ? wp_remote_retrieve_body( $result ) : null;
 
946
  // some servers may add a UTF8-BOM at the beginning of the response body, so we check if our test
947
  // string is included in the body, as an equal check would produce a false negative test result
948
+ return $body && Helper::str_exists( $body, '[TEST_LOOPBACK]' );
949
  }
950
 
951
 
973
 
974
  // this key is not unique to the plugin to avoid duplicate tools
975
  $tools['sv_wc_background_job_test'] = array(
976
+ 'name' => __( 'Background Processing Test', 'facebook-for-woocommerce' ),
977
+ 'button' => __( 'Run Test', 'facebook-for-woocommerce' ),
978
+ 'desc' => __( 'This tool will test whether your server is capable of processing background jobs.', 'facebook-for-woocommerce' ),
979
  'callback' => array( $this, 'run_debug_tool' ),
980
  );
981
 
993
  public function run_debug_tool() {
994
 
995
  if ( $this->test_connection() ) {
996
+ $this->debug_message = esc_html__( 'Success! You should be able to process background jobs.', 'facebook-for-woocommerce' );
997
  $result = true;
998
  } else {
999
+ $this->debug_message = esc_html__( 'Could not connect. Please ask your hosting company to ensure your server has loopback connections enabled.', 'facebook-for-woocommerce' );
1000
  $result = false;
1001
  }
1002
 
1018
  * @return string the updated text
1019
  */
1020
  public function translate_success_message( $translated, $original, $domain ) {
 
1021
  if ( 'woocommerce' === $domain && ( 'Tool ran.' === $original || 'There was an error calling %s' === $original ) ) {
1022
  $translated = $this->debug_message;
1023
  }
 
1024
  return $translated;
1025
  }
1026
 
1036
  * @return string
1037
  */
1038
  public function get_identifier() {
 
1039
  return $this->identifier;
1040
  }
 
 
1041
  }
 
 
 
includes/Handlers/Connection.php CHANGED
@@ -9,12 +9,13 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Handlers;
13
 
14
- use SkyVerge\WooCommerce\Facebook\Utilities\Heartbeat;
15
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_API_Exception;
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Helper;
17
- use SkyVerge\WooCommerce\Facebook\API\Exceptions\Connect_WC_API_Exception;
 
18
 
19
  defined( 'ABSPATH' ) or exit;
20
 
@@ -148,7 +149,7 @@ class Connection {
148
  $response->is_ig_cta_enabled()
149
  );
150
 
151
- } catch ( SV_WC_API_Exception $exception ) {
152
 
153
  $this->get_plugin()->log( 'Could not refresh business configuration. ' . $exception->getMessage() );
154
  }
@@ -172,7 +173,7 @@ class Connection {
172
 
173
  $this->update_installation_data();
174
 
175
- } catch ( SV_WC_API_Exception $exception ) {
176
 
177
  $this->get_plugin()->log( 'Could not refresh installation data. ' . $exception->getMessage() );
178
  }
@@ -185,7 +186,7 @@ class Connection {
185
  *
186
  * @since 2.0.0
187
  *
188
- * @throws SV_WC_API_Exception
189
  */
190
  private function update_installation_data() {
191
 
@@ -237,45 +238,35 @@ class Connection {
237
  * @since 2.0.0
238
  */
239
  public function handle_connect() {
240
-
241
  // don't handle anything unless the user can manage WooCommerce settings
242
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
243
  return;
244
  }
245
-
246
  try {
247
-
248
  if ( empty( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], self::ACTION_CONNECT ) ) {
249
- throw new SV_WC_API_Exception( 'Invalid nonce' );
250
  }
251
-
252
  $is_error = ! empty( $_GET['err'] ) ? true : false;
253
  $error_code = ! empty( $_GET['err_code'] ) ? stripslashes( sanitize_text_field( $_GET['err_code'] ) ) : '';
254
  $merchant_access_token = ! empty( $_GET['merchant_access_token'] ) ? sanitize_text_field( $_GET['merchant_access_token'] ) : '';
255
  $system_user_access_token = ! empty( $_GET['system_user_access_token'] ) ? sanitize_text_field( $_GET['system_user_access_token'] ) : '';
256
  $system_user_id = ! empty( $_GET['system_user_id'] ) ? sanitize_text_field( $_GET['system_user_id'] ) : '';
257
-
258
  if ( $is_error && $error_code ) {
259
- throw new Connect_WC_API_Exception( $error_code );
260
  }
261
-
262
  if ( ! $merchant_access_token ) {
263
- throw new SV_WC_API_Exception( 'Access token is missing' );
264
  }
265
-
266
  if ( ! $system_user_access_token ) {
267
- throw new SV_WC_API_Exception( 'System User access token is missing' );
268
  }
269
-
270
  if ( ! $system_user_id ) {
271
- throw new SV_WC_API_Exception( 'System User ID is missing' );
272
  }
273
-
274
  $this->update_access_token( $system_user_access_token );
275
  $this->update_merchant_access_token( $merchant_access_token );
276
  $this->update_system_user_id( $system_user_id );
277
  $this->update_installation_data();
278
-
279
  // Allow opt-out of full batch-API sync, for example if store has a large number of products.
280
  if ( facebook_for_woocommerce()->get_integration()->allow_full_batch_api_sync() ) {
281
  facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_all_products();
@@ -283,42 +274,26 @@ class Connection {
283
  else {
284
  facebook_for_woocommerce()->log( 'Initial full product sync disabled by filter hook `facebook_for_woocommerce_allow_full_batch_api_sync`', 'facebook_for_woocommerce_connect' );
285
  }
286
-
287
-
288
  update_option( 'wc_facebook_has_connected_fbe_2', 'yes' );
289
  update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' );
290
-
291
  // redirect to the Commerce onboarding if directed to do so
292
- if ( ! empty( SV_WC_Helper::get_requested_value( 'connect_commerce' ) ) ) {
293
-
294
  wp_redirect( $this->get_commerce_connect_url() );
295
  exit;
296
  }
297
-
298
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Connection successful!', 'facebook-for-woocommerce' ) );
299
-
300
- } catch ( SV_WC_API_Exception $exception ) {
301
-
302
  facebook_for_woocommerce()->log( sprintf( 'Connection failed: %s', $exception->getMessage() ) );
303
-
304
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
305
-
306
- wp_safe_redirect( facebook_for_woocommerce()->get_settings_url() );
307
- exit;
308
-
309
- } catch ( Connect_WC_API_Exception $exception ) {
310
  $message = $this->prepare_connect_server_message_for_user_display( $exception->getMessage() );
311
-
312
  facebook_for_woocommerce()->log( sprintf( 'Failed to connect to Facebook. Reason: %s', $message ), 'facebook_for_woocommerce_connect' );
313
-
314
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
315
-
316
- wp_safe_redirect( facebook_for_woocommerce()->get_settings_url() );
317
- exit;
318
-
319
  }
320
 
321
- wp_safe_redirect( facebook_for_woocommerce()->get_advertise_tab_url() );
322
  exit;
323
  }
324
 
@@ -353,28 +328,19 @@ class Connection {
353
  * @since 2.0.0
354
  */
355
  public function handle_disconnect() {
356
-
357
  check_admin_referer( self::ACTION_DISCONNECT );
358
-
359
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
360
  wp_die( __( 'You do not have permission to uninstall Facebook Business Extension.', 'facebook-for-woocommerce' ) );
361
  }
362
-
363
  try {
364
-
365
  $response = facebook_for_woocommerce()->get_api()->get_user();
366
  $response = facebook_for_woocommerce()->get_api()->delete_user_permission( $response->get_id(), 'manage_business_extension' );
367
-
368
  $this->disconnect();
369
-
370
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Disconnection successful.', 'facebook-for-woocommerce' ) );
371
-
372
- } catch ( SV_WC_API_Exception $exception ) {
373
-
374
  facebook_for_woocommerce()->log( sprintf( 'An error occurred during disconnection: %s. Your Facebook connection settings have been reset.', $exception->getMessage() ) );
375
  $this->disconnect();
376
  }
377
-
378
  wp_safe_redirect( facebook_for_woocommerce()->get_settings_url() );
379
  exit;
380
  }
@@ -388,7 +354,6 @@ class Connection {
388
  * @since 2.0.0
389
  */
390
  private function disconnect() {
391
-
392
  $this->update_access_token( '' );
393
  $this->update_merchant_access_token( '' );
394
  $this->update_system_user_id( '' );
@@ -397,12 +362,9 @@ class Connection {
397
  $this->update_instagram_business_id( '' );
398
  $this->update_commerce_merchant_settings_id( '' );
399
  $this->update_external_business_id('');
400
-
401
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, '' );
402
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, '' );
403
-
404
  facebook_for_woocommerce()->get_integration()->update_product_catalog_id( '' );
405
-
406
  }
407
 
408
 
@@ -413,38 +375,28 @@ class Connection {
413
  *
414
  * @param string $page_id desired Facebook page ID
415
  * @return string
416
- * @throws SV_WC_API_Exception
417
  */
418
  private function retrieve_page_access_token( $page_id ) {
419
-
420
  facebook_for_woocommerce()->log( 'Retrieving page access token' );
421
-
422
- $api_url = \WC_Facebookcommerce_Graph_API::GRAPH_API_URL . \WC_Facebookcommerce_Graph_API::API_VERSION;
423
-
424
  $response = wp_remote_get( $api_url . '/me/accounts?access_token=' . $this->get_access_token() );
425
-
426
  $body = wp_remote_retrieve_body( $response );
427
  $body = json_decode( $body, true );
428
-
429
  if ( ! is_array( $body ) || empty( $body['data'] ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
430
-
431
  facebook_for_woocommerce()->log( print_r( $body, true ) );
432
-
433
- throw new SV_WC_API_Exception(
434
  sprintf(
435
- /* translators: Placeholders: %s - API error message */
436
  __( 'Could not retrieve page access data. %s', 'facebook for woocommerce' ),
437
  wp_remote_retrieve_response_message( $response )
438
  )
439
  );
440
  }
441
-
442
  $page_access_tokens = wp_list_pluck( $body['data'], 'access_token', 'id' );
443
-
444
  // bail if the user isn't authorized to manage the page
445
  if ( empty( $page_access_tokens[ $page_id ] ) ) {
446
-
447
- throw new SV_WC_API_Exception(
448
  sprintf(
449
  /* translators: Placeholders: %s - Facebook page ID */
450
  __( 'Page %s not authorized.', 'facebook-for-woocommerce' ),
@@ -452,7 +404,6 @@ class Connection {
452
  )
453
  );
454
  }
455
-
456
  return $page_access_tokens[ $page_id ];
457
  }
458
 
@@ -465,9 +416,7 @@ class Connection {
465
  * @return string
466
  */
467
  public function get_access_token() {
468
-
469
  $access_token = get_option( self::OPTION_ACCESS_TOKEN, '' );
470
-
471
  /**
472
  * Filters the API access token.
473
  *
@@ -488,9 +437,7 @@ class Connection {
488
  * @return string
489
  */
490
  public function get_merchant_access_token() {
491
-
492
  $access_token = get_option( self::OPTION_MERCHANT_ACCESS_TOKEN, '' );
493
-
494
  /**
495
  * Filters the merchant access token.
496
  *
@@ -511,9 +458,7 @@ class Connection {
511
  * @return string
512
  */
513
  public function get_page_access_token() {
514
-
515
  $access_token = get_option( self::OPTION_PAGE_ACCESS_TOKEN, '' );
516
-
517
  /**
518
  * Filters the page access token.
519
  *
@@ -535,7 +480,6 @@ class Connection {
535
  * @return string
536
  */
537
  public function get_connect_url( $connect_commerce = false ) {
538
-
539
  return add_query_arg( rawurlencode_deep( $this->get_connect_parameters( $connect_commerce ) ), self::OAUTH_URL );
540
  }
541
 
@@ -561,7 +505,6 @@ class Connection {
561
  * @return string
562
  */
563
  public function get_commerce_connect_url() {
564
-
565
  // build the site URL to which the user will ultimately return
566
  $site_url = add_query_arg(
567
  array(
@@ -570,10 +513,8 @@ class Connection {
570
  ),
571
  home_url( '/' )
572
  );
573
-
574
  // build the proxy app URL where the user will land after onboarding, to be redirected to the site URL
575
  $redirect_url = add_query_arg( 'site_url', urlencode( $site_url ), $this->get_connection_authentication_url() );
576
-
577
  // build the final connect URL, direct to Facebook
578
  $connect_url = add_query_arg(
579
  array(
@@ -582,7 +523,6 @@ class Connection {
582
  ),
583
  'https://www.facebook.com/commerce_manager/onboarding/'
584
  );
585
-
586
  /**
587
  * Filters the URL used to connect to Facebook Commerce.
588
  *
@@ -602,10 +542,8 @@ class Connection {
602
  * @return string
603
  */
604
  public function get_manage_url() {
605
-
606
  $app_id = $this->get_client_id();
607
  $business_id = $this->get_external_business_id();
608
-
609
  return "https://www.facebook.com/facebook_business_extension?app_id={$app_id}&external_business_id={$business_id}";
610
  }
611
 
@@ -618,7 +556,6 @@ class Connection {
618
  * @return string
619
  */
620
  public function get_disconnect_url() {
621
-
622
  return wp_nonce_url( add_query_arg( 'action', self::ACTION_DISCONNECT, admin_url( 'admin.php' ) ), self::ACTION_DISCONNECT );
623
  }
624
 
@@ -633,7 +570,6 @@ class Connection {
633
  * @return string[]
634
  */
635
  public function get_scopes() {
636
-
637
  $scopes = array(
638
  'manage_business_extension',
639
  'catalog_management',
@@ -642,7 +578,6 @@ class Connection {
642
  'pages_read_engagement', // this scope is needed to enable order management if using the Commerce feature
643
  'instagram_basic',
644
  );
645
-
646
  /**
647
  * Filters the scopes that will be requested during the connection flow.
648
  *
@@ -663,13 +598,9 @@ class Connection {
663
  * @return string
664
  */
665
  public function get_external_business_id() {
666
-
667
  if ( ! is_string( $this->external_business_id ) ) {
668
-
669
  $external_id = get_option( self::OPTION_EXTERNAL_BUSINESS_ID );
670
-
671
  if ( ! is_string( $external_id ) || empty( $external_id ) ) {
672
-
673
  /**
674
  * Filters the shop's business external ID.
675
  *
@@ -681,17 +612,12 @@ class Connection {
681
  * @param string $external_id the shop's business external ID
682
  */
683
  $external_id = sanitize_key( (string) apply_filters( 'wc_facebook_connection_business_id', get_bloginfo( 'name' ) ) );
684
-
685
  if ( empty( $external_id ) ) {
686
  $external_id = sanitize_key( str_replace( array( 'http', 'https', 'www' ), '', get_bloginfo( 'url' ) ) );
687
  }
688
-
689
  $external_id = uniqid( sprintf( '%s-', $external_id ), false );
690
-
691
  $this->update_external_business_id( $external_id );
692
-
693
  }
694
-
695
  $this->external_business_id = $external_id;
696
  }
697
 
@@ -715,9 +641,7 @@ class Connection {
715
  * @return string
716
  */
717
  public function get_business_name() {
718
-
719
  $business_name = get_bloginfo( 'name' );
720
-
721
  /**
722
  * Filters the shop's business name.
723
  *
@@ -729,11 +653,9 @@ class Connection {
729
  * @param string $business_name the shop's business name
730
  */
731
  $business_name = trim( (string) apply_filters( 'wc_facebook_connection_business_name', is_string( $business_name ) ? $business_name : '' ) );
732
-
733
  if ( empty( $business_name ) ) {
734
  $business_name = get_bloginfo( 'url' );
735
  }
736
-
737
  return html_entity_decode( $business_name, ENT_QUOTES, 'UTF-8' );
738
  }
739
 
@@ -746,7 +668,6 @@ class Connection {
746
  * @return string
747
  */
748
  public function get_business_manager_id() {
749
-
750
  return get_option( self::OPTION_BUSINESS_MANAGER_ID, '' );
751
  }
752
 
@@ -759,7 +680,6 @@ class Connection {
759
  * @return string
760
  */
761
  public function get_ad_account_id() {
762
-
763
  return get_option( self::OPTION_AD_ACCOUNT_ID, '' );
764
  }
765
 
@@ -772,7 +692,6 @@ class Connection {
772
  * @return string
773
  */
774
  public function get_system_user_id() {
775
-
776
  return get_option( self::OPTION_SYSTEM_USER_ID, '' );
777
  }
778
 
@@ -785,7 +704,6 @@ class Connection {
785
  * @return string
786
  */
787
  public function get_commerce_manager_id() {
788
-
789
  return get_option( self::OPTION_COMMERCE_MANAGER_ID, '' );
790
  }
791
 
@@ -798,7 +716,6 @@ class Connection {
798
  * @return string
799
  */
800
  public function get_instagram_business_id() {
801
-
802
  return get_option( self::OPTION_INSTAGRAM_BUSINESS_ID, '' );
803
  }
804
 
@@ -811,7 +728,6 @@ class Connection {
811
  * @return string
812
  */
813
  public function get_commerce_merchant_settings_id() {
814
-
815
  return get_option( self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, '' );
816
  }
817
 
@@ -824,7 +740,6 @@ class Connection {
824
  * @return string URL
825
  */
826
  public function get_proxy_url() {
827
-
828
  /**
829
  * Filters the proxy URL.
830
  *
@@ -844,7 +759,6 @@ class Connection {
844
  * @return string URL
845
  */
846
  public function get_app_store_login_url() {
847
-
848
  /**
849
  * Filters App Store login URL.
850
  *
@@ -881,7 +795,6 @@ class Connection {
881
  * @return string
882
  */
883
  public function get_redirect_url() {
884
-
885
  $redirect_url = add_query_arg(
886
  array(
887
  'wc-api' => self::ACTION_CONNECT,
@@ -891,7 +804,6 @@ class Connection {
891
  ),
892
  home_url( '/' )
893
  );
894
-
895
  /**
896
  * Filters the redirect URL where the user will return to after OAuth.
897
  *
@@ -913,9 +825,7 @@ class Connection {
913
  * @return array
914
  */
915
  public function get_connect_parameters( $connect_commerce = false ) {
916
-
917
  $state = $this->get_redirect_url();
918
-
919
  if ( $connect_commerce ) {
920
  $state = add_query_arg( 'connect_commerce', true, $state );
921
  }
@@ -952,7 +862,6 @@ class Connection {
952
  * @return array associative array (to be converted to JSON encoded for connection purposes)
953
  */
954
  private function get_connect_parameters_extras() {
955
-
956
  $parameters = array(
957
  'setup' => array(
958
  'external_business_id' => $this->get_external_business_id(),
@@ -969,14 +878,11 @@ class Connection {
969
  ),
970
  'repeat' => false,
971
  );
972
-
973
  if ( $external_merchant_settings_id = facebook_for_woocommerce()->get_integration()->get_external_merchant_settings_id() ) {
974
  $parameters['setup']['merchant_settings_id'] = $external_merchant_settings_id;
975
  }
976
-
977
  // if messenger was previously enabled
978
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) {
979
-
980
  $parameters['business_config']['messenger_chat'] = array(
981
  'enabled' => true,
982
  'domains' => array(
@@ -984,7 +890,6 @@ class Connection {
984
  ),
985
  );
986
  }
987
-
988
  return $parameters;
989
  }
990
 
@@ -997,16 +902,12 @@ class Connection {
997
  * @return string
998
  */
999
  private function get_timezone_string() {
1000
-
1001
  $timezone = wc_timezone_string();
1002
-
1003
  // convert +05:30 and +05:00 into Etc/GMT+5 - we ignore the minutes because Facebook does not allow minute offsets
1004
  if ( preg_match( '/([+-])(\d{2}):\d{2}/', $timezone, $matches ) ) {
1005
-
1006
  $hours = (int) $matches[2];
1007
  $timezone = "Etc/GMT{$matches[1]}{$hours}";
1008
  }
1009
-
1010
  return $timezone;
1011
  }
1012
 
@@ -1019,7 +920,6 @@ class Connection {
1019
  * @param string $value the business manager ID
1020
  */
1021
  public function update_business_manager_id( $value ) {
1022
-
1023
  update_option( self::OPTION_BUSINESS_MANAGER_ID, $value );
1024
  }
1025
 
@@ -1032,7 +932,6 @@ class Connection {
1032
  * @param string $value the ad account ID
1033
  */
1034
  public function update_ad_account_id( $value ) {
1035
-
1036
  update_option( self::OPTION_AD_ACCOUNT_ID, $value );
1037
  }
1038
 
@@ -1045,7 +944,6 @@ class Connection {
1045
  * @param string $value the ID
1046
  */
1047
  public function update_system_user_id( $value ) {
1048
-
1049
  update_option( self::OPTION_SYSTEM_USER_ID, $value );
1050
  }
1051
 
@@ -1058,7 +956,6 @@ class Connection {
1058
  * @param string $id the ID
1059
  */
1060
  public function update_commerce_manager_id( $id ) {
1061
-
1062
  update_option( self::OPTION_COMMERCE_MANAGER_ID, $id );
1063
  }
1064
 
@@ -1071,7 +968,6 @@ class Connection {
1071
  * @param string $id the ID
1072
  */
1073
  public function update_instagram_business_id( $id ) {
1074
-
1075
  update_option( self::OPTION_INSTAGRAM_BUSINESS_ID, $id );
1076
  }
1077
 
@@ -1084,7 +980,6 @@ class Connection {
1084
  * @param string $id the ID
1085
  */
1086
  public function update_commerce_merchant_settings_id( $id ) {
1087
-
1088
  update_option( self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, $id );
1089
  }
1090
 
@@ -1097,7 +992,6 @@ class Connection {
1097
  * @param string $value the access token
1098
  */
1099
  public function update_access_token( $value ) {
1100
-
1101
  update_option( self::OPTION_ACCESS_TOKEN, $value );
1102
  }
1103
 
@@ -1110,7 +1004,6 @@ class Connection {
1110
  * @param string $value the access token
1111
  */
1112
  public function update_merchant_access_token( $value ) {
1113
-
1114
  update_option( self::OPTION_MERCHANT_ACCESS_TOKEN, $value );
1115
  }
1116
 
@@ -1123,7 +1016,6 @@ class Connection {
1123
  * @param string $value the access token
1124
  */
1125
  public function update_page_access_token( $value ) {
1126
-
1127
  update_option( self::OPTION_PAGE_ACCESS_TOKEN, is_string( $value ) ? $value : '' );
1128
  }
1129
 
@@ -1135,7 +1027,6 @@ class Connection {
1135
  * @param string $value external business id
1136
  */
1137
  public function update_external_business_id( $value ) {
1138
-
1139
  update_option( self::OPTION_EXTERNAL_BUSINESS_ID, is_string( $value ) ? $value : '' );
1140
  }
1141
 
@@ -1150,7 +1041,6 @@ class Connection {
1150
  * @return bool
1151
  */
1152
  public function is_connected() {
1153
-
1154
  return (bool) $this->get_access_token();
1155
  }
1156
 
@@ -1163,7 +1053,6 @@ class Connection {
1163
  * @return bool
1164
  */
1165
  public function has_previously_connected_fbe_2() {
1166
-
1167
  return 'yes' === get_option( 'wc_facebook_has_connected_fbe_2' );
1168
  }
1169
 
@@ -1176,9 +1065,7 @@ class Connection {
1176
  * @return bool
1177
  */
1178
  public function has_previously_connected_fbe_1() {
1179
-
1180
  $integration = $this->get_plugin()->get_integration();
1181
-
1182
  return $integration && $integration->get_external_merchant_settings_id();
1183
  }
1184
 
@@ -1191,7 +1078,6 @@ class Connection {
1191
  * @return string
1192
  */
1193
  public function get_client_id() {
1194
-
1195
  /**
1196
  * Filters the client ID.
1197
  *
@@ -1211,7 +1097,6 @@ class Connection {
1211
  * @return \WC_Facebookcommerce
1212
  */
1213
  public function get_plugin() {
1214
-
1215
  return $this->plugin;
1216
  }
1217
 
@@ -1225,27 +1110,19 @@ class Connection {
1225
  * @param object $data WebHook event data.
1226
  */
1227
  public function fbe_install_webhook( $data ) {
1228
-
1229
  // Reject other objects other than subscribed object
1230
  if ( empty( $data ) || ! isset( $data->object ) || self::WEBHOOK_SUBSCRIBED_OBJECT !== $data->object ) {
1231
-
1232
  $this->get_plugin()->log( 'Wrong (or empty) WebHook Event received' );
1233
  $this->get_plugin()->log( print_r( $data, true ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1234
-
1235
  return;
1236
  }
1237
-
1238
  $log_data = array();
1239
-
1240
  $this->get_plugin()->log( 'WebHook User Event received' );
1241
  $this->get_plugin()->log( print_r( $data, true ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1242
-
1243
-
1244
  $entry = (array) $data->entry[0];
1245
  if ( empty( $entry ) ) {
1246
  return;
1247
  }
1248
-
1249
  // Filter event by subscribed field
1250
  $event = array_filter(
1251
  $entry['changes'],
@@ -1253,12 +1130,10 @@ class Connection {
1253
  return self::WEBHOOK_SUBSCRIBED_FIELD === $change->field;
1254
  }
1255
  );
1256
-
1257
  $values = ! empty( $event[0] ) ? $event[0]->value : '';
1258
  if ( empty( $values ) ) {
1259
  return;
1260
  }
1261
-
1262
  /**
1263
  * If profiles, pages and instagram_profiles fields are not included in the Webhook payload, this means the business has uninstalled FBE.
1264
  * In this case also the field access_token will not be included.
@@ -1274,19 +1149,15 @@ class Connection {
1274
 
1275
  return;
1276
  }
1277
-
1278
  update_option( 'wc_facebook_has_connected_fbe_2', 'yes' );
1279
  update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' );
1280
-
1281
  $system_user_access_token = ! empty( $values->access_token ) ? sanitize_text_field( $values->access_token ) : '';
1282
  $this->update_access_token( $system_user_access_token );
1283
  $log_data[ self::OPTION_ACCESS_TOKEN ] = 'Token was saved';
1284
-
1285
  if ( ! empty( $entry['uid'] ) ) {
1286
  $this->update_system_user_id( sanitize_text_field( $entry['uid'] ) );
1287
  $log_data[ self::OPTION_SYSTEM_USER_ID ] = sanitize_text_field( $entry['uid'] );
1288
  }
1289
-
1290
  $merchant_access_token = ! empty( $values->merchant_access_token ) ? sanitize_text_field( $values->merchant_access_token ) : '';
1291
  $this->update_merchant_access_token( $merchant_access_token );
1292
  $log_data[ self::OPTION_MERCHANT_ACCESS_TOKEN ] = 'Token was saved';
@@ -1322,9 +1193,7 @@ class Connection {
1322
  }
1323
 
1324
  if ( ! empty( $values->instagram_profiles ) ) {
1325
-
1326
  $instagram_business_id = current( $values->instagram_profiles );
1327
-
1328
  $this->update_instagram_business_id( sanitize_text_field( $instagram_business_id ) );
1329
  $log_data[ self::OPTION_INSTAGRAM_BUSINESS_ID ] = sanitize_text_field( $instagram_business_id );
1330
  }
@@ -1335,22 +1204,15 @@ class Connection {
1335
  }
1336
 
1337
  if ( ! empty( $values->pages ) ) {
1338
-
1339
  $page_id = current( $values->pages );
1340
-
1341
  try {
1342
-
1343
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, sanitize_text_field( $page_id ) );
1344
  $log_data[ \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID ] = sanitize_text_field( $page_id );
1345
-
1346
  // get and store a current access token for the configured page
1347
  $page_access_token = $this->retrieve_page_access_token( $page_id );
1348
-
1349
  $this->update_page_access_token( $page_access_token );
1350
  $log_data[ self::OPTION_PAGE_ACCESS_TOKEN ] = sanitize_text_field( $page_access_token );
1351
-
1352
  } catch ( \Exception $e ) {
1353
-
1354
  $this->get_plugin()->log( 'Could not request Page Token: ' . $e->getMessage() );
1355
  }
1356
  }//end if
@@ -1366,7 +1228,6 @@ class Connection {
1366
  * @since 2.3.0
1367
  */
1368
  public function init_extras_endpoint() {
1369
-
1370
  register_rest_route(
1371
  'wc-facebook/v1',
1372
  'extras',
@@ -1389,7 +1250,6 @@ class Connection {
1389
  * @return boolean
1390
  */
1391
  public function extras_permission_callback() {
1392
-
1393
  return current_user_can( 'manage_woocommerce' );
1394
  }
1395
 
@@ -1402,12 +1262,10 @@ class Connection {
1402
  * @return \WP_REST_Response
1403
  */
1404
  public function extras_callback() {
1405
-
1406
  $extras = $this->get_connect_parameters_extras();
1407
  if ( empty( $extras ) ) {
1408
  return new \WP_REST_Response( null, 204 );
1409
  }
1410
-
1411
  return new \WP_REST_Response( $extras, 200 );
1412
  }
1413
 
@@ -1418,37 +1276,28 @@ class Connection {
1418
  * @since 2.3.0
1419
  */
1420
  public function handle_fbe_redirect() {
1421
-
1422
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
1423
  wp_die( esc_html__( 'You do not have permission to finish App Store login.', 'facebook-for-woocommerce' ) );
1424
  }
1425
-
1426
  $redirect_uri = base64_decode( $_REQUEST['redirect_uri'] ); //phpcs:ignore
1427
-
1428
  // To ensure that we are not sharing any user data with other parties, only redirect to the redirect_uri if it matches the regular expression
1429
  if ( empty( $redirect_uri ) || ! preg_match( '/https?:\/\/(www\.|m\.|l\.)?(\d{5}\.od\.)?(facebook|instagram|whatsapp)\.com(\/.*)?/', explode( '?', $redirect_uri )[0] ) ) {
1430
  wp_safe_redirect( site_url() );
1431
  exit;
1432
  }
1433
-
1434
  if ( empty( $_REQUEST['success'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1435
-
1436
  $url_params = array(
1437
  'store_url' => '',
1438
  'redirect_uri' => rawurlencode( $redirect_uri ),
1439
  'errors' => array( 'You need to grant access to WooCommerce.' ),
1440
  );
1441
-
1442
  $redirect_url = add_query_arg(
1443
  $url_params,
1444
  $this->get_app_store_login_url()
1445
  );
1446
-
1447
  } else {
1448
-
1449
  $redirect_url = $redirect_uri . '&extras=' . rawurlencode_deep( wp_json_encode( $this->get_connect_parameters_extras() ) );
1450
  }
1451
-
1452
  wp_redirect( $redirect_url ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
1453
  exit;
1454
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Handlers;
13
 
14
+ use WooCommerce\Facebook\API;
15
+ use WooCommerce\Facebook\API\Exceptions\ConnectApiException;
16
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
17
+ use WooCommerce\Facebook\Framework\Helper;
18
+ use WooCommerce\Facebook\Utilities\Heartbeat;
19
 
20
  defined( 'ABSPATH' ) or exit;
21
 
149
  $response->is_ig_cta_enabled()
150
  );
151
 
152
+ } catch ( ApiException $exception ) {
153
 
154
  $this->get_plugin()->log( 'Could not refresh business configuration. ' . $exception->getMessage() );
155
  }
173
 
174
  $this->update_installation_data();
175
 
176
+ } catch ( ApiException $exception ) {
177
 
178
  $this->get_plugin()->log( 'Could not refresh installation data. ' . $exception->getMessage() );
179
  }
186
  *
187
  * @since 2.0.0
188
  *
189
+ * @throws ApiException
190
  */
191
  private function update_installation_data() {
192
 
238
  * @since 2.0.0
239
  */
240
  public function handle_connect() {
 
241
  // don't handle anything unless the user can manage WooCommerce settings
242
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
243
  return;
244
  }
 
245
  try {
 
246
  if ( empty( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], self::ACTION_CONNECT ) ) {
247
+ throw new ApiException( 'Invalid nonce' );
248
  }
 
249
  $is_error = ! empty( $_GET['err'] ) ? true : false;
250
  $error_code = ! empty( $_GET['err_code'] ) ? stripslashes( sanitize_text_field( $_GET['err_code'] ) ) : '';
251
  $merchant_access_token = ! empty( $_GET['merchant_access_token'] ) ? sanitize_text_field( $_GET['merchant_access_token'] ) : '';
252
  $system_user_access_token = ! empty( $_GET['system_user_access_token'] ) ? sanitize_text_field( $_GET['system_user_access_token'] ) : '';
253
  $system_user_id = ! empty( $_GET['system_user_id'] ) ? sanitize_text_field( $_GET['system_user_id'] ) : '';
 
254
  if ( $is_error && $error_code ) {
255
+ throw new ConnectApiException( $error_code );
256
  }
 
257
  if ( ! $merchant_access_token ) {
258
+ throw new ApiException( 'Access token is missing' );
259
  }
 
260
  if ( ! $system_user_access_token ) {
261
+ throw new ApiException( 'System User access token is missing' );
262
  }
 
263
  if ( ! $system_user_id ) {
264
+ throw new ApiException( 'System User ID is missing' );
265
  }
 
266
  $this->update_access_token( $system_user_access_token );
267
  $this->update_merchant_access_token( $merchant_access_token );
268
  $this->update_system_user_id( $system_user_id );
269
  $this->update_installation_data();
 
270
  // Allow opt-out of full batch-API sync, for example if store has a large number of products.
271
  if ( facebook_for_woocommerce()->get_integration()->allow_full_batch_api_sync() ) {
272
  facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_all_products();
274
  else {
275
  facebook_for_woocommerce()->log( 'Initial full product sync disabled by filter hook `facebook_for_woocommerce_allow_full_batch_api_sync`', 'facebook_for_woocommerce_connect' );
276
  }
 
 
277
  update_option( 'wc_facebook_has_connected_fbe_2', 'yes' );
278
  update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' );
 
279
  // redirect to the Commerce onboarding if directed to do so
280
+ if ( ! empty( Helper::get_requested_value( 'connect_commerce' ) ) ) {
 
281
  wp_redirect( $this->get_commerce_connect_url() );
282
  exit;
283
  }
 
284
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Connection successful!', 'facebook-for-woocommerce' ) );
285
+ wp_safe_redirect( facebook_for_woocommerce()->get_advertise_tab_url() );
286
+ exit;
287
+ } catch ( ApiException $exception ) {
288
  facebook_for_woocommerce()->log( sprintf( 'Connection failed: %s', $exception->getMessage() ) );
 
289
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
290
+ } catch ( ConnectApiException $exception ) {
 
 
 
 
291
  $message = $this->prepare_connect_server_message_for_user_display( $exception->getMessage() );
 
292
  facebook_for_woocommerce()->log( sprintf( 'Failed to connect to Facebook. Reason: %s', $message ), 'facebook_for_woocommerce_connect' );
 
293
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
 
 
 
 
294
  }
295
 
296
+ wp_safe_redirect( facebook_for_woocommerce()->get_settings_url() );
297
  exit;
298
  }
299
 
328
  * @since 2.0.0
329
  */
330
  public function handle_disconnect() {
 
331
  check_admin_referer( self::ACTION_DISCONNECT );
 
332
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
333
  wp_die( __( 'You do not have permission to uninstall Facebook Business Extension.', 'facebook-for-woocommerce' ) );
334
  }
 
335
  try {
 
336
  $response = facebook_for_woocommerce()->get_api()->get_user();
337
  $response = facebook_for_woocommerce()->get_api()->delete_user_permission( $response->get_id(), 'manage_business_extension' );
 
338
  $this->disconnect();
 
339
  facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Disconnection successful.', 'facebook-for-woocommerce' ) );
340
+ } catch ( ApiException $exception ) {
 
 
341
  facebook_for_woocommerce()->log( sprintf( 'An error occurred during disconnection: %s. Your Facebook connection settings have been reset.', $exception->getMessage() ) );
342
  $this->disconnect();
343
  }
 
344
  wp_safe_redirect( facebook_for_woocommerce()->get_settings_url() );
345
  exit;
346
  }
354
  * @since 2.0.0
355
  */
356
  private function disconnect() {
 
357
  $this->update_access_token( '' );
358
  $this->update_merchant_access_token( '' );
359
  $this->update_system_user_id( '' );
362
  $this->update_instagram_business_id( '' );
363
  $this->update_commerce_merchant_settings_id( '' );
364
  $this->update_external_business_id('');
 
365
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, '' );
366
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID, '' );
 
367
  facebook_for_woocommerce()->get_integration()->update_product_catalog_id( '' );
 
368
  }
369
 
370
 
375
  *
376
  * @param string $page_id desired Facebook page ID
377
  * @return string
378
+ * @throws ApiException
379
  */
380
  private function retrieve_page_access_token( $page_id ) {
 
381
  facebook_for_woocommerce()->log( 'Retrieving page access token' );
382
+ $api_url = Api::GRAPH_API_URL . Api::API_VERSION;
 
 
383
  $response = wp_remote_get( $api_url . '/me/accounts?access_token=' . $this->get_access_token() );
 
384
  $body = wp_remote_retrieve_body( $response );
385
  $body = json_decode( $body, true );
 
386
  if ( ! is_array( $body ) || empty( $body['data'] ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
 
387
  facebook_for_woocommerce()->log( print_r( $body, true ) );
388
+ throw new ApiException(
 
389
  sprintf(
390
+ /* translators: Placeholders: %s - API error message */
391
  __( 'Could not retrieve page access data. %s', 'facebook for woocommerce' ),
392
  wp_remote_retrieve_response_message( $response )
393
  )
394
  );
395
  }
 
396
  $page_access_tokens = wp_list_pluck( $body['data'], 'access_token', 'id' );
 
397
  // bail if the user isn't authorized to manage the page
398
  if ( empty( $page_access_tokens[ $page_id ] ) ) {
399
+ throw new ApiException(
 
400
  sprintf(
401
  /* translators: Placeholders: %s - Facebook page ID */
402
  __( 'Page %s not authorized.', 'facebook-for-woocommerce' ),
404
  )
405
  );
406
  }
 
407
  return $page_access_tokens[ $page_id ];
408
  }
409
 
416
  * @return string
417
  */
418
  public function get_access_token() {
 
419
  $access_token = get_option( self::OPTION_ACCESS_TOKEN, '' );
 
420
  /**
421
  * Filters the API access token.
422
  *
437
  * @return string
438
  */
439
  public function get_merchant_access_token() {
 
440
  $access_token = get_option( self::OPTION_MERCHANT_ACCESS_TOKEN, '' );
 
441
  /**
442
  * Filters the merchant access token.
443
  *
458
  * @return string
459
  */
460
  public function get_page_access_token() {
 
461
  $access_token = get_option( self::OPTION_PAGE_ACCESS_TOKEN, '' );
 
462
  /**
463
  * Filters the page access token.
464
  *
480
  * @return string
481
  */
482
  public function get_connect_url( $connect_commerce = false ) {
 
483
  return add_query_arg( rawurlencode_deep( $this->get_connect_parameters( $connect_commerce ) ), self::OAUTH_URL );
484
  }
485
 
505
  * @return string
506
  */
507
  public function get_commerce_connect_url() {
 
508
  // build the site URL to which the user will ultimately return
509
  $site_url = add_query_arg(
510
  array(
513
  ),
514
  home_url( '/' )
515
  );
 
516
  // build the proxy app URL where the user will land after onboarding, to be redirected to the site URL
517
  $redirect_url = add_query_arg( 'site_url', urlencode( $site_url ), $this->get_connection_authentication_url() );
 
518
  // build the final connect URL, direct to Facebook
519
  $connect_url = add_query_arg(
520
  array(
523
  ),
524
  'https://www.facebook.com/commerce_manager/onboarding/'
525
  );
 
526
  /**
527
  * Filters the URL used to connect to Facebook Commerce.
528
  *
542
  * @return string
543
  */
544
  public function get_manage_url() {
 
545
  $app_id = $this->get_client_id();
546
  $business_id = $this->get_external_business_id();
 
547
  return "https://www.facebook.com/facebook_business_extension?app_id={$app_id}&external_business_id={$business_id}";
548
  }
549
 
556
  * @return string
557
  */
558
  public function get_disconnect_url() {
 
559
  return wp_nonce_url( add_query_arg( 'action', self::ACTION_DISCONNECT, admin_url( 'admin.php' ) ), self::ACTION_DISCONNECT );
560
  }
561
 
570
  * @return string[]
571
  */
572
  public function get_scopes() {
 
573
  $scopes = array(
574
  'manage_business_extension',
575
  'catalog_management',
578
  'pages_read_engagement', // this scope is needed to enable order management if using the Commerce feature
579
  'instagram_basic',
580
  );
 
581
  /**
582
  * Filters the scopes that will be requested during the connection flow.
583
  *
598
  * @return string
599
  */
600
  public function get_external_business_id() {
 
601
  if ( ! is_string( $this->external_business_id ) ) {
 
602
  $external_id = get_option( self::OPTION_EXTERNAL_BUSINESS_ID );
 
603
  if ( ! is_string( $external_id ) || empty( $external_id ) ) {
 
604
  /**
605
  * Filters the shop's business external ID.
606
  *
612
  * @param string $external_id the shop's business external ID
613
  */
614
  $external_id = sanitize_key( (string) apply_filters( 'wc_facebook_connection_business_id', get_bloginfo( 'name' ) ) );
 
615
  if ( empty( $external_id ) ) {
616
  $external_id = sanitize_key( str_replace( array( 'http', 'https', 'www' ), '', get_bloginfo( 'url' ) ) );
617
  }
 
618
  $external_id = uniqid( sprintf( '%s-', $external_id ), false );
 
619
  $this->update_external_business_id( $external_id );
 
620
  }
 
621
  $this->external_business_id = $external_id;
622
  }
623
 
641
  * @return string
642
  */
643
  public function get_business_name() {
 
644
  $business_name = get_bloginfo( 'name' );
 
645
  /**
646
  * Filters the shop's business name.
647
  *
653
  * @param string $business_name the shop's business name
654
  */
655
  $business_name = trim( (string) apply_filters( 'wc_facebook_connection_business_name', is_string( $business_name ) ? $business_name : '' ) );
 
656
  if ( empty( $business_name ) ) {
657
  $business_name = get_bloginfo( 'url' );
658
  }
 
659
  return html_entity_decode( $business_name, ENT_QUOTES, 'UTF-8' );
660
  }
661
 
668
  * @return string
669
  */
670
  public function get_business_manager_id() {
 
671
  return get_option( self::OPTION_BUSINESS_MANAGER_ID, '' );
672
  }
673
 
680
  * @return string
681
  */
682
  public function get_ad_account_id() {
 
683
  return get_option( self::OPTION_AD_ACCOUNT_ID, '' );
684
  }
685
 
692
  * @return string
693
  */
694
  public function get_system_user_id() {
 
695
  return get_option( self::OPTION_SYSTEM_USER_ID, '' );
696
  }
697
 
704
  * @return string
705
  */
706
  public function get_commerce_manager_id() {
 
707
  return get_option( self::OPTION_COMMERCE_MANAGER_ID, '' );
708
  }
709
 
716
  * @return string
717
  */
718
  public function get_instagram_business_id() {
 
719
  return get_option( self::OPTION_INSTAGRAM_BUSINESS_ID, '' );
720
  }
721
 
728
  * @return string
729
  */
730
  public function get_commerce_merchant_settings_id() {
 
731
  return get_option( self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, '' );
732
  }
733
 
740
  * @return string URL
741
  */
742
  public function get_proxy_url() {
 
743
  /**
744
  * Filters the proxy URL.
745
  *
759
  * @return string URL
760
  */
761
  public function get_app_store_login_url() {
 
762
  /**
763
  * Filters App Store login URL.
764
  *
795
  * @return string
796
  */
797
  public function get_redirect_url() {
 
798
  $redirect_url = add_query_arg(
799
  array(
800
  'wc-api' => self::ACTION_CONNECT,
804
  ),
805
  home_url( '/' )
806
  );
 
807
  /**
808
  * Filters the redirect URL where the user will return to after OAuth.
809
  *
825
  * @return array
826
  */
827
  public function get_connect_parameters( $connect_commerce = false ) {
 
828
  $state = $this->get_redirect_url();
 
829
  if ( $connect_commerce ) {
830
  $state = add_query_arg( 'connect_commerce', true, $state );
831
  }
862
  * @return array associative array (to be converted to JSON encoded for connection purposes)
863
  */
864
  private function get_connect_parameters_extras() {
 
865
  $parameters = array(
866
  'setup' => array(
867
  'external_business_id' => $this->get_external_business_id(),
878
  ),
879
  'repeat' => false,
880
  );
 
881
  if ( $external_merchant_settings_id = facebook_for_woocommerce()->get_integration()->get_external_merchant_settings_id() ) {
882
  $parameters['setup']['merchant_settings_id'] = $external_merchant_settings_id;
883
  }
 
884
  // if messenger was previously enabled
885
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) {
 
886
  $parameters['business_config']['messenger_chat'] = array(
887
  'enabled' => true,
888
  'domains' => array(
890
  ),
891
  );
892
  }
 
893
  return $parameters;
894
  }
895
 
902
  * @return string
903
  */
904
  private function get_timezone_string() {
 
905
  $timezone = wc_timezone_string();
 
906
  // convert +05:30 and +05:00 into Etc/GMT+5 - we ignore the minutes because Facebook does not allow minute offsets
907
  if ( preg_match( '/([+-])(\d{2}):\d{2}/', $timezone, $matches ) ) {
 
908
  $hours = (int) $matches[2];
909
  $timezone = "Etc/GMT{$matches[1]}{$hours}";
910
  }
 
911
  return $timezone;
912
  }
913
 
920
  * @param string $value the business manager ID
921
  */
922
  public function update_business_manager_id( $value ) {
 
923
  update_option( self::OPTION_BUSINESS_MANAGER_ID, $value );
924
  }
925
 
932
  * @param string $value the ad account ID
933
  */
934
  public function update_ad_account_id( $value ) {
 
935
  update_option( self::OPTION_AD_ACCOUNT_ID, $value );
936
  }
937
 
944
  * @param string $value the ID
945
  */
946
  public function update_system_user_id( $value ) {
 
947
  update_option( self::OPTION_SYSTEM_USER_ID, $value );
948
  }
949
 
956
  * @param string $id the ID
957
  */
958
  public function update_commerce_manager_id( $id ) {
 
959
  update_option( self::OPTION_COMMERCE_MANAGER_ID, $id );
960
  }
961
 
968
  * @param string $id the ID
969
  */
970
  public function update_instagram_business_id( $id ) {
 
971
  update_option( self::OPTION_INSTAGRAM_BUSINESS_ID, $id );
972
  }
973
 
980
  * @param string $id the ID
981
  */
982
  public function update_commerce_merchant_settings_id( $id ) {
 
983
  update_option( self::OPTION_COMMERCE_MERCHANT_SETTINGS_ID, $id );
984
  }
985
 
992
  * @param string $value the access token
993
  */
994
  public function update_access_token( $value ) {
 
995
  update_option( self::OPTION_ACCESS_TOKEN, $value );
996
  }
997
 
1004
  * @param string $value the access token
1005
  */
1006
  public function update_merchant_access_token( $value ) {
 
1007
  update_option( self::OPTION_MERCHANT_ACCESS_TOKEN, $value );
1008
  }
1009
 
1016
  * @param string $value the access token
1017
  */
1018
  public function update_page_access_token( $value ) {
 
1019
  update_option( self::OPTION_PAGE_ACCESS_TOKEN, is_string( $value ) ? $value : '' );
1020
  }
1021
 
1027
  * @param string $value external business id
1028
  */
1029
  public function update_external_business_id( $value ) {
 
1030
  update_option( self::OPTION_EXTERNAL_BUSINESS_ID, is_string( $value ) ? $value : '' );
1031
  }
1032
 
1041
  * @return bool
1042
  */
1043
  public function is_connected() {
 
1044
  return (bool) $this->get_access_token();
1045
  }
1046
 
1053
  * @return bool
1054
  */
1055
  public function has_previously_connected_fbe_2() {
 
1056
  return 'yes' === get_option( 'wc_facebook_has_connected_fbe_2' );
1057
  }
1058
 
1065
  * @return bool
1066
  */
1067
  public function has_previously_connected_fbe_1() {
 
1068
  $integration = $this->get_plugin()->get_integration();
 
1069
  return $integration && $integration->get_external_merchant_settings_id();
1070
  }
1071
 
1078
  * @return string
1079
  */
1080
  public function get_client_id() {
 
1081
  /**
1082
  * Filters the client ID.
1083
  *
1097
  * @return \WC_Facebookcommerce
1098
  */
1099
  public function get_plugin() {
 
1100
  return $this->plugin;
1101
  }
1102
 
1110
  * @param object $data WebHook event data.
1111
  */
1112
  public function fbe_install_webhook( $data ) {
 
1113
  // Reject other objects other than subscribed object
1114
  if ( empty( $data ) || ! isset( $data->object ) || self::WEBHOOK_SUBSCRIBED_OBJECT !== $data->object ) {
 
1115
  $this->get_plugin()->log( 'Wrong (or empty) WebHook Event received' );
1116
  $this->get_plugin()->log( print_r( $data, true ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
 
1117
  return;
1118
  }
 
1119
  $log_data = array();
 
1120
  $this->get_plugin()->log( 'WebHook User Event received' );
1121
  $this->get_plugin()->log( print_r( $data, true ) ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
 
 
1122
  $entry = (array) $data->entry[0];
1123
  if ( empty( $entry ) ) {
1124
  return;
1125
  }
 
1126
  // Filter event by subscribed field
1127
  $event = array_filter(
1128
  $entry['changes'],
1130
  return self::WEBHOOK_SUBSCRIBED_FIELD === $change->field;
1131
  }
1132
  );
 
1133
  $values = ! empty( $event[0] ) ? $event[0]->value : '';
1134
  if ( empty( $values ) ) {
1135
  return;
1136
  }
 
1137
  /**
1138
  * If profiles, pages and instagram_profiles fields are not included in the Webhook payload, this means the business has uninstalled FBE.
1139
  * In this case also the field access_token will not be included.
1149
 
1150
  return;
1151
  }
 
1152
  update_option( 'wc_facebook_has_connected_fbe_2', 'yes' );
1153
  update_option( 'wc_facebook_has_authorized_pages_read_engagement', 'yes' );
 
1154
  $system_user_access_token = ! empty( $values->access_token ) ? sanitize_text_field( $values->access_token ) : '';
1155
  $this->update_access_token( $system_user_access_token );
1156
  $log_data[ self::OPTION_ACCESS_TOKEN ] = 'Token was saved';
 
1157
  if ( ! empty( $entry['uid'] ) ) {
1158
  $this->update_system_user_id( sanitize_text_field( $entry['uid'] ) );
1159
  $log_data[ self::OPTION_SYSTEM_USER_ID ] = sanitize_text_field( $entry['uid'] );
1160
  }
 
1161
  $merchant_access_token = ! empty( $values->merchant_access_token ) ? sanitize_text_field( $values->merchant_access_token ) : '';
1162
  $this->update_merchant_access_token( $merchant_access_token );
1163
  $log_data[ self::OPTION_MERCHANT_ACCESS_TOKEN ] = 'Token was saved';
1193
  }
1194
 
1195
  if ( ! empty( $values->instagram_profiles ) ) {
 
1196
  $instagram_business_id = current( $values->instagram_profiles );
 
1197
  $this->update_instagram_business_id( sanitize_text_field( $instagram_business_id ) );
1198
  $log_data[ self::OPTION_INSTAGRAM_BUSINESS_ID ] = sanitize_text_field( $instagram_business_id );
1199
  }
1204
  }
1205
 
1206
  if ( ! empty( $values->pages ) ) {
 
1207
  $page_id = current( $values->pages );
 
1208
  try {
 
1209
  update_option( \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID, sanitize_text_field( $page_id ) );
1210
  $log_data[ \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID ] = sanitize_text_field( $page_id );
 
1211
  // get and store a current access token for the configured page
1212
  $page_access_token = $this->retrieve_page_access_token( $page_id );
 
1213
  $this->update_page_access_token( $page_access_token );
1214
  $log_data[ self::OPTION_PAGE_ACCESS_TOKEN ] = sanitize_text_field( $page_access_token );
 
1215
  } catch ( \Exception $e ) {
 
1216
  $this->get_plugin()->log( 'Could not request Page Token: ' . $e->getMessage() );
1217
  }
1218
  }//end if
1228
  * @since 2.3.0
1229
  */
1230
  public function init_extras_endpoint() {
 
1231
  register_rest_route(
1232
  'wc-facebook/v1',
1233
  'extras',
1250
  * @return boolean
1251
  */
1252
  public function extras_permission_callback() {
 
1253
  return current_user_can( 'manage_woocommerce' );
1254
  }
1255
 
1262
  * @return \WP_REST_Response
1263
  */
1264
  public function extras_callback() {
 
1265
  $extras = $this->get_connect_parameters_extras();
1266
  if ( empty( $extras ) ) {
1267
  return new \WP_REST_Response( null, 204 );
1268
  }
 
1269
  return new \WP_REST_Response( $extras, 200 );
1270
  }
1271
 
1276
  * @since 2.3.0
1277
  */
1278
  public function handle_fbe_redirect() {
 
1279
  if ( ! current_user_can( 'manage_woocommerce' ) ) {
1280
  wp_die( esc_html__( 'You do not have permission to finish App Store login.', 'facebook-for-woocommerce' ) );
1281
  }
 
1282
  $redirect_uri = base64_decode( $_REQUEST['redirect_uri'] ); //phpcs:ignore
 
1283
  // To ensure that we are not sharing any user data with other parties, only redirect to the redirect_uri if it matches the regular expression
1284
  if ( empty( $redirect_uri ) || ! preg_match( '/https?:\/\/(www\.|m\.|l\.)?(\d{5}\.od\.)?(facebook|instagram|whatsapp)\.com(\/.*)?/', explode( '?', $redirect_uri )[0] ) ) {
1285
  wp_safe_redirect( site_url() );
1286
  exit;
1287
  }
 
1288
  if ( empty( $_REQUEST['success'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
 
1289
  $url_params = array(
1290
  'store_url' => '',
1291
  'redirect_uri' => rawurlencode( $redirect_uri ),
1292
  'errors' => array( 'You need to grant access to WooCommerce.' ),
1293
  );
 
1294
  $redirect_url = add_query_arg(
1295
  $url_params,
1296
  $this->get_app_store_login_url()
1297
  );
 
1298
  } else {
 
1299
  $redirect_url = $redirect_uri . '&extras=' . rawurlencode_deep( wp_json_encode( $this->get_connect_parameters_extras() ) );
1300
  }
 
1301
  wp_redirect( $redirect_url ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
1302
  exit;
1303
  }
includes/Handlers/WebHook.php CHANGED
@@ -9,10 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Handlers;
13
-
14
- use SkyVerge\WooCommerce\PluginFramework\v5_5_4\SV_WC_API_Exception;
15
- use SkyVerge\WooCommerce\PluginFramework\v5_5_4\SV_WC_Helper;
16
 
17
  defined( 'ABSPATH' ) or exit;
18
 
@@ -34,7 +31,6 @@ class WebHook {
34
  * @since 2.3.0
35
  */
36
  public function __construct( \WC_Facebookcommerce $plugin ) {
37
-
38
  add_action( 'rest_api_init', array( $this, 'init_webhook_endpoint' ) );
39
  }
40
 
@@ -45,7 +41,6 @@ class WebHook {
45
  * @since 2.3.0
46
  */
47
  public function init_webhook_endpoint() {
48
-
49
  register_rest_route(
50
  'wc-facebook/v1',
51
  'webhook',
@@ -69,7 +64,6 @@ class WebHook {
69
  * @return boolean
70
  */
71
  public function permission_callback() {
72
-
73
  return current_user_can( 'manage_woocommerce' );
74
  }
75
 
@@ -78,21 +72,17 @@ class WebHook {
78
  * WebHook Listener
79
  *
80
  * @since 2.3.0
81
- * @see SkyVerge\WooCommerce\Facebook\Handlers\Connection
82
  *
83
  * @param \WP_REST_Request $request The request.
84
  * @return \WP_REST_Response
85
  */
86
  public function webhook_callback( \WP_REST_Request $request ) {
87
-
88
  $request_body = json_decode( $request->get_body() );
89
-
90
  if ( empty( $request_body ) ) {
91
  return new \WP_REST_Response( null, 204 );
92
  }
93
-
94
  do_action( 'fbe_webhook', $request_body );
95
-
96
  return new \WP_REST_Response( null, 200 );
97
  }
98
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Handlers;
 
 
 
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
31
  * @since 2.3.0
32
  */
33
  public function __construct( \WC_Facebookcommerce $plugin ) {
 
34
  add_action( 'rest_api_init', array( $this, 'init_webhook_endpoint' ) );
35
  }
36
 
41
  * @since 2.3.0
42
  */
43
  public function init_webhook_endpoint() {
 
44
  register_rest_route(
45
  'wc-facebook/v1',
46
  'webhook',
64
  * @return boolean
65
  */
66
  public function permission_callback() {
 
67
  return current_user_can( 'manage_woocommerce' );
68
  }
69
 
72
  * WebHook Listener
73
  *
74
  * @since 2.3.0
75
+ * @see Connection
76
  *
77
  * @param \WP_REST_Request $request The request.
78
  * @return \WP_REST_Response
79
  */
80
  public function webhook_callback( \WP_REST_Request $request ) {
 
81
  $request_body = json_decode( $request->get_body() );
 
82
  if ( empty( $request_body ) ) {
83
  return new \WP_REST_Response( null, 204 );
84
  }
 
85
  do_action( 'fbe_webhook', $request_body );
 
86
  return new \WP_REST_Response( null, 200 );
87
  }
88
  }
includes/Integrations/Bookings.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Integrations;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Integrations;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/Jobs/AbstractChainedJob.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\AbstractChainedJob as FrameworkAbstractChainedJob;
7
  use Exception;
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\AbstractChainedJob as FrameworkAbstractChainedJob;
7
  use Exception;
includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php CHANGED
@@ -1,8 +1,9 @@
1
  <?php
 
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\Jobs;
4
 
5
- use SkyVerge\WooCommerce\Facebook\Utilities\Heartbeat;
6
 
7
  defined( 'ABSPATH' ) || exit;
8
 
@@ -14,7 +15,7 @@ defined( 'ABSPATH' ) || exit;
14
  *
15
  * Note - this is closely coupled to the SkyVerge background job system, and is essentially a patch to improve it.
16
  *
17
- * @see SV_WP_Background_Job_Handler
18
  *
19
  * @since 2.6.0
20
  */
@@ -25,7 +26,7 @@ class CleanupSkyvergeFrameworkJobOptions {
25
  */
26
  public function init() {
27
  // Register our cleanup routine to run regularly.
28
- add_action( Heartbeat::DAILY, array( $this, 'clean_up_old_completed_options' ) );
29
  }
30
 
31
  /**
@@ -33,7 +34,7 @@ class CleanupSkyvergeFrameworkJobOptions {
33
  *
34
  * Logic and database query are adapted from SV_WP_Background_Job_Handler::get_jobs().
35
  *
36
- * @see SV_WP_Background_Job_Handler
37
  * @see Products\Sync\Background
38
  */
39
  public function clean_up_old_completed_options() {
1
  <?php
2
+ declare( strict_types=1 );
3
 
4
+ namespace WooCommerce\Facebook\Jobs;
5
 
6
+ use WooCommerce\Facebook\Utilities\Heartbeat;
7
 
8
  defined( 'ABSPATH' ) || exit;
9
 
15
  *
16
  * Note - this is closely coupled to the SkyVerge background job system, and is essentially a patch to improve it.
17
  *
18
+ * @see BackgroundJobHandler
19
  *
20
  * @since 2.6.0
21
  */
26
  */
27
  public function init() {
28
  // Register our cleanup routine to run regularly.
29
+ add_action( Heartbeat::DAILY, [ $this, 'clean_up_old_completed_options' ] );
30
  }
31
 
32
  /**
34
  *
35
  * Logic and database query are adapted from SV_WP_Background_Job_Handler::get_jobs().
36
  *
37
+ * @see BackgroundJobHandler
38
  * @see Products\Sync\Background
39
  */
40
  public function clean_up_old_completed_options() {
includes/Jobs/GenerateProductFeed.php CHANGED
@@ -1,12 +1,11 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities\BatchQueryOffset;
7
  use Exception;
8
  use WC_Facebookcommerce;
9
- use WC_Product;
10
 
11
  defined( 'ABSPATH' ) || exit;
12
 
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities\BatchQueryOffset;
7
  use Exception;
8
  use WC_Facebookcommerce;
 
9
 
10
  defined( 'ABSPATH' ) || exit;
11
 
includes/Jobs/JobManager.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies\ActionScheduler;
7
 
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Jobs;
5
 
6
  use Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies\ActionScheduler;
7
 
includes/Jobs/LoggingTrait.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Jobs;
5
 
6
  use WC_Facebookcommerce;
7
 
@@ -34,6 +34,4 @@ trait LoggingTrait {
34
  sprintf( '%s_%s', WC_Facebookcommerce::PLUGIN_ID, $this->get_name() )
35
  );
36
  }
37
-
38
-
39
  }
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Jobs;
5
 
6
  use WC_Facebookcommerce;
7
 
34
  sprintf( '%s_%s', WC_Facebookcommerce::PLUGIN_ID, $this->get_name() )
35
  );
36
  }
 
 
37
  }
includes/Lifecycle.php CHANGED
@@ -9,9 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
-
14
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
15
 
16
  defined( 'ABSPATH' ) or exit;
17
 
@@ -22,7 +20,7 @@ defined( 'ABSPATH' ) or exit;
22
  *
23
  * @method \WC_Facebookcommerce get_plugin()
24
  */
25
- class Lifecycle extends Framework\Plugin\Lifecycle {
26
 
27
 
28
  /**
@@ -30,12 +28,10 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
30
  *
31
  * @since 1.10.0
32
  *
33
- * @param Framework\SV_WC_Plugin $plugin
34
  */
35
- public function __construct( $plugin ) {
36
-
37
  parent::__construct( $plugin );
38
-
39
  $this->upgrade_versions = array(
40
  '1.10.0',
41
  '1.10.1',
@@ -55,13 +51,11 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
55
  * @since 1.10.0
56
  */
57
  protected function install() {
58
-
59
  /**
60
  * Versions prior to 1.10.0 did not set a version option, so the upgrade method needs to be called manually.
61
  * We do this by checking first if an old option exists, but a new one doesn't.
62
  */
63
  if ( get_option( 'woocommerce_facebookcommerce_settings' ) && ! get_option( 'wc_facebook_page_access_token' ) ) {
64
-
65
  $this->upgrade( '1.9.15' );
66
  }
67
  }
@@ -73,7 +67,6 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
73
  * @since 1.10.0
74
  */
75
  protected function upgrade_to_1_10_0() {
76
-
77
  $this->migrate_1_9_settings();
78
  }
79
 
@@ -87,14 +80,11 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
87
  * @since 1.10.1
88
  */
89
  private function migrate_1_9_settings() {
90
-
91
  $values = get_option( 'woocommerce_facebookcommerce_settings', array() );
92
-
93
  // preserve legacy values
94
  if ( false === get_option( 'woocommerce_facebookcommerce_legacy_settings' ) ) {
95
  update_option( 'woocommerce_facebookcommerce_legacy_settings', $values );
96
  }
97
-
98
  // migrate options from woocommerce_facebookcommerce_settings
99
  $options = array(
100
  'fb_api_key' => \WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN,
@@ -106,23 +96,17 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
106
  );
107
 
108
  foreach ( $options as $old_index => $new_option_name ) {
109
-
110
  if ( isset( $values[ $old_index ] ) && false === get_option( $new_option_name ) ) {
111
-
112
  $new_value = $values[ $old_index ];
113
-
114
  if ( 'pixel_install_time' === $old_index ) {
115
-
116
  // convert to UTC timestamp
117
  try {
118
  $pixel_install_time = \DateTime::createFromFormat( 'Y-m-d G:i:s', $new_value, new \DateTimeZone( wc_timezone_string() ) );
119
  } catch ( \Exception $e ) {
120
  $pixel_install_time = false;
121
  }
122
-
123
  $new_value = $pixel_install_time instanceof \DateTime ? $pixel_install_time->getTimestamp() : null;
124
  }
125
-
126
  update_option( $new_option_name, $new_value );
127
  }
128
  }
@@ -141,7 +125,6 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
141
  );
142
 
143
  foreach ( $settings as $old_index => $new_index ) {
144
-
145
  if ( isset( $values[ $old_index ] ) && ! isset( $new_settings[ $new_index ] ) ) {
146
  $new_settings[ $new_index ] = $values[ $old_index ];
147
  }
@@ -149,9 +132,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
149
 
150
  // migrate settings from standalone options
151
  if ( ! isset( $new_settings[ \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC ] ) ) {
152
-
153
  $product_sync_enabled = empty( get_option( 'fb_disable_sync_on_dev_environment', 0 ) );
154
-
155
  $new_settings[ \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC ] = $product_sync_enabled ? 'yes' : 'no';
156
  }
157
 
@@ -160,18 +141,13 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
160
  }
161
 
162
  if ( ! isset( $new_settings[ \WC_Facebookcommerce_Integration::SETTING_SCHEDULED_RESYNC_OFFSET ] ) ) {
163
-
164
  $autosync_time = get_option( 'woocommerce_fb_autosync_time' );
165
  $parsed_time = ! empty( $autosync_time ) ? strtotime( $autosync_time ) : false;
166
  $resync_offset = null;
167
-
168
  if ( false !== $parsed_time ) {
169
-
170
  $midnight = ( new \DateTime() )->setTimestamp( $parsed_time )->setTime( 0, 0, 0 );
171
-
172
  $resync_offset = $parsed_time - $midnight->getTimestamp();
173
  }
174
-
175
  $new_settings[ \WC_Facebookcommerce_Integration::SETTING_SCHEDULED_RESYNC_OFFSET ] = $resync_offset;
176
  }
177
 
@@ -183,17 +159,14 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
183
  }
184
 
185
  update_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', $new_settings );
186
-
187
  }
188
 
189
-
190
  /**
191
  * Upgrades to version 1.10.1.
192
  *
193
  * @since 1.10.1
194
  */
195
  protected function upgrade_to_1_10_1() {
196
-
197
  $this->migrate_1_9_settings();
198
  }
199
 
@@ -204,9 +177,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
204
  * @since 1.11.0
205
  */
206
  protected function upgrade_to_1_11_0() {
207
-
208
  $settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', array() );
209
-
210
  // moves the upload ID to a standalone option
211
  if ( ! empty( $settings['fb_upload_id'] ) ) {
212
  $this->get_plugin()->get_integration()->update_upload_id( $settings['fb_upload_id'] );
@@ -220,21 +191,15 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
220
  * @since 2.0.0
221
  */
222
  protected function upgrade_to_2_0_0() {
223
-
224
  // handle sync enabled and visible virtual products and variations
225
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
226
-
227
  // create_job() expects an non-empty array of attributes
228
  $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
229
  $handler->dispatch();
230
  }
231
-
232
  update_option( 'wc_facebook_has_connected_fbe_2', 'no' );
233
-
234
  $settings = get_option( 'woocommerce_facebookcommerce_settings' );
235
-
236
  if ( is_array( $settings ) ) {
237
-
238
  $settings_map = array(
239
  'facebook_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
240
  'facebook_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
@@ -248,15 +213,12 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
248
  'messenger_color_hex' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
249
  'enable_debug_mode' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
250
  );
251
-
252
  foreach ( $settings_map as $old_name => $new_name ) {
253
-
254
  if ( ! empty( $settings[ $old_name ] ) ) {
255
  update_option( $new_name, $settings[ $old_name ] );
256
  }
257
  }
258
  }
259
-
260
  // deletes an option that is not longer used to generate an admin notice
261
  delete_option( 'fb_cart_url' );
262
  }
@@ -268,7 +230,6 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
268
  * @since 2.0.3
269
  */
270
  protected function upgrade_to_2_0_3() {
271
-
272
  if ( ! $this->should_create_remove_duplicate_visibility_meta_background_job() ) {
273
  return;
274
  }
@@ -280,7 +241,6 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
280
 
281
  // create a job to remove duplicate visibility meta data entries
282
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
283
-
284
  // create_job() expects an non-empty array of attributes
285
  $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
286
  $handler->dispatch();
@@ -296,19 +256,15 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
296
  * @return bool
297
  */
298
  private function should_create_remove_duplicate_visibility_meta_background_job() {
299
-
300
  // we should try to remove duplicate meta if the virtual product variations job ran
301
  if ( 'yes' === get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) ) {
302
  return true;
303
  }
304
-
305
  $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance();
306
-
307
  // the virtual product variations job is not marked as complete but there is at least one job in the database
308
  if ( $handler && $handler->get_jobs() ) {
309
  return true;
310
  }
311
-
312
  return false;
313
  }
314
 
@@ -319,12 +275,10 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
319
  * @since 2.0.4
320
  */
321
  protected function upgrade_to_2_0_4() {
322
-
323
  // if unfinished jobs are stuck, give the handlers a chance to complete them
324
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
325
  $handler->dispatch();
326
  }
327
-
328
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
329
  $handler->dispatch();
330
  }
@@ -351,7 +305,6 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
351
  * Update procedure just needs to remove all current actions.
352
  * The Feed class will reschedule new generation with proper cadence.
353
  */
354
- as_unschedule_all_actions( \SkyVerge\WooCommerce\Facebook\Products\Feed::GENERATE_FEED_ACTION );
355
  }
356
-
357
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
 
 
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
20
  *
21
  * @method \WC_Facebookcommerce get_plugin()
22
  */
23
+ class Lifecycle extends Framework\Lifecycle {
24
 
25
 
26
  /**
28
  *
29
  * @since 1.10.0
30
  *
31
+ * @param Framework\Plugin $plugin
32
  */
33
+ public function __construct( Framework\Plugin $plugin ) {
 
34
  parent::__construct( $plugin );
 
35
  $this->upgrade_versions = array(
36
  '1.10.0',
37
  '1.10.1',
51
  * @since 1.10.0
52
  */
53
  protected function install() {
 
54
  /**
55
  * Versions prior to 1.10.0 did not set a version option, so the upgrade method needs to be called manually.
56
  * We do this by checking first if an old option exists, but a new one doesn't.
57
  */
58
  if ( get_option( 'woocommerce_facebookcommerce_settings' ) && ! get_option( 'wc_facebook_page_access_token' ) ) {
 
59
  $this->upgrade( '1.9.15' );
60
  }
61
  }
67
  * @since 1.10.0
68
  */
69
  protected function upgrade_to_1_10_0() {
 
70
  $this->migrate_1_9_settings();
71
  }
72
 
80
  * @since 1.10.1
81
  */
82
  private function migrate_1_9_settings() {
 
83
  $values = get_option( 'woocommerce_facebookcommerce_settings', array() );
 
84
  // preserve legacy values
85
  if ( false === get_option( 'woocommerce_facebookcommerce_legacy_settings' ) ) {
86
  update_option( 'woocommerce_facebookcommerce_legacy_settings', $values );
87
  }
 
88
  // migrate options from woocommerce_facebookcommerce_settings
89
  $options = array(
90
  'fb_api_key' => \WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN,
96
  );
97
 
98
  foreach ( $options as $old_index => $new_option_name ) {
 
99
  if ( isset( $values[ $old_index ] ) && false === get_option( $new_option_name ) ) {
 
100
  $new_value = $values[ $old_index ];
 
101
  if ( 'pixel_install_time' === $old_index ) {
 
102
  // convert to UTC timestamp
103
  try {
104
  $pixel_install_time = \DateTime::createFromFormat( 'Y-m-d G:i:s', $new_value, new \DateTimeZone( wc_timezone_string() ) );
105
  } catch ( \Exception $e ) {
106
  $pixel_install_time = false;
107
  }
 
108
  $new_value = $pixel_install_time instanceof \DateTime ? $pixel_install_time->getTimestamp() : null;
109
  }
 
110
  update_option( $new_option_name, $new_value );
111
  }
112
  }
125
  );
126
 
127
  foreach ( $settings as $old_index => $new_index ) {
 
128
  if ( isset( $values[ $old_index ] ) && ! isset( $new_settings[ $new_index ] ) ) {
129
  $new_settings[ $new_index ] = $values[ $old_index ];
130
  }
132
 
133
  // migrate settings from standalone options
134
  if ( ! isset( $new_settings[ \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC ] ) ) {
 
135
  $product_sync_enabled = empty( get_option( 'fb_disable_sync_on_dev_environment', 0 ) );
 
136
  $new_settings[ \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC ] = $product_sync_enabled ? 'yes' : 'no';
137
  }
138
 
141
  }
142
 
143
  if ( ! isset( $new_settings[ \WC_Facebookcommerce_Integration::SETTING_SCHEDULED_RESYNC_OFFSET ] ) ) {
 
144
  $autosync_time = get_option( 'woocommerce_fb_autosync_time' );
145
  $parsed_time = ! empty( $autosync_time ) ? strtotime( $autosync_time ) : false;
146
  $resync_offset = null;
 
147
  if ( false !== $parsed_time ) {
 
148
  $midnight = ( new \DateTime() )->setTimestamp( $parsed_time )->setTime( 0, 0, 0 );
 
149
  $resync_offset = $parsed_time - $midnight->getTimestamp();
150
  }
 
151
  $new_settings[ \WC_Facebookcommerce_Integration::SETTING_SCHEDULED_RESYNC_OFFSET ] = $resync_offset;
152
  }
153
 
159
  }
160
 
161
  update_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', $new_settings );
 
162
  }
163
 
 
164
  /**
165
  * Upgrades to version 1.10.1.
166
  *
167
  * @since 1.10.1
168
  */
169
  protected function upgrade_to_1_10_1() {
 
170
  $this->migrate_1_9_settings();
171
  }
172
 
177
  * @since 1.11.0
178
  */
179
  protected function upgrade_to_1_11_0() {
 
180
  $settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', array() );
 
181
  // moves the upload ID to a standalone option
182
  if ( ! empty( $settings['fb_upload_id'] ) ) {
183
  $this->get_plugin()->get_integration()->update_upload_id( $settings['fb_upload_id'] );
191
  * @since 2.0.0
192
  */
193
  protected function upgrade_to_2_0_0() {
 
194
  // handle sync enabled and visible virtual products and variations
195
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
 
196
  // create_job() expects an non-empty array of attributes
197
  $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
198
  $handler->dispatch();
199
  }
 
200
  update_option( 'wc_facebook_has_connected_fbe_2', 'no' );
 
201
  $settings = get_option( 'woocommerce_facebookcommerce_settings' );
 
202
  if ( is_array( $settings ) ) {
 
203
  $settings_map = array(
204
  'facebook_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
205
  'facebook_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
213
  'messenger_color_hex' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
214
  'enable_debug_mode' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
215
  );
 
216
  foreach ( $settings_map as $old_name => $new_name ) {
 
217
  if ( ! empty( $settings[ $old_name ] ) ) {
218
  update_option( $new_name, $settings[ $old_name ] );
219
  }
220
  }
221
  }
 
222
  // deletes an option that is not longer used to generate an admin notice
223
  delete_option( 'fb_cart_url' );
224
  }
230
  * @since 2.0.3
231
  */
232
  protected function upgrade_to_2_0_3() {
 
233
  if ( ! $this->should_create_remove_duplicate_visibility_meta_background_job() ) {
234
  return;
235
  }
241
 
242
  // create a job to remove duplicate visibility meta data entries
243
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
 
244
  // create_job() expects an non-empty array of attributes
245
  $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
246
  $handler->dispatch();
256
  * @return bool
257
  */
258
  private function should_create_remove_duplicate_visibility_meta_background_job() {
 
259
  // we should try to remove duplicate meta if the virtual product variations job ran
260
  if ( 'yes' === get_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'no' ) ) {
261
  return true;
262
  }
 
263
  $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance();
 
264
  // the virtual product variations job is not marked as complete but there is at least one job in the database
265
  if ( $handler && $handler->get_jobs() ) {
266
  return true;
267
  }
 
268
  return false;
269
  }
270
 
275
  * @since 2.0.4
276
  */
277
  protected function upgrade_to_2_0_4() {
 
278
  // if unfinished jobs are stuck, give the handlers a chance to complete them
279
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
280
  $handler->dispatch();
281
  }
 
282
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
283
  $handler->dispatch();
284
  }
305
  * Update procedure just needs to remove all current actions.
306
  * The Feed class will reschedule new generation with proper cadence.
307
  */
308
+ as_unschedule_all_actions( Products\Feed::GENERATE_FEED_ACTION );
309
  }
 
310
  }
includes/Locale.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/ProductSets/Sync.php CHANGED
@@ -9,11 +9,9 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\ProductSets;
13
 
14
- if ( ! defined( 'ABSPATH' ) ) {
15
- exit;
16
- }
17
 
18
  /**
19
  * The product set sync handler.
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\ProductSets;
13
 
14
+ defined( 'ABSPATH' ) || exit;
 
 
15
 
16
  /**
17
  * The product set sync handler.
includes/ProductSync/ProductExcludedException.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
 
5
  use Exception;
6
 
1
  <?php
2
 
3
+ namespace WooCommerce\Facebook\ProductSync;
4
 
5
  use Exception;
6
 
includes/ProductSync/ProductInvalidException.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
 
5
  use Exception;
6
 
1
  <?php
2
 
3
+ namespace WooCommerce\Facebook\ProductSync;
4
 
5
  use Exception;
6
 
includes/ProductSync/ProductValidator.php CHANGED
@@ -1,11 +1,12 @@
1
  <?php
 
2
 
3
- namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
 
5
- use SkyVerge\WooCommerce\Facebook\Products;
6
  use WC_Facebook_Product;
7
- use WC_Product;
8
  use WC_Facebookcommerce_Integration;
 
 
9
 
10
  if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
11
  include_once '../fbutils.php';
@@ -25,28 +26,28 @@ class ProductValidator {
25
  *
26
  * @var string
27
  */
28
- const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
29
 
30
  /**
31
  * Maximum length of product description.
32
  *
33
  * @var int
34
  */
35
- const MAX_DESCRIPTION_LENGTH = 5000;
36
 
37
  /**
38
  * Maximum length of product title.
39
  *
40
  * @var int
41
  */
42
- const MAX_TITLE_LENGTH = 150;
43
 
44
  /**
45
  * Maximum allowed attributes in a variation;
46
  *
47
  * @var int
48
  */
49
- const MAX_NUMBER_OF_ATTRIBUTES_IN_VARIATION = 4;
50
 
51
  /**
52
  * The FB integration instance.
@@ -305,7 +306,7 @@ class ProductValidator {
305
  $primary_product = $this->product_parent ? $this->product_parent : $this->product;
306
 
307
  // Variable and simple products are allowed to have no price.
308
- if ( in_array( $primary_product->get_type(), array( 'simple', 'variable' ), true ) ) {
309
  return;
310
  }
311
 
1
  <?php
2
+ declare( strict_types=1 );
3
 
4
+ namespace WooCommerce\Facebook\ProductSync;
5
 
 
6
  use WC_Facebook_Product;
 
7
  use WC_Facebookcommerce_Integration;
8
+ use WC_Product;
9
+ use WooCommerce\Facebook\Products;
10
 
11
  if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
12
  include_once '../fbutils.php';
26
  *
27
  * @var string
28
  */
29
+ public const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
30
 
31
  /**
32
  * Maximum length of product description.
33
  *
34
  * @var int
35
  */
36
+ public const MAX_DESCRIPTION_LENGTH = 5000;
37
 
38
  /**
39
  * Maximum length of product title.
40
  *
41
  * @var int
42
  */
43
+ public const MAX_TITLE_LENGTH = 150;
44
 
45
  /**
46
  * Maximum allowed attributes in a variation;
47
  *
48
  * @var int
49
  */
50
+ public const MAX_NUMBER_OF_ATTRIBUTES_IN_VARIATION = 4;
51
 
52
  /**
53
  * The FB integration instance.
306
  $primary_product = $this->product_parent ? $this->product_parent : $this->product;
307
 
308
  // Variable and simple products are allowed to have no price.
309
+ if ( in_array( $primary_product->get_type(), [ 'simple', 'variable' ], true ) ) {
310
  return;
311
  }
312
 
includes/Product_Categories.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
@@ -20,7 +20,6 @@ defined( 'ABSPATH' ) or exit;
20
  */
21
  class Product_Categories {
22
 
23
-
24
  /**
25
  * Gets the category’s stored Products::GOOGLE_PRODUCT_CATEGORY_META_KEY meta.
26
  *
@@ -32,11 +31,9 @@ class Product_Categories {
32
  * @return string
33
  */
34
  public static function get_google_product_category_id( $category_id ) {
35
-
36
  return get_term_meta( $category_id, Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
37
  }
38
 
39
-
40
  /**
41
  * Updates the stored Google product category ID for the Products::GOOGLE_PRODUCT_CATEGORY_META_KEY meta.
42
  *
@@ -46,9 +43,6 @@ class Product_Categories {
46
  * @param string $category_id Google product category ID
47
  */
48
  public static function update_google_product_category_id( $id, $category_id ) {
49
-
50
  update_term_meta( $id, Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, $category_id );
51
  }
52
-
53
-
54
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
20
  */
21
  class Product_Categories {
22
 
 
23
  /**
24
  * Gets the category’s stored Products::GOOGLE_PRODUCT_CATEGORY_META_KEY meta.
25
  *
31
  * @return string
32
  */
33
  public static function get_google_product_category_id( $category_id ) {
 
34
  return get_term_meta( $category_id, Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, true );
35
  }
36
 
 
37
  /**
38
  * Updates the stored Google product category ID for the Products::GOOGLE_PRODUCT_CATEGORY_META_KEY meta.
39
  *
43
  * @param string $category_id Google product category ID
44
  */
45
  public static function update_google_product_category_id( $id, $category_id ) {
 
46
  update_term_meta( $id, Products::GOOGLE_PRODUCT_CATEGORY_META_KEY, $category_id );
47
  }
 
 
48
  }
includes/Products.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook;
13
 
14
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Plugin_Exception;
15
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
16
  use WC_Facebook_Product;
 
 
17
 
18
  defined( 'ABSPATH' ) or exit;
19
 
@@ -24,7 +24,6 @@ defined( 'ABSPATH' ) or exit;
24
  */
25
  class Products {
26
 
27
-
28
  /** @var string the meta key used to flag whether a product should be synced in Facebook */
29
  const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
30
 
@@ -79,25 +78,17 @@ class Products {
79
  */
80
  private static function set_sync_for_products( array $products, $enabled ) {
81
  $enabled = wc_bool_to_string( $enabled );
82
-
83
  foreach ( $products as $product ) {
84
-
85
  if ( $product instanceof \WC_Product ) {
86
-
87
  if ( $product->is_type( 'variable' ) ) {
88
-
89
  foreach ( $product->get_children() as $variation ) {
90
-
91
  $product_variation = wc_get_product( $variation );
92
-
93
  if ( $product_variation instanceof \WC_Product ) {
94
-
95
  $product_variation->update_meta_data( self::SYNC_ENABLED_META_KEY, $enabled );
96
  $product_variation->save_meta_data();
97
  }
98
  }
99
  } else {
100
-
101
  $product->update_meta_data( self::SYNC_ENABLED_META_KEY, $enabled );
102
  $product->save_meta_data();
103
  }
@@ -111,7 +102,6 @@ class Products {
111
  }//end foreach
112
  }
113
 
114
-
115
  /**
116
  * Enables sync for given products.
117
  *
@@ -120,7 +110,6 @@ class Products {
120
  * @param \WC_Product[] $products an array of product objects
121
  */
122
  public static function enable_sync_for_products( array $products ) {
123
-
124
  self::set_sync_for_products( $products, true );
125
  }
126
 
@@ -133,7 +122,6 @@ class Products {
133
  * @param \WC_Product[] $products an array of product objects
134
  */
135
  public static function disable_sync_for_products( array $products ) {
136
-
137
  self::set_sync_for_products( $products, false );
138
  }
139
 
@@ -149,7 +137,6 @@ class Products {
149
  * }
150
  */
151
  public static function disable_sync_for_products_with_terms( array $args ) {
152
-
153
  $args = wp_parse_args(
154
  $args,
155
  array(
@@ -157,12 +144,9 @@ class Products {
157
  'include' => array(),
158
  )
159
  );
160
-
161
  $products = array();
162
-
163
  // get all products belonging to the given terms
164
  if ( is_array( $args['include'] ) && ! empty( $args['include'] ) ) {
165
-
166
  $terms = get_terms(
167
  array(
168
  'taxonomy' => $args['taxonomy'],
@@ -170,11 +154,8 @@ class Products {
170
  'include' => array_map( 'intval', $args['include'] ),
171
  )
172
  );
173
-
174
  if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
175
-
176
  $taxonomy = $args['taxonomy'] === 'product_tag' ? 'tag' : 'category';
177
-
178
  $products = wc_get_products(
179
  array(
180
  $taxonomy => $terms,
@@ -183,7 +164,6 @@ class Products {
183
  );
184
  }
185
  }//end if
186
-
187
  if ( ! empty( $products ) ) {
188
  self::disable_sync_for_products( $products );
189
  }
@@ -193,7 +173,7 @@ class Products {
193
  /**
194
  * Determines whether the given product should be synced.
195
  *
196
- * @deprecated use \SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator::validate() instead
197
  *
198
  * @since 1.10.0
199
  *
@@ -215,7 +195,7 @@ class Products {
215
  *
216
  * If a product is enabled for sync, but belongs to an excluded term, it will return as excluded from sync:
217
  *
218
- * @deprecated use \SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator::validate() instead
219
  *
220
  * @since 2.0.0-dev.1
221
  *
@@ -244,7 +224,6 @@ class Products {
244
  * @return bool
245
  */
246
  public static function product_should_be_deleted( \WC_Product $product ) {
247
-
248
  return ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $product->is_in_stock() ) || ! facebook_for_woocommerce()->get_product_sync_validator( $product )->passes_product_terms_check();
249
  }
250
 
@@ -255,7 +234,7 @@ class Products {
255
  * If the product is not explicitly set to disable sync, it'll be considered enabled.
256
  * This applies to products that may not have the meta value set.
257
  *
258
- * @deprecated use \SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator::passes_product_sync_field_check() instead
259
  *
260
  * @since 1.10.0
261
  *
@@ -272,7 +251,7 @@ class Products {
272
  *
273
  * @since 1.10.0
274
  *
275
- * @deprecated use \SkyVerge\WooCommerce\Facebook\ProductSync\ProductValidator::passes_product_terms_check() instead
276
  *
277
  * @param \WC_Product $product product object
278
  * @return bool if true, product should be excluded from sync, if false, product can be included in sync (unless manually excluded by individual product meta)
@@ -292,18 +271,13 @@ class Products {
292
  * @return bool success
293
  */
294
  public static function set_product_visibility( \WC_Product $product, $visibility ) {
295
-
296
  unset( self::$products_visibility[ $product->get_id() ] );
297
-
298
  if ( ! is_bool( $visibility ) ) {
299
  return false;
300
  }
301
-
302
  $product->update_meta_data( self::VISIBILITY_META_KEY, wc_bool_to_string( $visibility ) );
303
  $product->save_meta_data();
304
-
305
  self::$products_visibility[ $product->get_id() ] = $visibility;
306
-
307
  return true;
308
  }
309
 
@@ -317,37 +291,25 @@ class Products {
317
  * @return bool
318
  */
319
  public static function is_product_visible( \WC_Product $product ) {
320
-
321
  // accounts for a legacy bool value, current should be (string) 'yes' or (string) 'no'
322
  if ( ! isset( self::$products_visibility[ $product->get_id() ] ) ) {
323
-
324
  if ( $product->is_type( 'variable' ) ) {
325
-
326
  // assume variable products are not visible until a visible child is found
327
  $is_visible = false;
328
-
329
  foreach ( $product->get_children() as $child_id ) {
330
-
331
  $child_product = wc_get_product( $child_id );
332
-
333
  if ( $child_product && self::is_product_visible( $child_product ) ) {
334
-
335
  $is_visible = true;
336
  break;
337
  }
338
  }
339
  } elseif ( $meta = $product->get_meta( self::VISIBILITY_META_KEY ) ) {
340
-
341
  $is_visible = wc_string_to_bool( $product->get_meta( self::VISIBILITY_META_KEY ) );
342
-
343
  } else {
344
-
345
  $is_visible = true;
346
  }//end if
347
-
348
  self::$products_visibility[ $product->get_id() ] = $is_visible;
349
  }//end if
350
-
351
  return self::$products_visibility[ $product->get_id() ];
352
  }
353
 
@@ -365,32 +327,21 @@ class Products {
365
  * @return int
366
  */
367
  public static function get_product_price( \WC_Product $product ) {
368
-
369
  $facebook_price = $product->get_meta( WC_Facebook_Product::FB_PRODUCT_PRICE );
370
-
371
  // use the user defined Facebook price if set
372
  if ( is_numeric( $facebook_price ) ) {
373
-
374
  $price = $facebook_price;
375
-
376
  } elseif ( class_exists( 'WC_Product_Composite' ) && $product instanceof \WC_Product_Composite ) {
377
-
378
  $price = get_option( 'woocommerce_tax_display_shop' ) === 'incl' ? $product->get_composite_price_including_tax() : $product->get_composite_price();
379
-
380
  } elseif ( class_exists( 'WC_Product_Bundle' )
381
  && empty( $product->get_regular_price() )
382
  && 'bundle' === $product->get_type() ) {
383
-
384
  // if product is a product bundle with individually priced items, we rely on their pricing
385
  $price = wc_get_price_to_display( $product, array( 'price' => $product->get_bundle_price() ) );
386
-
387
  } else {
388
-
389
  $price = wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) );
390
  }
391
-
392
  $price = (int) ( $price ? round( $price * 100 ) : 0 );
393
-
394
  /**
395
  * Filters the product price used for Facebook sync.
396
  *
@@ -412,7 +363,6 @@ class Products {
412
  * @param \WC_Product $product the product object
413
  */
414
  public static function is_product_ready_for_commerce( \WC_Product $product ) {
415
-
416
  return $product->managing_stock()
417
  && self::get_product_price( $product )
418
  && self::is_commerce_enabled_for_product( $product )
@@ -429,11 +379,9 @@ class Products {
429
  * @return bool
430
  */
431
  public static function is_commerce_enabled_for_product( \WC_Product $product ) {
432
-
433
  if ( $product->is_type( 'variation' ) ) {
434
  $product = wc_get_product( $product->get_parent_id() );
435
  }
436
-
437
  return $product instanceof \WC_Product && wc_string_to_bool( $product->get_meta( self::COMMERCE_ENABLED_META_KEY ) );
438
  }
439
 
@@ -447,7 +395,6 @@ class Products {
447
  * @param bool $is_enabled whether or not Commerce is to be enabled
448
  */
449
  public static function update_commerce_enabled_for_product( \WC_Product $product, $is_enabled ) {
450
-
451
  $product->update_meta_data( self::COMMERCE_ENABLED_META_KEY, wc_bool_to_string( $is_enabled ) );
452
  $product->save_meta_data();
453
  }
@@ -464,7 +411,6 @@ class Products {
464
  * @return string
465
  */
466
  public static function get_google_product_category_id( \WC_Product $product ) {
467
-
468
  // attempt to get from product or parent product metadata
469
  if ( $product->is_type( 'variation' ) ) {
470
  $parent_product = wc_get_product( $product->get_parent_id() );
@@ -472,23 +418,17 @@ class Products {
472
  } else {
473
  $google_product_category_id = $product->get_meta( self::GOOGLE_PRODUCT_CATEGORY_META_KEY );
474
  }
475
-
476
  // fallback to the highest category's Google product category ID
477
  if ( empty( $google_product_category_id ) ) {
478
-
479
  $google_product_category_id = self::get_google_product_category_id_from_highest_category( $product );
480
  }
481
-
482
  // fallback to plugin-level default Google product category ID
483
  if ( empty( $google_product_category_id ) ) {
484
-
485
  $google_product_category_id = facebook_for_woocommerce()->get_commerce_handler()->get_default_google_product_category_id();
486
  }
487
-
488
  return $google_product_category_id;
489
  }
490
 
491
-
492
  /**
493
  * Gets the stored Google product category ID from the highest category.
494
  *
@@ -498,96 +438,65 @@ class Products {
498
  * @return string
499
  */
500
  private static function get_google_product_category_id_from_highest_category( \WC_Product $product ) {
501
-
502
  $google_product_category_id = '';
503
-
504
  // get all categories for the product
505
  if ( $product->is_type( 'variation' ) ) {
506
-
507
  $parent_product = wc_get_product( $product->get_parent_id() );
508
  $categories = $parent_product instanceof \WC_Product ? get_the_terms( $parent_product->get_id(), 'product_cat' ) : array();
509
-
510
  } else {
511
-
512
  $categories = get_the_terms( $product->get_id(), 'product_cat' );
513
  }
514
-
515
  if ( ! is_array( $categories ) ) {
516
  return $google_product_category_id;
517
  }
518
-
519
  $categories_per_level = array();
520
-
521
  if ( empty( $categories ) ) {
522
  return $categories_per_level;
523
  }
524
-
525
  // determine the level (depth) of each category
526
  foreach ( $categories as $category ) {
527
-
528
  $level = 0;
529
  $parent_category = $category;
530
-
531
  while ( (int) $parent_category->parent !== 0 ) {
532
-
533
  $parent_category = get_term( $parent_category->parent, 'product_cat' );
534
-
535
  if ( ! $parent_category instanceof \WP_Term ) {
536
  break;
537
  }
538
-
539
  $level ++;
540
  }
541
-
542
  if ( empty( $categories_per_level[ $level ] ) ) {
543
  $categories_per_level[ $level ] = array();
544
  }
545
-
546
  $categories_per_level[ $level ][] = $category;
547
  }
548
-
549
  // sort descending by level
550
  krsort( $categories_per_level );
551
-
552
  // remove categories without a Google product category
553
  foreach ( $categories_per_level as $level => $categories ) {
554
-
555
  foreach ( $categories as $key => $category ) {
556
-
557
  $google_product_category_id = Product_Categories::get_google_product_category_id( $category->term_id );
558
  if ( empty( $google_product_category_id ) ) {
559
  unset( $categories_per_level[ $level ][ $key ] );
560
  }
561
  }
562
-
563
  if ( empty( $categories_per_level[ $level ] ) ) {
564
  unset( $categories_per_level[ $level ] );
565
  }
566
  }
567
-
568
  if ( ! empty( $categories_per_level ) ) {
569
-
570
  // get highest level categories
571
  $categories = current( $categories_per_level );
572
-
573
  $google_product_category_id = '';
574
-
575
  foreach ( $categories as $category ) {
576
-
577
  $category_google_product_category_id = Product_Categories::get_google_product_category_id( $category->term_id );
578
-
579
  if ( empty( $google_product_category_id && ! empty( $category_google_product_category_id ) ) ) {
580
-
581
  $google_product_category_id = $category_google_product_category_id;
582
-
583
  } elseif ( $google_product_category_id !== $category_google_product_category_id ) {
584
-
585
  // conflicting Google product category IDs, discard them
586
  $google_product_category_id = '';
587
  }
588
  }
589
  }//end if
590
-
591
  return $google_product_category_id;
592
  }
593
 
@@ -598,47 +507,33 @@ class Products {
598
  * @return string
599
  */
600
  private static function get_ordered_categories_for_product( \WC_Product $product ) {
601
- // get all categories for the product
602
  if ( $product->is_type( 'variation' ) ) {
603
-
604
  $parent_product = wc_get_product( $product->get_parent_id() );
605
  $categories = $parent_product instanceof \WC_Product ? get_the_terms( $parent_product->get_id(), 'product_cat' ) : array();
606
-
607
  } else {
608
-
609
  $categories = get_the_terms( $product->get_id(), 'product_cat' );
610
  }
611
-
612
  if ( empty( $categories ) ) {
613
  return array();
614
  }
615
-
616
  $categories_per_level = array();
617
-
618
  // determine the level (depth) of each category
619
  foreach ( $categories as $category ) {
620
-
621
  $level = 0;
622
  $parent_category = $category;
623
-
624
  while ( (int) $parent_category->parent !== 0 ) {
625
-
626
  $parent_category = get_term( $parent_category->parent, 'product_cat' );
627
-
628
  if ( ! $parent_category instanceof \WP_Term ) {
629
  break;
630
  }
631
-
632
  $level ++;
633
  }
634
-
635
  if ( empty( $categories_per_level[ $level ] ) ) {
636
  $categories_per_level[ $level ] = array();
637
  }
638
-
639
  $categories_per_level[ $level ][] = $category;
640
  }
641
-
642
  // sort descending by level
643
  krsort( $categories_per_level );
644
  return $categories_per_level;
@@ -799,17 +694,17 @@ class Products {
799
  *
800
  * @param \WC_Product $product the product object
801
  * @param string $attribute_name the attribute to be used to store the color
802
- * @throws SV_WC_Plugin_Exception
803
  */
804
  public static function update_product_color_attribute( \WC_Product $product, $attribute_name ) {
805
 
806
  // check if the name matches an available attribute
807
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
808
- throw new SV_WC_Plugin_Exception( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
809
  }
810
 
811
  if ( $attribute_name !== self::get_product_color_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
812
- throw new SV_WC_Plugin_Exception( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
813
  }
814
 
815
  $product->update_meta_data( self::COLOR_ATTRIBUTE_META_KEY, $attribute_name );
@@ -895,17 +790,17 @@ class Products {
895
  *
896
  * @param \WC_Product $product the product object
897
  * @param string $attribute_name the attribute to be used to store the size
898
- * @throws SV_WC_Plugin_Exception
899
  */
900
  public static function update_product_size_attribute( \WC_Product $product, $attribute_name ) {
901
 
902
  // check if the name matches an available attribute
903
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
904
- throw new SV_WC_Plugin_Exception( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
905
  }
906
 
907
  if ( $attribute_name !== self::get_product_size_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
908
- throw new SV_WC_Plugin_Exception( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
909
  }
910
 
911
  $product->update_meta_data( self::SIZE_ATTRIBUTE_META_KEY, $attribute_name );
@@ -991,19 +886,16 @@ class Products {
991
  *
992
  * @param \WC_Product $product the product object
993
  * @param string $attribute_name the attribute to be used to store the pattern
994
- * @throws SV_WC_Plugin_Exception
995
  */
996
  public static function update_product_pattern_attribute( \WC_Product $product, $attribute_name ) {
997
-
998
  // check if the name matches an available attribute
999
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
1000
- throw new SV_WC_Plugin_Exception( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
1001
  }
1002
-
1003
  if ( $attribute_name !== self::get_product_pattern_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
1004
- throw new SV_WC_Plugin_Exception( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
1005
  }
1006
-
1007
  $product->update_meta_data( self::PATTERN_ATTRIBUTE_META_KEY, $attribute_name );
1008
  $product->save_meta_data();
1009
  }
@@ -1150,7 +1042,7 @@ class Products {
1150
  return array_merge(
1151
  $attrs,
1152
  array(
1153
- str_replace( $prefix, '', $attr_key ) => wc_clean( Framework\SV_WC_Helper::get_posted_value( $attr_key ) ),
1154
  )
1155
  );
1156
  },
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook;
13
 
 
 
14
  use WC_Facebook_Product;
15
+ use WooCommerce\Facebook\Framework\Helper;
16
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
17
 
18
  defined( 'ABSPATH' ) or exit;
19
 
24
  */
25
  class Products {
26
 
 
27
  /** @var string the meta key used to flag whether a product should be synced in Facebook */
28
  const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
29
 
78
  */
79
  private static function set_sync_for_products( array $products, $enabled ) {
80
  $enabled = wc_bool_to_string( $enabled );
 
81
  foreach ( $products as $product ) {
 
82
  if ( $product instanceof \WC_Product ) {
 
83
  if ( $product->is_type( 'variable' ) ) {
 
84
  foreach ( $product->get_children() as $variation ) {
 
85
  $product_variation = wc_get_product( $variation );
 
86
  if ( $product_variation instanceof \WC_Product ) {
 
87
  $product_variation->update_meta_data( self::SYNC_ENABLED_META_KEY, $enabled );
88
  $product_variation->save_meta_data();
89
  }
90
  }
91
  } else {
 
92
  $product->update_meta_data( self::SYNC_ENABLED_META_KEY, $enabled );
93
  $product->save_meta_data();
94
  }
102
  }//end foreach
103
  }
104
 
 
105
  /**
106
  * Enables sync for given products.
107
  *
110
  * @param \WC_Product[] $products an array of product objects
111
  */
112
  public static function enable_sync_for_products( array $products ) {
 
113
  self::set_sync_for_products( $products, true );
114
  }
115
 
122
  * @param \WC_Product[] $products an array of product objects
123
  */
124
  public static function disable_sync_for_products( array $products ) {
 
125
  self::set_sync_for_products( $products, false );
126
  }
127
 
137
  * }
138
  */
139
  public static function disable_sync_for_products_with_terms( array $args ) {
 
140
  $args = wp_parse_args(
141
  $args,
142
  array(
144
  'include' => array(),
145
  )
146
  );
 
147
  $products = array();
 
148
  // get all products belonging to the given terms
149
  if ( is_array( $args['include'] ) && ! empty( $args['include'] ) ) {
 
150
  $terms = get_terms(
151
  array(
152
  'taxonomy' => $args['taxonomy'],
154
  'include' => array_map( 'intval', $args['include'] ),
155
  )
156
  );
 
157
  if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
 
158
  $taxonomy = $args['taxonomy'] === 'product_tag' ? 'tag' : 'category';
 
159
  $products = wc_get_products(
160
  array(
161
  $taxonomy => $terms,
164
  );
165
  }
166
  }//end if
 
167
  if ( ! empty( $products ) ) {
168
  self::disable_sync_for_products( $products );
169
  }
173
  /**
174
  * Determines whether the given product should be synced.
175
  *
176
+ * @deprecated use \WooCommerce\Facebook\ProductSync\ProductValidator::validate() instead
177
  *
178
  * @since 1.10.0
179
  *
195
  *
196
  * If a product is enabled for sync, but belongs to an excluded term, it will return as excluded from sync:
197
  *
198
+ * @deprecated use \WooCommerce\Facebook\ProductSync\ProductValidator::validate() instead
199
  *
200
  * @since 2.0.0-dev.1
201
  *
224
  * @return bool
225
  */
226
  public static function product_should_be_deleted( \WC_Product $product ) {
 
227
  return ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $product->is_in_stock() ) || ! facebook_for_woocommerce()->get_product_sync_validator( $product )->passes_product_terms_check();
228
  }
229
 
234
  * If the product is not explicitly set to disable sync, it'll be considered enabled.
235
  * This applies to products that may not have the meta value set.
236
  *
237
+ * @deprecated use \WooCommerce\Facebook\ProductSync\ProductValidator::passes_product_sync_field_check() instead
238
  *
239
  * @since 1.10.0
240
  *
251
  *
252
  * @since 1.10.0
253
  *
254
+ * @deprecated use \WooCommerce\Facebook\ProductSync\ProductValidator::passes_product_terms_check() instead
255
  *
256
  * @param \WC_Product $product product object
257
  * @return bool if true, product should be excluded from sync, if false, product can be included in sync (unless manually excluded by individual product meta)
271
  * @return bool success
272
  */
273
  public static function set_product_visibility( \WC_Product $product, $visibility ) {
 
274
  unset( self::$products_visibility[ $product->get_id() ] );
 
275
  if ( ! is_bool( $visibility ) ) {
276
  return false;
277
  }
 
278
  $product->update_meta_data( self::VISIBILITY_META_KEY, wc_bool_to_string( $visibility ) );
279
  $product->save_meta_data();
 
280
  self::$products_visibility[ $product->get_id() ] = $visibility;
 
281
  return true;
282
  }
283
 
291
  * @return bool
292
  */
293
  public static function is_product_visible( \WC_Product $product ) {
 
294
  // accounts for a legacy bool value, current should be (string) 'yes' or (string) 'no'
295
  if ( ! isset( self::$products_visibility[ $product->get_id() ] ) ) {
 
296
  if ( $product->is_type( 'variable' ) ) {
 
297
  // assume variable products are not visible until a visible child is found
298
  $is_visible = false;
 
299
  foreach ( $product->get_children() as $child_id ) {
 
300
  $child_product = wc_get_product( $child_id );
 
301
  if ( $child_product && self::is_product_visible( $child_product ) ) {
 
302
  $is_visible = true;
303
  break;
304
  }
305
  }
306
  } elseif ( $meta = $product->get_meta( self::VISIBILITY_META_KEY ) ) {
 
307
  $is_visible = wc_string_to_bool( $product->get_meta( self::VISIBILITY_META_KEY ) );
 
308
  } else {
 
309
  $is_visible = true;
310
  }//end if
 
311
  self::$products_visibility[ $product->get_id() ] = $is_visible;
312
  }//end if
 
313
  return self::$products_visibility[ $product->get_id() ];
314
  }
315
 
327
  * @return int
328
  */
329
  public static function get_product_price( \WC_Product $product ) {
 
330
  $facebook_price = $product->get_meta( WC_Facebook_Product::FB_PRODUCT_PRICE );
 
331
  // use the user defined Facebook price if set
332
  if ( is_numeric( $facebook_price ) ) {
 
333
  $price = $facebook_price;
 
334
  } elseif ( class_exists( 'WC_Product_Composite' ) && $product instanceof \WC_Product_Composite ) {
 
335
  $price = get_option( 'woocommerce_tax_display_shop' ) === 'incl' ? $product->get_composite_price_including_tax() : $product->get_composite_price();
 
336
  } elseif ( class_exists( 'WC_Product_Bundle' )
337
  && empty( $product->get_regular_price() )
338
  && 'bundle' === $product->get_type() ) {
 
339
  // if product is a product bundle with individually priced items, we rely on their pricing
340
  $price = wc_get_price_to_display( $product, array( 'price' => $product->get_bundle_price() ) );
 
341
  } else {
 
342
  $price = wc_get_price_to_display( $product, array( 'price' => $product->get_regular_price() ) );
343
  }
 
344
  $price = (int) ( $price ? round( $price * 100 ) : 0 );
 
345
  /**
346
  * Filters the product price used for Facebook sync.
347
  *
363
  * @param \WC_Product $product the product object
364
  */
365
  public static function is_product_ready_for_commerce( \WC_Product $product ) {
 
366
  return $product->managing_stock()
367
  && self::get_product_price( $product )
368
  && self::is_commerce_enabled_for_product( $product )
379
  * @return bool
380
  */
381
  public static function is_commerce_enabled_for_product( \WC_Product $product ) {
 
382
  if ( $product->is_type( 'variation' ) ) {
383
  $product = wc_get_product( $product->get_parent_id() );
384
  }
 
385
  return $product instanceof \WC_Product && wc_string_to_bool( $product->get_meta( self::COMMERCE_ENABLED_META_KEY ) );
386
  }
387
 
395
  * @param bool $is_enabled whether or not Commerce is to be enabled
396
  */
397
  public static function update_commerce_enabled_for_product( \WC_Product $product, $is_enabled ) {
 
398
  $product->update_meta_data( self::COMMERCE_ENABLED_META_KEY, wc_bool_to_string( $is_enabled ) );
399
  $product->save_meta_data();
400
  }
411
  * @return string
412
  */
413
  public static function get_google_product_category_id( \WC_Product $product ) {
 
414
  // attempt to get from product or parent product metadata
415
  if ( $product->is_type( 'variation' ) ) {
416
  $parent_product = wc_get_product( $product->get_parent_id() );
418
  } else {
419
  $google_product_category_id = $product->get_meta( self::GOOGLE_PRODUCT_CATEGORY_META_KEY );
420
  }
 
421
  // fallback to the highest category's Google product category ID
422
  if ( empty( $google_product_category_id ) ) {
 
423
  $google_product_category_id = self::get_google_product_category_id_from_highest_category( $product );
424
  }
 
425
  // fallback to plugin-level default Google product category ID
426
  if ( empty( $google_product_category_id ) ) {
 
427
  $google_product_category_id = facebook_for_woocommerce()->get_commerce_handler()->get_default_google_product_category_id();
428
  }
 
429
  return $google_product_category_id;
430
  }
431
 
 
432
  /**
433
  * Gets the stored Google product category ID from the highest category.
434
  *
438
  * @return string
439
  */
440
  private static function get_google_product_category_id_from_highest_category( \WC_Product $product ) {
 
441
  $google_product_category_id = '';
 
442
  // get all categories for the product
443
  if ( $product->is_type( 'variation' ) ) {
 
444
  $parent_product = wc_get_product( $product->get_parent_id() );
445
  $categories = $parent_product instanceof \WC_Product ? get_the_terms( $parent_product->get_id(), 'product_cat' ) : array();
 
446
  } else {
 
447
  $categories = get_the_terms( $product->get_id(), 'product_cat' );
448
  }
 
449
  if ( ! is_array( $categories ) ) {
450
  return $google_product_category_id;
451
  }
 
452
  $categories_per_level = array();
 
453
  if ( empty( $categories ) ) {
454
  return $categories_per_level;
455
  }
 
456
  // determine the level (depth) of each category
457
  foreach ( $categories as $category ) {
 
458
  $level = 0;
459
  $parent_category = $category;
 
460
  while ( (int) $parent_category->parent !== 0 ) {
 
461
  $parent_category = get_term( $parent_category->parent, 'product_cat' );
 
462
  if ( ! $parent_category instanceof \WP_Term ) {
463
  break;
464
  }
 
465
  $level ++;
466
  }
 
467
  if ( empty( $categories_per_level[ $level ] ) ) {
468
  $categories_per_level[ $level ] = array();
469
  }
 
470
  $categories_per_level[ $level ][] = $category;
471
  }
 
472
  // sort descending by level
473
  krsort( $categories_per_level );
 
474
  // remove categories without a Google product category
475
  foreach ( $categories_per_level as $level => $categories ) {
 
476
  foreach ( $categories as $key => $category ) {
 
477
  $google_product_category_id = Product_Categories::get_google_product_category_id( $category->term_id );
478
  if ( empty( $google_product_category_id ) ) {
479
  unset( $categories_per_level[ $level ][ $key ] );
480
  }
481
  }
 
482
  if ( empty( $categories_per_level[ $level ] ) ) {
483
  unset( $categories_per_level[ $level ] );
484
  }
485
  }
 
486
  if ( ! empty( $categories_per_level ) ) {
 
487
  // get highest level categories
488
  $categories = current( $categories_per_level );
 
489
  $google_product_category_id = '';
 
490
  foreach ( $categories as $category ) {
 
491
  $category_google_product_category_id = Product_Categories::get_google_product_category_id( $category->term_id );
 
492
  if ( empty( $google_product_category_id && ! empty( $category_google_product_category_id ) ) ) {
 
493
  $google_product_category_id = $category_google_product_category_id;
 
494
  } elseif ( $google_product_category_id !== $category_google_product_category_id ) {
 
495
  // conflicting Google product category IDs, discard them
496
  $google_product_category_id = '';
497
  }
498
  }
499
  }//end if
 
500
  return $google_product_category_id;
501
  }
502
 
507
  * @return string
508
  */
509
  private static function get_ordered_categories_for_product( \WC_Product $product ) {
510
+ // get all categories for the product
511
  if ( $product->is_type( 'variation' ) ) {
 
512
  $parent_product = wc_get_product( $product->get_parent_id() );
513
  $categories = $parent_product instanceof \WC_Product ? get_the_terms( $parent_product->get_id(), 'product_cat' ) : array();
 
514
  } else {
 
515
  $categories = get_the_terms( $product->get_id(), 'product_cat' );
516
  }
 
517
  if ( empty( $categories ) ) {
518
  return array();
519
  }
 
520
  $categories_per_level = array();
 
521
  // determine the level (depth) of each category
522
  foreach ( $categories as $category ) {
 
523
  $level = 0;
524
  $parent_category = $category;
 
525
  while ( (int) $parent_category->parent !== 0 ) {
 
526
  $parent_category = get_term( $parent_category->parent, 'product_cat' );
 
527
  if ( ! $parent_category instanceof \WP_Term ) {
528
  break;
529
  }
 
530
  $level ++;
531
  }
 
532
  if ( empty( $categories_per_level[ $level ] ) ) {
533
  $categories_per_level[ $level ] = array();
534
  }
 
535
  $categories_per_level[ $level ][] = $category;
536
  }
 
537
  // sort descending by level
538
  krsort( $categories_per_level );
539
  return $categories_per_level;
694
  *
695
  * @param \WC_Product $product the product object
696
  * @param string $attribute_name the attribute to be used to store the color
697
+ * @throws PluginException
698
  */
699
  public static function update_product_color_attribute( \WC_Product $product, $attribute_name ) {
700
 
701
  // check if the name matches an available attribute
702
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
703
+ throw new PluginException( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
704
  }
705
 
706
  if ( $attribute_name !== self::get_product_color_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
707
+ throw new PluginException( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
708
  }
709
 
710
  $product->update_meta_data( self::COLOR_ATTRIBUTE_META_KEY, $attribute_name );
790
  *
791
  * @param \WC_Product $product the product object
792
  * @param string $attribute_name the attribute to be used to store the size
793
+ * @throws PluginException
794
  */
795
  public static function update_product_size_attribute( \WC_Product $product, $attribute_name ) {
796
 
797
  // check if the name matches an available attribute
798
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
799
+ throw new PluginException( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
800
  }
801
 
802
  if ( $attribute_name !== self::get_product_size_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
803
+ throw new PluginException( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
804
  }
805
 
806
  $product->update_meta_data( self::SIZE_ATTRIBUTE_META_KEY, $attribute_name );
886
  *
887
  * @param \WC_Product $product the product object
888
  * @param string $attribute_name the attribute to be used to store the pattern
889
+ * @throws PluginException
890
  */
891
  public static function update_product_pattern_attribute( \WC_Product $product, $attribute_name ) {
 
892
  // check if the name matches an available attribute
893
  if ( ! empty( $attribute_name ) && ! self::product_has_attribute( $product, $attribute_name ) ) {
894
+ throw new PluginException( "The provided attribute name $attribute_name does not match any of the available attributes for the product {$product->get_name()}" );
895
  }
 
896
  if ( $attribute_name !== self::get_product_pattern_attribute( $product ) && in_array( $attribute_name, self::get_distinct_product_attributes( $product ) ) ) {
897
+ throw new PluginException( "The provided attribute $attribute_name is already used for the product {$product->get_name()}" );
898
  }
 
899
  $product->update_meta_data( self::PATTERN_ATTRIBUTE_META_KEY, $attribute_name );
900
  $product->save_meta_data();
901
  }
1042
  return array_merge(
1043
  $attrs,
1044
  array(
1045
+ str_replace( $prefix, '', $attr_key ) => wc_clean( Helper::get_posted_value( $attr_key ) ),
1046
  )
1047
  );
1048
  },
includes/Products/FBCategories.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) || exit;
15
 
@@ -22,14 +22,6 @@ defined( 'ABSPATH' ) || exit;
22
  */
23
  class FBCategories {
24
 
25
- /**
26
- * This function ensures that everything is loaded before the we start using the data.
27
- */
28
- private function ensure_data_is_loaded() {
29
- // This makes the GoogleProductTaxonomy available.
30
- require_once __DIR__ . '/GoogleProductTaxonomy.php';
31
- }
32
-
33
  /**
34
  * Fetches the attribute from a category using attribute key.
35
  *
@@ -88,7 +80,6 @@ class FBCategories {
88
  * @return null|array Null if category was not found or the category array.
89
  */
90
  public function get_category( $category_id ) {
91
- $this->ensure_data_is_loaded();
92
  if ( $this->is_category( $category_id ) ) {
93
  return GoogleProductTaxonomy::TAXONOMY[ $category_id ];
94
  } else {
@@ -152,7 +143,6 @@ class FBCategories {
152
  * @return null|array Null if no attributes were found or category is invalid, otherwise array of attributes.
153
  */
154
  public function get_attributes_with_fallback_to_parent_category( $category_id ) {
155
- $this->ensure_data_is_loaded();
156
  if ( ! $this->is_category( $category_id ) ) {
157
  return null;
158
  }
@@ -189,7 +179,6 @@ class FBCategories {
189
  * @return boolean Is the id a valid category id.
190
  */
191
  public function is_category( $category_id ) {
192
- $this->ensure_data_is_loaded();
193
  return isset( GoogleProductTaxonomy::TAXONOMY[ $category_id ] );
194
  }
195
 
@@ -199,7 +188,6 @@ class FBCategories {
199
  * @return array All categories data.
200
  */
201
  public function get_categories() {
202
- $this->ensure_data_is_loaded();
203
  return GoogleProductTaxonomy::TAXONOMY;
204
  }
205
 
@@ -227,7 +215,6 @@ class FBCategories {
227
  */
228
  protected function get_raw_attributes_data() {
229
  static $data = null;
230
-
231
  if ( null === $data ) {
232
  $contents = file_get_contents( facebook_for_woocommerce()->get_plugin_path() . '/data/google_category_to_attribute_mapping.json' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
233
  if ( $contents ) {
@@ -237,7 +224,6 @@ class FBCategories {
237
  facebook_for_woocommerce()->log( 'Error reading category attributes JSON data.' );
238
  }
239
  }
240
-
241
  return $data;
242
  }
243
 
@@ -261,36 +247,4 @@ class FBCategories {
261
 
262
  return $data;
263
  }
264
-
265
- /**
266
- * @deprecated in version 2.4.0. Use `::get_attributes_with_fallback_to_parent_category()` instead.
267
- *
268
- * @see \SkyVerge\WooCommerce\Facebook\Products\FBCategories::get_attributes_with_fallback_to_parent_category()
269
- *
270
- * Get attributes for the category.
271
- *
272
- * @param string $category_id Id of the category for which we want to fetch attributes.
273
- *
274
- * @return null|array
275
- */
276
- public function get_category_with_attrs( $category_id ) {
277
- wc_deprecated_function( __METHOD__, '2.4.0', __CLASS__ . '::get_attributes_with_fallback_to_parent_category' );
278
-
279
- $attributes = $this->get_attributes_with_fallback_to_parent_category( $category_id );
280
- if ( ! is_array( $attributes ) ) {
281
- return null;
282
- }
283
-
284
- // Use legacy return format for backwards compatibility with 3rd party code
285
- $all_attributes_data = $this->get_raw_attributes_data();
286
- if ( ! isset( $all_attributes_data[ $category_id ] ) ) {
287
- return null;
288
- }
289
-
290
- $category = $all_attributes_data[ $category_id ];
291
- $category['attributes'] = $attributes;
292
-
293
- return $category;
294
- }
295
-
296
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) || exit;
15
 
22
  */
23
  class FBCategories {
24
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Fetches the attribute from a category using attribute key.
27
  *
80
  * @return null|array Null if category was not found or the category array.
81
  */
82
  public function get_category( $category_id ) {
 
83
  if ( $this->is_category( $category_id ) ) {
84
  return GoogleProductTaxonomy::TAXONOMY[ $category_id ];
85
  } else {
143
  * @return null|array Null if no attributes were found or category is invalid, otherwise array of attributes.
144
  */
145
  public function get_attributes_with_fallback_to_parent_category( $category_id ) {
 
146
  if ( ! $this->is_category( $category_id ) ) {
147
  return null;
148
  }
179
  * @return boolean Is the id a valid category id.
180
  */
181
  public function is_category( $category_id ) {
 
182
  return isset( GoogleProductTaxonomy::TAXONOMY[ $category_id ] );
183
  }
184
 
188
  * @return array All categories data.
189
  */
190
  public function get_categories() {
 
191
  return GoogleProductTaxonomy::TAXONOMY;
192
  }
193
 
215
  */
216
  protected function get_raw_attributes_data() {
217
  static $data = null;
 
218
  if ( null === $data ) {
219
  $contents = file_get_contents( facebook_for_woocommerce()->get_plugin_path() . '/data/google_category_to_attribute_mapping.json' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
220
  if ( $contents ) {
224
  facebook_for_woocommerce()->log( 'Error reading category attributes JSON data.' );
225
  }
226
  }
 
227
  return $data;
228
  }
229
 
247
 
248
  return $data;
249
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  }
includes/Products/Feed.php CHANGED
@@ -9,12 +9,13 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Products;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Utilities\Heartbeat;
17
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
18
 
19
  /**
20
  * The main product feed handler.
@@ -42,7 +43,6 @@ class Feed {
42
  * @since 1.11.0
43
  */
44
  public function __construct() {
45
-
46
  // add the necessary action and filter hooks
47
  $this->add_hooks();
48
  }
@@ -54,7 +54,6 @@ class Feed {
54
  * @since 1.11.0
55
  */
56
  private function add_hooks() {
57
-
58
  // schedule the recurring feed generation
59
  add_action( Heartbeat::HOURLY, array( $this, 'schedule_feed_generation' ) );
60
 
@@ -74,7 +73,6 @@ class Feed {
74
  * @since 1.11.0
75
  */
76
  public function handle_feed_data_request() {
77
-
78
  \WC_Facebookcommerce_Utils::log( 'Facebook is requesting the product feed.' );
79
  facebook_for_woocommerce()->get_tracker()->track_feed_file_requested();
80
 
@@ -87,15 +85,14 @@ class Feed {
87
  }
88
 
89
  try {
90
-
91
  // bail early if the feed secret is not included or is not valid
92
- if ( self::get_feed_secret() !== Framework\SV_WC_Helper::get_requested_value( 'secret' ) ) {
93
- throw new Framework\SV_WC_Plugin_Exception( 'Invalid feed secret provided.', 401 );
94
  }
95
 
96
  // bail early if the file can't be read
97
  if ( ! is_readable( $file_path ) ) {
98
- throw new Framework\SV_WC_Plugin_Exception( 'File is not readable.', 404 );
99
  }
100
 
101
  // set the download headers
@@ -108,31 +105,23 @@ class Feed {
108
  header( 'Content-Length:' . filesize( $file_path ) );
109
 
110
  $file = @fopen( $file_path, 'rb' );
111
-
112
  if ( ! $file ) {
113
- throw new Framework\SV_WC_Plugin_Exception( 'Could not open feed file.', 500 );
114
  }
115
 
116
  // fpassthru might be disabled in some hosts (like Flywheel)
117
  if ( $this->is_fpassthru_disabled() || ! @fpassthru( $file ) ) {
118
-
119
  \WC_Facebookcommerce_Utils::log( 'fpassthru is disabled: getting file contents' );
120
-
121
  $contents = @stream_get_contents( $file );
122
-
123
  if ( ! $contents ) {
124
- throw new Framework\SV_WC_Plugin_Exception( 'Could not get feed file contents.', 500 );
125
  }
126
-
127
  echo $contents;
128
  }
129
  } catch ( \Exception $exception ) {
130
-
131
  \WC_Facebookcommerce_Utils::log( 'Could not serve product feed. ' . $exception->getMessage() . ' (' . $exception->getCode() . ')' );
132
-
133
  status_header( $exception->getCode() );
134
  }
135
-
136
  exit;
137
  }
138
 
@@ -166,7 +155,6 @@ class Feed {
166
  public function schedule_feed_generation() {
167
  $integration = facebook_for_woocommerce()->get_integration();
168
  $configured_ok = $integration && $integration->is_configured();
169
-
170
  // Only schedule feed job if store has not opted out of product sync.
171
  $store_allows_sync = $configured_ok && $integration->is_product_sync_enabled();
172
  // Only schedule if has not opted out of feed generation (e.g. large stores).
@@ -185,7 +173,6 @@ class Feed {
185
  * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes.
186
  */
187
  $interval = apply_filters( 'wc_facebook_feed_generation_interval', DAY_IN_SECONDS );
188
-
189
  if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) {
190
  as_schedule_recurring_action( time(), max( 2, $interval ), self::GENERATE_FEED_ACTION, array(), facebook_for_woocommerce()->get_id_dasherized() );
191
  }
@@ -202,16 +189,11 @@ class Feed {
202
  * @return bool
203
  */
204
  private function is_fpassthru_disabled() {
205
-
206
  $disabled = false;
207
-
208
  if ( function_exists( 'ini_get' ) ) {
209
-
210
  $disabled_functions = @ini_get( 'disable_functions' );
211
-
212
  $disabled = is_string( $disabled_functions ) && in_array( 'fpassthru', explode( ',', $disabled_functions ), false );
213
  }
214
-
215
  return $disabled;
216
  }
217
 
@@ -224,12 +206,10 @@ class Feed {
224
  * @return string
225
  */
226
  public static function get_feed_data_url() {
227
-
228
- $query_args = array(
229
  'wc-api' => self::REQUEST_FEED_ACTION,
230
  'secret' => self::get_feed_secret(),
231
- );
232
-
233
  return add_query_arg( $query_args, home_url( '/' ) );
234
  }
235
 
@@ -244,18 +224,11 @@ class Feed {
244
  * @return string
245
  */
246
  public static function get_feed_secret() {
247
-
248
  $secret = get_option( self::OPTION_FEED_URL_SECRET, '' );
249
-
250
  if ( ! $secret ) {
251
-
252
  $secret = wp_hash( 'products-feed-' . time() );
253
-
254
  update_option( self::OPTION_FEED_URL_SECRET, $secret );
255
  }
256
-
257
  return $secret;
258
  }
259
-
260
-
261
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Products;
13
 
14
+ defined( 'ABSPATH' ) || exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Helper;
17
+ use WooCommerce\Facebook\Utilities\Heartbeat;
18
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
19
 
20
  /**
21
  * The main product feed handler.
43
  * @since 1.11.0
44
  */
45
  public function __construct() {
 
46
  // add the necessary action and filter hooks
47
  $this->add_hooks();
48
  }
54
  * @since 1.11.0
55
  */
56
  private function add_hooks() {
 
57
  // schedule the recurring feed generation
58
  add_action( Heartbeat::HOURLY, array( $this, 'schedule_feed_generation' ) );
59
 
73
  * @since 1.11.0
74
  */
75
  public function handle_feed_data_request() {
 
76
  \WC_Facebookcommerce_Utils::log( 'Facebook is requesting the product feed.' );
77
  facebook_for_woocommerce()->get_tracker()->track_feed_file_requested();
78
 
85
  }
86
 
87
  try {
 
88
  // bail early if the feed secret is not included or is not valid
89
+ if ( self::get_feed_secret() !== Helper::get_requested_value( 'secret' ) ) {
90
+ throw new PluginException( 'Invalid feed secret provided.', 401 );
91
  }
92
 
93
  // bail early if the file can't be read
94
  if ( ! is_readable( $file_path ) ) {
95
+ throw new PluginException( 'File is not readable.', 404 );
96
  }
97
 
98
  // set the download headers
105
  header( 'Content-Length:' . filesize( $file_path ) );
106
 
107
  $file = @fopen( $file_path, 'rb' );
 
108
  if ( ! $file ) {
109
+ throw new PluginException( 'Could not open feed file.', 500 );
110
  }
111
 
112
  // fpassthru might be disabled in some hosts (like Flywheel)
113
  if ( $this->is_fpassthru_disabled() || ! @fpassthru( $file ) ) {
 
114
  \WC_Facebookcommerce_Utils::log( 'fpassthru is disabled: getting file contents' );
 
115
  $contents = @stream_get_contents( $file );
 
116
  if ( ! $contents ) {
117
+ throw new PluginException( 'Could not get feed file contents.', 500 );
118
  }
 
119
  echo $contents;
120
  }
121
  } catch ( \Exception $exception ) {
 
122
  \WC_Facebookcommerce_Utils::log( 'Could not serve product feed. ' . $exception->getMessage() . ' (' . $exception->getCode() . ')' );
 
123
  status_header( $exception->getCode() );
124
  }
 
125
  exit;
126
  }
127
 
155
  public function schedule_feed_generation() {
156
  $integration = facebook_for_woocommerce()->get_integration();
157
  $configured_ok = $integration && $integration->is_configured();
 
158
  // Only schedule feed job if store has not opted out of product sync.
159
  $store_allows_sync = $configured_ok && $integration->is_product_sync_enabled();
160
  // Only schedule if has not opted out of feed generation (e.g. large stores).
173
  * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes.
174
  */
175
  $interval = apply_filters( 'wc_facebook_feed_generation_interval', DAY_IN_SECONDS );
 
176
  if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) {
177
  as_schedule_recurring_action( time(), max( 2, $interval ), self::GENERATE_FEED_ACTION, array(), facebook_for_woocommerce()->get_id_dasherized() );
178
  }
189
  * @return bool
190
  */
191
  private function is_fpassthru_disabled() {
 
192
  $disabled = false;
 
193
  if ( function_exists( 'ini_get' ) ) {
 
194
  $disabled_functions = @ini_get( 'disable_functions' );
 
195
  $disabled = is_string( $disabled_functions ) && in_array( 'fpassthru', explode( ',', $disabled_functions ), false );
196
  }
 
197
  return $disabled;
198
  }
199
 
206
  * @return string
207
  */
208
  public static function get_feed_data_url() {
209
+ $query_args = [
 
210
  'wc-api' => self::REQUEST_FEED_ACTION,
211
  'secret' => self::get_feed_secret(),
212
+ ];
 
213
  return add_query_arg( $query_args, home_url( '/' ) );
214
  }
215
 
224
  * @return string
225
  */
226
  public static function get_feed_secret() {
 
227
  $secret = get_option( self::OPTION_FEED_URL_SECRET, '' );
 
228
  if ( ! $secret ) {
 
229
  $secret = wp_hash( 'products-feed-' . time() );
 
230
  update_option( self::OPTION_FEED_URL_SECRET, $secret );
231
  }
 
232
  return $secret;
233
  }
 
 
234
  }
includes/Products/GoogleProductTaxonomy.php CHANGED
@@ -1,5 +1,6 @@
1
  <?php
2
- namespace SkyVerge\WooCommerce\Facebook\Products;
 
3
  // This file was generated using GenerateCategories.php, do not modify it manually
4
  // php GenerateCategories.php taxonomy-with-ids.en-US.txt
5
  class GoogleProductTaxonomy {
1
  <?php
2
+ // phpcs:ignoreFile
3
+ namespace WooCommerce\Facebook\Products;
4
  // This file was generated using GenerateCategories.php, do not modify it manually
5
  // php GenerateCategories.php taxonomy-with-ids.en-US.txt
6
  class GoogleProductTaxonomy {
includes/Products/Stock.php CHANGED
@@ -9,11 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Products;
17
 
18
  /**
19
  * The product stock handler.
@@ -22,14 +22,12 @@ use SkyVerge\WooCommerce\Facebook\Products;
22
  */
23
  class Stock {
24
 
25
-
26
  /**
27
  * Stock constructor.
28
  *
29
  * @since 2.0.5
30
  */
31
  public function __construct() {
32
-
33
  $this->add_hooks();
34
  }
35
 
@@ -40,7 +38,6 @@ class Stock {
40
  * @since 2.0.5
41
  */
42
  private function add_hooks() {
43
-
44
  add_action( 'woocommerce_variation_set_stock', array( $this, 'set_product_stock' ) );
45
  add_action( 'woocommerce_product_set_stock', array( $this, 'set_product_stock' ) );
46
  }
@@ -56,11 +53,9 @@ class Stock {
56
  * @param \WC_Product $product the product that was updated
57
  */
58
  public function set_product_stock( $product ) {
59
-
60
  if ( ! $product instanceof \WC_Product ) {
61
  return;
62
  }
63
-
64
  foreach ( $this->get_products_to_sync( $product ) as $item ) {
65
  $this->maybe_sync_product_stock_status( $item );
66
  }
@@ -79,9 +74,7 @@ class Stock {
79
  * @return \WC_Product[]
80
  */
81
  private function get_products_to_sync( \WC_Product $product ) {
82
-
83
  if ( $product->is_type( 'variable' ) ) {
84
-
85
  return array_filter(
86
  array_map( 'wc_get_product', $product->get_children() ),
87
  function ( $item ) {
@@ -89,7 +82,6 @@ class Stock {
89
  }
90
  );
91
  }
92
-
93
  return array( $product );
94
  }
95
 
@@ -104,15 +96,10 @@ class Stock {
104
  * @param \WC_Product $product a product object
105
  */
106
  private function maybe_sync_product_stock_status( \WC_Product $product ) {
107
-
108
  if ( Products::product_should_be_deleted( $product ) ) {
109
-
110
  facebook_for_woocommerce()->get_products_sync_handler()->delete_products( array( \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ) ) );
111
  return;
112
  }
113
-
114
  facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( array( $product->get_id() ) );
115
  }
116
-
117
-
118
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Products;
17
 
18
  /**
19
  * The product stock handler.
22
  */
23
  class Stock {
24
 
 
25
  /**
26
  * Stock constructor.
27
  *
28
  * @since 2.0.5
29
  */
30
  public function __construct() {
 
31
  $this->add_hooks();
32
  }
33
 
38
  * @since 2.0.5
39
  */
40
  private function add_hooks() {
 
41
  add_action( 'woocommerce_variation_set_stock', array( $this, 'set_product_stock' ) );
42
  add_action( 'woocommerce_product_set_stock', array( $this, 'set_product_stock' ) );
43
  }
53
  * @param \WC_Product $product the product that was updated
54
  */
55
  public function set_product_stock( $product ) {
 
56
  if ( ! $product instanceof \WC_Product ) {
57
  return;
58
  }
 
59
  foreach ( $this->get_products_to_sync( $product ) as $item ) {
60
  $this->maybe_sync_product_stock_status( $item );
61
  }
74
  * @return \WC_Product[]
75
  */
76
  private function get_products_to_sync( \WC_Product $product ) {
 
77
  if ( $product->is_type( 'variable' ) ) {
 
78
  return array_filter(
79
  array_map( 'wc_get_product', $product->get_children() ),
80
  function ( $item ) {
82
  }
83
  );
84
  }
 
85
  return array( $product );
86
  }
87
 
96
  * @param \WC_Product $product a product object
97
  */
98
  private function maybe_sync_product_stock_status( \WC_Product $product ) {
 
99
  if ( Products::product_should_be_deleted( $product ) ) {
 
100
  facebook_for_woocommerce()->get_products_sync_handler()->delete_products( array( \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ) ) );
101
  return;
102
  }
 
103
  facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( array( $product->get_id() ) );
104
  }
 
 
105
  }
includes/Products/Sync.php CHANGED
@@ -9,12 +9,10 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Products;
17
-
18
  /**
19
  * The product sync handler.
20
  *
@@ -93,7 +91,6 @@ class Sync {
93
  * @param int[] $product_ids
94
  */
95
  public function create_or_update_products( array $product_ids ) {
96
-
97
  foreach ( $product_ids as $product_id ) {
98
  $this->requests[ $this->get_product_index( $product_id ) ] = self::ACTION_UPDATE;
99
  }
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Products;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
 
 
16
  /**
17
  * The product sync handler.
18
  *
91
  * @param int[] $product_ids
92
  */
93
  public function create_or_update_products( array $product_ids ) {
 
94
  foreach ( $product_ids as $product_id ) {
95
  $this->requests[ $this->get_product_index( $product_id ) ] = self::ACTION_UPDATE;
96
  }
includes/Products/Sync/Background.php CHANGED
@@ -1,29 +1,20 @@
1
  <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Products\Sync;
13
 
14
- defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\Facebook\Products;
17
- use SkyVerge\WooCommerce\Facebook\Products\Sync;
18
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
 
 
19
 
20
  /**
21
  * The background sync handler.
22
- *
23
- * @since 2.0.0
24
  */
25
- class Background extends Framework\SV_WP_Background_Job_Handler {
26
-
27
 
28
  /** @var string async request prefix */
29
  protected $prefix = 'wc_facebook';
@@ -34,7 +25,6 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
34
  /** @var string data key */
35
  protected $data_key = 'requests';
36
 
37
-
38
  /**
39
  * Processes a job.
40
  *
@@ -42,11 +32,10 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
42
  *
43
  * @param \stdClass|object $job
44
  * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
45
- * @throws \Exception when job data is incorrect
46
  * @return \stdClass $job
47
  */
48
  public function process_job( $job, $items_per_batch = null ) {
49
-
50
  $profiling_logger = facebook_for_woocommerce()->get_profiling_logger();
51
  $profiling_logger->start( 'background_product_sync__process_job' );
52
 
@@ -66,11 +55,13 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
66
  $data_key = $this->data_key;
67
 
68
  if ( ! isset( $job->{$data_key} ) ) {
69
- throw new \Exception( sprintf( __( 'Job data key "%s" not set', 'woocommerce-plugin-framework' ), $data_key ) );
 
70
  }
71
 
72
  if ( ! is_array( $job->{$data_key} ) ) {
73
- throw new \Exception( sprintf( __( 'Job data key "%s" is not an array', 'woocommerce-plugin-framework' ), $data_key ) );
 
74
  }
75
 
76
  $data = $job->{$data_key};
@@ -114,28 +105,23 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
114
  * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
115
  */
116
  public function process_items( $job, $data, $items_per_batch = null ) {
117
-
118
  $processed = 0;
119
- $requests = array();
120
 
121
  foreach ( $data as $item_id => $method ) {
122
-
123
  try {
124
-
125
- if ( $request = $this->process_item( array( $item_id, $method ), $job ) ) {
126
  $requests[] = $request;
127
  }
128
- } catch ( Framework\SV_WC_Plugin_Exception $e ) {
129
-
130
  facebook_for_woocommerce()->log( "Background sync error: {$e->getMessage()}" );
131
  }
132
 
133
  $processed++;
134
  $job->progress++;
135
-
136
  // update job progress
137
  $job = $this->update_job( $job );
138
-
139
  // job limits reached
140
  if ( ( $items_per_batch && $processed >= $items_per_batch ) || $this->time_exceeded() || $this->memory_exceeded() ) {
141
  break;
@@ -144,41 +130,30 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
144
 
145
  // send item updates to Facebook and update the job with the returned array of batch handles
146
  if ( ! empty( $requests ) ) {
147
-
148
  try {
149
-
150
- $handles = $this->send_item_updates( $requests );
151
-
152
  $job->handles = ! isset( $job->handles ) || ! is_array( $job->handles ) ? $handles : array_merge( $job->handles, $handles );
153
-
154
- $job = $this->update_job( $job );
155
-
156
- } catch ( Framework\SV_WC_API_Exception $e ) {
157
-
158
- $message = sprintf( __( 'There was an error trying sync products using the Catalog Batch API for job %1$s: %2$s' ), $job->id, $e->getMessage() );
159
-
160
  facebook_for_woocommerce()->log( $message );
161
  }
162
  }
163
  }
164
 
165
-
166
  /**
167
  * Processes a single item.
168
  *
169
- * @since 2.0.0
170
- *
171
  * @param mixed $item
172
  * @param object|\stdClass $job
173
  * @return array|null
174
- * @throws Framework\SV_WC_Plugin_Exception
175
  */
176
  public function process_item( $item, $job ) {
177
-
178
  list( $item_id, $method ) = $item;
179
-
180
- if ( ! in_array( $method, array( Sync::ACTION_UPDATE, Sync::ACTION_DELETE ), true ) ) {
181
- throw new Framework\SV_WC_Plugin_Exception( "Invalid sync request method: {$method}." );
182
  }
183
 
184
  if ( Sync::ACTION_UPDATE === $method ) {
@@ -186,11 +161,9 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
186
  } else {
187
  $request = $this->process_item_delete( $item_id );
188
  }
189
-
190
  return $request;
191
  }
192
 
193
-
194
  /**
195
  * Processes an UPDATE sync request for the given product.
196
  *
@@ -198,19 +171,17 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
198
  *
199
  * @param string $prefixed_product_id prefixed product ID
200
  * @return array|null
201
- * @throws Framework\SV_WC_Plugin_Exception
202
  */
203
  private function process_item_update( $prefixed_product_id ) {
204
-
205
  $product_id = (int) str_replace( Sync::PRODUCT_INDEX_PREFIX, '', $prefixed_product_id );
206
  $product = wc_get_product( $product_id );
207
 
208
  if ( ! $product instanceof \WC_Product ) {
209
- throw new Framework\SV_WC_Plugin_Exception( "No product found with ID equal to {$product_id}." );
210
  }
211
 
212
  $request = null;
213
-
214
  if ( ! Products::product_should_be_deleted( $product ) && Products::product_should_be_synced( $product ) ) {
215
 
216
  if ( $product->is_type( 'variation' ) ) {
@@ -227,11 +198,10 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
227
  unset( $product_data['retailer_id'] );
228
  $product_data['id'] = $retailer_id;
229
 
230
- $request = array(
231
- // 'retailer_id' => $retailer_id,
232
  'method' => Sync::ACTION_UPDATE,
233
  'data' => $product_data,
234
- );
235
 
236
  /**
237
  * Filters the data that will be included in a UPDATE sync request.
@@ -247,7 +217,6 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
247
  return $request;
248
  }
249
 
250
-
251
  /**
252
  * Prepares the data for a product variation to be included in a sync request.
253
  *
@@ -255,14 +224,13 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
255
  *
256
  * @param \WC_Product $product product object
257
  * @return array
258
- * @throws Framework\SV_WC_Plugin_Exception
259
  */
260
  private function prepare_product_variation_data( $product ) {
261
-
262
  $parent_product = wc_get_product( $product->get_parent_id() );
263
 
264
  if ( ! $parent_product instanceof \WC_Product ) {
265
- throw new Framework\SV_WC_Plugin_Exception( "No parent product found with ID equal to {$product->get_parent_id()}." );
266
  }
267
 
268
  $fb_parent_product = new \WC_Facebook_Product( $parent_product->get_id() );
@@ -287,16 +255,13 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
287
  * @return array
288
  */
289
  private function normalize_product_data( $data ) {
290
-
291
  // Allowed values are 'refurbished', 'used', and 'new', but the plugin has always used the latter.
292
  $data['condition'] = 'new';
293
-
294
  // Attributes other than size, color, pattern, or gender need to be included in the additional_variant_attributes field.
295
  if ( isset( $data['custom_data'] ) && is_array( $data['custom_data'] ) ) {
296
-
297
- $attributes = array();
298
-
299
  foreach ( $data['custom_data'] as $key => $val ) {
 
300
  /**
301
  * Filter: facebook_for_woocommerce_variant_attribute_comma_replacement
302
  *
@@ -318,7 +283,7 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
318
  $val
319
  );
320
  /** Force replacing , and : characters if those were not cleaned up by filters */
321
- $attributes[] = $key . ':' . str_replace( array( ',', ':' ), ' ', $attribute_value );
322
  }
323
 
324
  $data['additional_variant_attribute'] = implode( ',', $attributes );
@@ -337,31 +302,23 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
337
  * @return array
338
  */
339
  private function prepare_product_data( $product ) {
340
-
341
  $fb_product = new \WC_Facebook_Product( $product->get_id() );
342
-
343
- $data = $fb_product->prepare_product( null, \WC_Facebook_Product::PRODUCT_PREP_TYPE_ITEMS_BATCH );
344
-
345
  // products that are not variations use their retailer retailer ID as the retailer product group ID
346
  $data['item_group_id'] = $data['retailer_id'];
347
-
348
  return $this->normalize_product_data( $data );
349
  }
350
 
351
-
352
  /**
353
  * Processes a DELETE sync request for the given product.
354
  *
355
- * @since 2.0.0
356
- *
357
- * @param string $retailer product retailer ID
358
  */
359
  private function process_item_delete( $retailer_id ) {
360
-
361
- $request = array(
362
- 'data' => array( 'id' => $retailer_id ),
363
  'method' => Sync::ACTION_DELETE,
364
- );
365
 
366
  /**
367
  * Filters the data that will be included in a DELETE sync request.
@@ -374,23 +331,16 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
374
  return apply_filters( 'wc_facebook_sync_background_item_delete_request', $request, $retailer_id );
375
  }
376
 
377
-
378
  /**
379
  * Sends item updates to Facebook.
380
  *
381
- * @since 2.0.0
382
- *
383
- * @param array $requests sync requests
384
- * @return array
385
- * @throws Framework\SV_WC_API_Exception
386
  */
387
- private function send_item_updates( array $requests ) {
388
-
389
- $catalog_id = facebook_for_woocommerce()->get_integration()->get_product_catalog_id();
390
- $response = facebook_for_woocommerce()->get_api()->send_item_updates( $catalog_id, $requests, true );
391
-
392
- return $response->get_handles();
393
  }
394
-
395
-
396
  }
1
  <?php
2
+ declare( strict_types=1 );
 
 
 
 
 
 
 
 
3
 
4
+ namespace WooCommerce\Facebook\Products\Sync;
5
 
6
+ defined( 'ABSPATH' ) || exit;
7
 
8
+ use WooCommerce\Facebook\Framework\Api\Exception as ApiException;
9
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
10
+ use WooCommerce\Facebook\Framework\Utilities\BackgroundJobHandler;
11
+ use WooCommerce\Facebook\Products;
12
+ use WooCommerce\Facebook\Products\Sync;
13
 
14
  /**
15
  * The background sync handler.
 
 
16
  */
17
+ class Background extends BackgroundJobHandler {
 
18
 
19
  /** @var string async request prefix */
20
  protected $prefix = 'wc_facebook';
25
  /** @var string data key */
26
  protected $data_key = 'requests';
27
 
 
28
  /**
29
  * Processes a job.
30
  *
32
  *
33
  * @param \stdClass|object $job
34
  * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
35
+ * @throws \Exception When job data is incorrect.
36
  * @return \stdClass $job
37
  */
38
  public function process_job( $job, $items_per_batch = null ) {
 
39
  $profiling_logger = facebook_for_woocommerce()->get_profiling_logger();
40
  $profiling_logger->start( 'background_product_sync__process_job' );
41
 
55
  $data_key = $this->data_key;
56
 
57
  if ( ! isset( $job->{$data_key} ) ) {
58
+ /* translators: Placeholders: %s - user-friendly error message */
59
+ throw new \Exception( sprintf( __( 'Job data key "%s" not set', 'facebook-for-woocommerce' ), $data_key ) );
60
  }
61
 
62
  if ( ! is_array( $job->{$data_key} ) ) {
63
+ /* translators: Placeholders: %s - user-friendly error message */
64
+ throw new \Exception( sprintf( __( 'Job data key "%s" is not an array', 'facebook-for-woocommerce' ), $data_key ) );
65
  }
66
 
67
  $data = $job->{$data_key};
105
  * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
106
  */
107
  public function process_items( $job, $data, $items_per_batch = null ) {
 
108
  $processed = 0;
109
+ $requests = [];
110
 
111
  foreach ( $data as $item_id => $method ) {
 
112
  try {
113
+ $request = $this->process_item( [ $item_id, $method ], $job );
114
+ if ( $request ) {
115
  $requests[] = $request;
116
  }
117
+ } catch ( PluginException $e ) {
 
118
  facebook_for_woocommerce()->log( "Background sync error: {$e->getMessage()}" );
119
  }
120
 
121
  $processed++;
122
  $job->progress++;
 
123
  // update job progress
124
  $job = $this->update_job( $job );
 
125
  // job limits reached
126
  if ( ( $items_per_batch && $processed >= $items_per_batch ) || $this->time_exceeded() || $this->memory_exceeded() ) {
127
  break;
130
 
131
  // send item updates to Facebook and update the job with the returned array of batch handles
132
  if ( ! empty( $requests ) ) {
 
133
  try {
134
+ $handles = $this->send_item_updates( $requests );
 
 
135
  $job->handles = ! isset( $job->handles ) || ! is_array( $job->handles ) ? $handles : array_merge( $job->handles, $handles );
136
+ $this->update_job( $job );
137
+ } catch ( ApiException $e ) {
138
+ /* translators: Placeholders: %1$s - <string job ID, %2$s - <strong> error message */
139
+ $message = sprintf( __( 'There was an error trying sync products using the Catalog Batch API for job %1$s: %2$s', 'facebook-for-woocommerce' ), $job->id, $e->getMessage() );
 
 
 
140
  facebook_for_woocommerce()->log( $message );
141
  }
142
  }
143
  }
144
 
 
145
  /**
146
  * Processes a single item.
147
  *
 
 
148
  * @param mixed $item
149
  * @param object|\stdClass $job
150
  * @return array|null
151
+ * @throws PluginException In case of invalid sync request method.
152
  */
153
  public function process_item( $item, $job ) {
 
154
  list( $item_id, $method ) = $item;
155
+ if ( ! in_array( $method, [ Sync::ACTION_UPDATE, Sync::ACTION_DELETE ], true ) ) {
156
+ throw new PluginException( "Invalid sync request method: {$method}." );
 
157
  }
158
 
159
  if ( Sync::ACTION_UPDATE === $method ) {
161
  } else {
162
  $request = $this->process_item_delete( $item_id );
163
  }
 
164
  return $request;
165
  }
166
 
 
167
  /**
168
  * Processes an UPDATE sync request for the given product.
169
  *
171
  *
172
  * @param string $prefixed_product_id prefixed product ID
173
  * @return array|null
174
+ * @throws PluginException In case no product was found.
175
  */
176
  private function process_item_update( $prefixed_product_id ) {
 
177
  $product_id = (int) str_replace( Sync::PRODUCT_INDEX_PREFIX, '', $prefixed_product_id );
178
  $product = wc_get_product( $product_id );
179
 
180
  if ( ! $product instanceof \WC_Product ) {
181
+ throw new PluginException( "No product found with ID equal to {$product_id}." );
182
  }
183
 
184
  $request = null;
 
185
  if ( ! Products::product_should_be_deleted( $product ) && Products::product_should_be_synced( $product ) ) {
186
 
187
  if ( $product->is_type( 'variation' ) ) {
198
  unset( $product_data['retailer_id'] );
199
  $product_data['id'] = $retailer_id;
200
 
201
+ $request = [
 
202
  'method' => Sync::ACTION_UPDATE,
203
  'data' => $product_data,
204
+ ];
205
 
206
  /**
207
  * Filters the data that will be included in a UPDATE sync request.
217
  return $request;
218
  }
219
 
 
220
  /**
221
  * Prepares the data for a product variation to be included in a sync request.
222
  *
224
  *
225
  * @param \WC_Product $product product object
226
  * @return array
227
+ * @throws PluginException In case no product found.
228
  */
229
  private function prepare_product_variation_data( $product ) {
 
230
  $parent_product = wc_get_product( $product->get_parent_id() );
231
 
232
  if ( ! $parent_product instanceof \WC_Product ) {
233
+ throw new PluginException( "No parent product found with ID equal to {$product->get_parent_id()}." );
234
  }
235
 
236
  $fb_parent_product = new \WC_Facebook_Product( $parent_product->get_id() );
255
  * @return array
256
  */
257
  private function normalize_product_data( $data ) {
 
258
  // Allowed values are 'refurbished', 'used', and 'new', but the plugin has always used the latter.
259
  $data['condition'] = 'new';
 
260
  // Attributes other than size, color, pattern, or gender need to be included in the additional_variant_attributes field.
261
  if ( isset( $data['custom_data'] ) && is_array( $data['custom_data'] ) ) {
262
+ $attributes = [];
 
 
263
  foreach ( $data['custom_data'] as $key => $val ) {
264
+
265
  /**
266
  * Filter: facebook_for_woocommerce_variant_attribute_comma_replacement
267
  *
283
  $val
284
  );
285
  /** Force replacing , and : characters if those were not cleaned up by filters */
286
+ $attributes[] = $key . ':' . str_replace( [ ',', ':' ], ' ', $attribute_value );
287
  }
288
 
289
  $data['additional_variant_attribute'] = implode( ',', $attributes );
302
  * @return array
303
  */
304
  private function prepare_product_data( $product ) {
 
305
  $fb_product = new \WC_Facebook_Product( $product->get_id() );
306
+ $data = $fb_product->prepare_product( null, \WC_Facebook_Product::PRODUCT_PREP_TYPE_ITEMS_BATCH );
 
 
307
  // products that are not variations use their retailer retailer ID as the retailer product group ID
308
  $data['item_group_id'] = $data['retailer_id'];
 
309
  return $this->normalize_product_data( $data );
310
  }
311
 
 
312
  /**
313
  * Processes a DELETE sync request for the given product.
314
  *
315
+ * @param string $retailer_id Product retailer ID.
 
 
316
  */
317
  private function process_item_delete( $retailer_id ) {
318
+ $request = [
319
+ 'data' => [ 'id' => $retailer_id ],
 
320
  'method' => Sync::ACTION_DELETE,
321
+ ];
322
 
323
  /**
324
  * Filters the data that will be included in a DELETE sync request.
331
  return apply_filters( 'wc_facebook_sync_background_item_delete_request', $request, $retailer_id );
332
  }
333
 
 
334
  /**
335
  * Sends item updates to Facebook.
336
  *
337
+ * @param array $requests Array of JSON objects containing batch requests. Each batch request consists of method and data fields.
338
+ * @return array An array of handles.
339
+ * @throws ApiException In case of failed API request.
 
 
340
  */
341
+ private function send_item_updates( array $requests ): array {
342
+ $facebook_catalog_id = facebook_for_woocommerce()->get_integration()->get_product_catalog_id();
343
+ $response = facebook_for_woocommerce()->get_api()->send_item_updates( $facebook_catalog_id, $requests );
344
+ return $response->handles;
 
 
345
  }
 
 
346
  }
includes/Utilities/Background_Handle_Virtual_Products_Variations.php CHANGED
@@ -9,19 +9,18 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
-
18
 
19
  /**
20
  * Background job handler to change all sync enabled and visible virtual products and virtual product variations to Sync and hide.
21
  *
22
  * @since 2.0.0
23
  */
24
- class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Background_Job_Handler {
25
 
26
 
27
  /**
@@ -30,10 +29,8 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
30
  * @since 2.0.0
31
  */
32
  public function __construct() {
33
-
34
  $this->prefix = 'wc_facebook';
35
  $this->action = 'background_handle_virtual_products_variations';
36
-
37
  parent::__construct();
38
  }
39
 
@@ -51,12 +48,9 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
51
  * @return object
52
  */
53
  public function process_job( $job, $items_per_batch = null ) {
54
-
55
  if ( ! isset( $job->total ) ) {
56
  $job->total = $this->count_remaining_products();
57
-
58
  if ( empty( $job->total ) ) {
59
-
60
  // no products or variations need to be set to Sync and hide, do not display admin notice
61
  update_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'yes' );
62
  }
@@ -71,15 +65,11 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
71
 
72
  // set to Sync and hide until memory or time limit is exceeded
73
  while ( $processed_products < $remaining_products ) {
74
-
75
  $rows_updated = $this->sync_and_hide();
76
-
77
  $processed_products += $rows_updated;
78
  $job->progress += $rows_updated;
79
-
80
  // update job progress
81
  $job = $this->update_job( $job );
82
-
83
  // memory or time limit reached
84
  if ( $this->time_exceeded() || $this->memory_exceeded() ) {
85
  break;
@@ -88,9 +78,7 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
88
 
89
  // job complete! :)
90
  if ( $this->count_remaining_products() === 0 ) {
91
-
92
  update_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'yes' );
93
-
94
  $this->complete_job( $job );
95
  }
96
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Utilities\BackgroundJobHandler;
 
17
 
18
  /**
19
  * Background job handler to change all sync enabled and visible virtual products and virtual product variations to Sync and hide.
20
  *
21
  * @since 2.0.0
22
  */
23
+ class Background_Handle_Virtual_Products_Variations extends BackgroundJobHandler {
24
 
25
 
26
  /**
29
  * @since 2.0.0
30
  */
31
  public function __construct() {
 
32
  $this->prefix = 'wc_facebook';
33
  $this->action = 'background_handle_virtual_products_variations';
 
34
  parent::__construct();
35
  }
36
 
48
  * @return object
49
  */
50
  public function process_job( $job, $items_per_batch = null ) {
 
51
  if ( ! isset( $job->total ) ) {
52
  $job->total = $this->count_remaining_products();
 
53
  if ( empty( $job->total ) ) {
 
54
  // no products or variations need to be set to Sync and hide, do not display admin notice
55
  update_option( 'wc_facebook_background_handle_virtual_products_variations_skipped', 'yes' );
56
  }
65
 
66
  // set to Sync and hide until memory or time limit is exceeded
67
  while ( $processed_products < $remaining_products ) {
 
68
  $rows_updated = $this->sync_and_hide();
 
69
  $processed_products += $rows_updated;
70
  $job->progress += $rows_updated;
 
71
  // update job progress
72
  $job = $this->update_job( $job );
 
73
  // memory or time limit reached
74
  if ( $this->time_exceeded() || $this->memory_exceeded() ) {
75
  break;
78
 
79
  // job complete! :)
80
  if ( $this->count_remaining_products() === 0 ) {
 
81
  update_option( 'wc_facebook_background_handle_virtual_products_variations_complete', 'yes' );
 
82
  $this->complete_job( $job );
83
  }
84
 
includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php CHANGED
@@ -9,12 +9,11 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
-
18
 
19
  /**
20
  * Background job handler to remove duplicate fb_visibility entries from the postmeta table.
@@ -23,7 +22,7 @@ use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
23
  *
24
  * @since 2.0.3
25
  */
26
- class Background_Remove_Duplicate_Visibility_Meta extends Framework\SV_WP_Background_Job_Handler {
27
 
28
 
29
  /**
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
16
+ use WooCommerce\Facebook\Framework\Utilities\BackgroundJobHandler;
 
17
 
18
  /**
19
  * Background job handler to remove duplicate fb_visibility entries from the postmeta table.
22
  *
23
  * @since 2.0.3
24
  */
25
+ class Background_Remove_Duplicate_Visibility_Meta extends BackgroundJobHandler {
26
 
27
 
28
  /**
includes/Utilities/Heartbeat.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
- namespace SkyVerge\WooCommerce\Facebook\Utilities;
5
 
6
  use WC_Queue_Interface;
7
 
1
  <?php
2
  // phpcs:ignoreFile
3
 
4
+ namespace WooCommerce\Facebook\Utilities;
5
 
6
  use WC_Queue_Interface;
7
 
includes/Utilities/Shipment.php CHANGED
@@ -9,7 +9,7 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- namespace SkyVerge\WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ namespace WooCommerce\Facebook\Utilities;
13
 
14
  defined( 'ABSPATH' ) or exit;
15
 
includes/Utilities/Tracker.php CHANGED
@@ -8,7 +8,7 @@
8
  * @package FacebookCommerce
9
  */
10
 
11
- namespace SkyVerge\WooCommerce\Facebook\Utilities;
12
 
13
  defined( 'ABSPATH' ) || exit;
14
 
@@ -26,56 +26,56 @@ class Tracker {
26
  *
27
  * @var string
28
  */
29
- const TRANSIENT_WCTRACKER_LIFE_TIME = 2 * WEEK_IN_SECONDS;
30
 
31
  /**
32
  * Transient key name; how long it took to generate the most recent feed file, or minus one if it failed.
33
  *
34
  * @var string
35
  */
36
- const TRANSIENT_WCTRACKER_FEED_GENERATION_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_time';
37
 
38
  /**
39
  * Transient key name; how long it took to generate already generated batches.
40
  *
41
  * @var string
42
  */
43
- const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_time';
44
 
45
  /**
46
  * Transient key name; how much wall ( clock ) time it took to generate the feed file.
47
  *
48
  * @var string
49
  */
50
- const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
51
 
52
  /**
53
  * Transient key name; the time when was the batched feed generation started.
54
  *
55
  * @var string
56
  */
57
- const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_START_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
58
 
59
  /**
60
  * Transient key name; true if feed has been requested by Facebook.
61
  *
62
  * @var string
63
  */
64
- const TRANSIENT_WCTRACKER_FEED_REQUESTED = 'facebook_for_woocommerce_wctracker_feed_requested';
65
 
66
  /**
67
  * Transient key name; stores various FBE business settings.
68
  *
69
  * @var string
70
  */
71
- const TRANSIENT_WCTRACKER_FBE_BUSINESS_CONFIG = 'facebook_for_woocommerce_wctracker_fbe_business_config';
72
 
73
  /**
74
  * Transient key name; stores feed (data source) settings for catalog sync.
75
  *
76
  * @var string
77
  */
78
- const TRANSIENT_WCTRACKER_FB_FEED_CONFIG = 'facebook_for_woocommerce_wctracker_fb_feed_config';
79
 
80
  /**
81
  * Constructor.
@@ -85,7 +85,7 @@ class Tracker {
85
  public function __construct() {
86
  add_filter(
87
  'woocommerce_tracker_data',
88
- array( $this, 'add_tracker_data' )
89
  );
90
  }
91
 
@@ -96,9 +96,9 @@ class Tracker {
96
  * @return array $data Snapshot updated with our data.
97
  * @since 2.3.4
98
  */
99
- public function add_tracker_data( array $data = array() ) {
100
  if ( ! isset( $data['extensions'] ) ) {
101
- $data['extensions'] = array();
102
  }
103
 
104
  /**
@@ -266,10 +266,10 @@ class Tracker {
266
  bool $ig_shopping_enabled,
267
  bool $ig_cta_enabled
268
  ) {
269
- $transient = array(
270
  'ig_shopping_enabled' => $ig_shopping_enabled,
271
  'ig_cta_enabled' => $ig_cta_enabled,
272
- );
273
  set_transient( self::TRANSIENT_WCTRACKER_FBE_BUSINESS_CONFIG, $transient, self::TRANSIENT_WCTRACKER_LIFE_TIME );
274
  }
275
 
8
  * @package FacebookCommerce
9
  */
10
 
11
+ namespace WooCommerce\Facebook\Utilities;
12
 
13
  defined( 'ABSPATH' ) || exit;
14
 
26
  *
27
  * @var string
28
  */
29
+ public const TRANSIENT_WCTRACKER_LIFE_TIME = 2 * WEEK_IN_SECONDS;
30
 
31
  /**
32
  * Transient key name; how long it took to generate the most recent feed file, or minus one if it failed.
33
  *
34
  * @var string
35
  */
36
+ public const TRANSIENT_WCTRACKER_FEED_GENERATION_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_time';
37
 
38
  /**
39
  * Transient key name; how long it took to generate already generated batches.
40
  *
41
  * @var string
42
  */
43
+ public const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_time';
44
 
45
  /**
46
  * Transient key name; how much wall ( clock ) time it took to generate the feed file.
47
  *
48
  * @var string
49
  */
50
+ public const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
51
 
52
  /**
53
  * Transient key name; the time when was the batched feed generation started.
54
  *
55
  * @var string
56
  */
57
+ public const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_START_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
58
 
59
  /**
60
  * Transient key name; true if feed has been requested by Facebook.
61
  *
62
  * @var string
63
  */
64
+ public const TRANSIENT_WCTRACKER_FEED_REQUESTED = 'facebook_for_woocommerce_wctracker_feed_requested';
65
 
66
  /**
67
  * Transient key name; stores various FBE business settings.
68
  *
69
  * @var string
70
  */
71
+ public const TRANSIENT_WCTRACKER_FBE_BUSINESS_CONFIG = 'facebook_for_woocommerce_wctracker_fbe_business_config';
72
 
73
  /**
74
  * Transient key name; stores feed (data source) settings for catalog sync.
75
  *
76
  * @var string
77
  */
78
+ public const TRANSIENT_WCTRACKER_FB_FEED_CONFIG = 'facebook_for_woocommerce_wctracker_fb_feed_config';
79
 
80
  /**
81
  * Constructor.
85
  public function __construct() {
86
  add_filter(
87
  'woocommerce_tracker_data',
88
+ [ $this, 'add_tracker_data' ]
89
  );
90
  }
91
 
96
  * @return array $data Snapshot updated with our data.
97
  * @since 2.3.4
98
  */
99
+ public function add_tracker_data( array $data = [] ) {
100
  if ( ! isset( $data['extensions'] ) ) {
101
+ $data['extensions'] = [];
102
  }
103
 
104
  /**
266
  bool $ig_shopping_enabled,
267
  bool $ig_cta_enabled
268
  ) {
269
+ $transient = [
270
  'ig_shopping_enabled' => $ig_shopping_enabled,
271
  'ig_cta_enabled' => $ig_cta_enabled,
272
+ ];
273
  set_transient( self::TRANSIENT_WCTRACKER_FBE_BUSINESS_CONFIG, $transient, self::TRANSIENT_WCTRACKER_LIFE_TIME );
274
  }
275
 
includes/fbasync.php CHANGED
@@ -9,34 +9,27 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
 
16
  if ( ! class_exists( 'WP_Async_Request', false ) ) {
17
  // Do not attempt to create this class without WP_Async_Request
18
  return;
19
  }
20
 
21
- if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) :
 
 
 
 
 
22
 
23
  /**
24
- * FB Graph API async request
 
 
 
25
  */
26
- class WC_Facebookcommerce_Async_Request extends WP_Async_Request {
27
-
28
- protected $action = 'wc_facebook_async_request';
29
-
30
- /**
31
- * Handle
32
- *
33
- * Override this method to perform any actions required
34
- * during the async request.
35
- */
36
- protected function handle() {
37
- // Actions to perform
38
- }
39
-
40
  }
41
-
42
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
13
 
14
  if ( ! class_exists( 'WP_Async_Request', false ) ) {
15
  // Do not attempt to create this class without WP_Async_Request
16
  return;
17
  }
18
 
19
+ /**
20
+ * FB Graph API async request
21
+ */
22
+ class WC_Facebookcommerce_Async_Request extends WP_Async_Request {
23
+
24
+ protected $action = 'wc_facebook_async_request';
25
 
26
  /**
27
+ * Handle
28
+ *
29
+ * Override this method to perform any actions required
30
+ * during the async request.
31
  */
32
+ protected function handle() {
33
+ // Actions to perform
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
+ }
 
includes/fbbackground.php CHANGED
@@ -9,18 +9,14 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
 
16
  if ( ! class_exists( 'WP_Background_Process', false ) ) {
17
  // Do not attempt to create this class without WP_Background_Process
18
  return;
19
  }
20
 
21
- if ( ! class_exists( 'WC_Facebookcommerce_Background_Process' ) ) :
22
-
23
- class WC_Facebookcommerce_Background_Process extends WP_Background_Process {
24
 
25
  public function __construct( $commerce ) {
26
  $this->commerce = $commerce; // Full WC_Facebookcommerce_Integration obj
@@ -160,5 +156,3 @@ if ( ! class_exists( 'WC_Facebookcommerce_Background_Process' ) ) :
160
  }
161
 
162
  }
163
-
164
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
13
 
14
  if ( ! class_exists( 'WP_Background_Process', false ) ) {
15
  // Do not attempt to create this class without WP_Background_Process
16
  return;
17
  }
18
 
19
+ class WC_Facebookcommerce_Background_Process extends WP_Background_Process {
 
 
20
 
21
  public function __construct( $commerce ) {
22
  $this->commerce = $commerce; // Full WC_Facebookcommerce_Integration obj
156
  }
157
 
158
  }
 
 
includes/fbgraph.php DELETED
@@ -1,677 +0,0 @@
1
- <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- */
11
-
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
17
-
18
- if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
19
-
20
- if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
21
- include_once 'fbasync.php';
22
- }
23
-
24
- /**
25
- * FB Graph API helper functions
26
- */
27
- class WC_Facebookcommerce_Graph_API {
28
- const GRAPH_API_URL = 'https://graph.facebook.com/';
29
- const API_VERSION = 'v13.0';
30
- const CURL_TIMEOUT = 500;
31
-
32
- /**
33
- * Cache the api_key
34
- */
35
- var $api_key;
36
-
37
- /**
38
- * Init
39
- */
40
- public function __construct( $api_key ) {
41
- $this->api_key = $api_key;
42
- }
43
-
44
-
45
- /**
46
- * Issues a GET request to the Graph API.
47
- *
48
- * @param string $url request URL
49
- * @param string $api_key Graph API key
50
- * @return array|\WP_Error
51
- */
52
- public function _get( $url, $api_key = '' ) {
53
-
54
- $api_key = $api_key ?: $this->api_key;
55
-
56
- $request_args = array(
57
- 'headers' => array(
58
- 'Authorization' => 'Bearer ' . $api_key,
59
- ),
60
- 'timeout' => self::CURL_TIMEOUT,
61
- );
62
-
63
- $response = wp_remote_get( $url, $request_args );
64
-
65
- $this->log_request( $url, $request_args, $response );
66
-
67
- return $response;
68
- }
69
-
70
-
71
- /**
72
- * Performs a Graph API request to the given URL.
73
- *
74
- * Throws an exception if a WP_Error is returned or we receive a 401 Not Authorized response status.
75
- *
76
- * @since 1.10.2
77
- *
78
- * @param string $url
79
- * @throws Framework\SV_WC_API_Exception
80
- * @return array
81
- */
82
- public function perform_request( $url ) {
83
-
84
- $request_args = array(
85
- 'headers' => array(
86
- 'Authorization' => 'Bearer ' . $this->api_key,
87
- ),
88
- 'timeout' => self::CURL_TIMEOUT,
89
- );
90
-
91
- $response = wp_remote_get( $url, $request_args );
92
-
93
- $this->log_request( $url, $request_args, $response );
94
-
95
- if ( is_wp_error( $response ) ) {
96
-
97
- throw new Framework\SV_WC_API_Exception( $response->get_error_message(), $response->get_error_code() );
98
-
99
- } elseif ( 401 === (int) wp_remote_retrieve_response_code( $response ) ) {
100
-
101
- $response_body = json_decode( wp_remote_retrieve_body( $response ) );
102
-
103
- if ( isset( $response_body->error->code, $response_body->error->message ) ) {
104
- throw new Framework\SV_WC_API_Exception( $response_body->error->message, $response_body->error->code );
105
- } else {
106
- throw new Framework\SV_WC_API_Exception( sprintf( __( 'HTTP %1$s: %2$s', 'facebook-for-woocommerce' ), wp_remote_retrieve_response_code( $response ), wp_remote_retrieve_response_message( $response ) ) );
107
- }
108
- }
109
-
110
- return $response;
111
- }
112
-
113
-
114
- public function _post( $url, $data, $api_key = '' ) {
115
- if ( class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
116
- return self::_post_async( $url, $data );
117
- } else {
118
- return self::_post_sync( $url, $data );
119
- }
120
- }
121
-
122
- public function _post_sync( $url, $data, $api_key = '' ) {
123
- $api_key = $api_key ?: $this->api_key;
124
-
125
- $request_args = array(
126
- 'body' => $data,
127
- 'headers' => array(
128
- 'Authorization' => 'Bearer ' . $api_key,
129
- ),
130
- 'timeout' => self::CURL_TIMEOUT,
131
- );
132
-
133
- $response = wp_remote_post( $url, $request_args );
134
-
135
- $this->log_request( $url, $request_args, $response, 'POST' );
136
-
137
- return $response;
138
- }
139
-
140
-
141
- /**
142
- * Issues an asynchronous POST request to the Graph API.
143
- *
144
- * @param string $url request URL
145
- * @param array $data request data
146
- * @param string $api_key Graph API key
147
- * @return array|\WP_Error
148
- */
149
- public function _post_async( $url, $data, $api_key = '' ) {
150
-
151
- if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
152
- return;
153
- }
154
-
155
- $api_key = $api_key ?: $this->api_key;
156
-
157
- $request_args = array(
158
- 'body' => $data,
159
- 'headers' => array(
160
- 'Authorization' => 'Bearer ' . $api_key,
161
- ),
162
- 'timeout' => self::CURL_TIMEOUT,
163
- );
164
-
165
- $fbasync = new WC_Facebookcommerce_Async_Request();
166
- $fbasync->query_url = $url;
167
- $fbasync->query_args = array();
168
- $fbasync->post_args = $request_args;
169
-
170
- $response = $fbasync->dispatch();
171
-
172
- $this->log_request( $url, $request_args, $response, 'POST' );
173
-
174
- return $response;
175
- }
176
-
177
-
178
- /**
179
- * Issues a DELETE request to the Graph API.
180
- *
181
- * @param string $url request URL
182
- * @param string $api_key Graph API key
183
- * @return array|\WP_Error
184
- */
185
- public function _delete( $url, $api_key = '' ) {
186
-
187
- $api_key = $api_key ?: $this->api_key;
188
-
189
- $request_args = array(
190
- 'headers' => array(
191
- 'Authorization' => 'Bearer ' . $api_key,
192
- ),
193
- 'timeout' => self::CURL_TIMEOUT,
194
- 'method' => 'DELETE',
195
- );
196
-
197
- $response = wp_remote_request( $url, $request_args );
198
-
199
- $this->log_request( $url, $request_args, $response, 'DELETE' );
200
-
201
- return $response;
202
- }
203
-
204
-
205
- /**
206
- * Logs the request and response data.
207
- *
208
- * @since 1.10.2
209
- *
210
- * @param $url
211
- * @param $request_args
212
- * @param array|\WP_Error $response WordPress response object
213
- * @param string $method
214
- */
215
- private function log_request( $url, $request_args, $response, $method = '' ) {
216
-
217
- // bail if this class is loaded incorrectly or logging is disabled
218
- if ( ! function_exists( 'facebook_for_woocommerce' ) || ! facebook_for_woocommerce()->get_integration()->is_debug_mode_enabled() ) {
219
- return;
220
- }
221
-
222
- // add the URI to the data
223
- $request_data = array_merge(
224
- array(
225
- 'uri' => $url,
226
- ),
227
- $request_args
228
- );
229
-
230
- // the request args may not include the method, so allow it to be set
231
- if ( $method ) {
232
- $request_data['method'] = $method;
233
- }
234
-
235
- // mask the page access token
236
- if ( ! empty( $request_data['headers']['Authorization'] ) ) {
237
-
238
- $auth_value = $request_data['headers']['Authorization'];
239
-
240
- $request_data['headers']['Authorization'] = str_replace( $auth_value, str_repeat( '*', strlen( $auth_value ) ), $auth_value );
241
- }
242
-
243
- // if there was a problem
244
- if ( is_wp_error( $response ) ) {
245
-
246
- $code = $response->get_error_code();
247
- $message = $response->get_error_message();
248
- $headers = array();
249
- $body = '';
250
-
251
- } else {
252
-
253
- $headers = wp_remote_retrieve_headers( $response );
254
-
255
- if ( is_object( $headers ) ) {
256
- $headers = $headers->getAll();
257
- } elseif ( ! is_array( $headers ) ) {
258
- $headers = array();
259
- }
260
-
261
- $code = wp_remote_retrieve_response_code( $response );
262
- $message = wp_remote_retrieve_response_message( $response );
263
- $body = wp_remote_retrieve_body( $response );
264
- }
265
-
266
- $response_data = array(
267
- 'code' => $code,
268
- 'message' => $message,
269
- 'headers' => $headers,
270
- 'body' => $body,
271
- );
272
-
273
- facebook_for_woocommerce()->log_api_request( $request_data, $response_data );
274
- }
275
-
276
-
277
- // GET https://graph.facebook.com/vX.X/{page-id}/?fields=name
278
- public function get_page_name( $page_id, $api_key = '' ) {
279
- $api_key = $api_key ?: $this->api_key;
280
- $url = $this->build_url( $page_id, '/?fields=name' );
281
- $response = self::_get( $url, $api_key );
282
-
283
- if ( is_wp_error( $response ) ) {
284
- WC_Facebookcommerce_Utils::log( $response->get_error_message() );
285
- return '';
286
- }
287
-
288
- if ( $response['response']['code'] != '200' ) {
289
- return '';
290
- }
291
-
292
- $response_body = json_decode( wp_remote_retrieve_body( $response ) );
293
-
294
- return isset( $response_body->name ) ? $response_body->name : '';
295
- }
296
-
297
-
298
- /**
299
- * Gets a Facebook Page URL.
300
- *
301
- * Endpoint: https://graph.facebook.com/vX.X/{page-id}/?fields=link
302
- *
303
- * @param string|int $page_id page identifier
304
- * @param string $api_key API key
305
- * @return string URL
306
- */
307
- public function get_page_url( $page_id, $api_key = '' ) {
308
-
309
- $api_key = $api_key ?: $this->api_key;
310
- $request = $this->build_url( $page_id, '/?fields=link' );
311
- $response = $this->_get( $request, $api_key );
312
- $page_url = '';
313
-
314
- if ( is_wp_error( $response ) ) {
315
-
316
- \WC_Facebookcommerce_Utils::log( $response->get_error_message() );
317
-
318
- } elseif ( 200 === (int) $response['response']['code'] ) {
319
-
320
- $response_body = wp_remote_retrieve_body( $response );
321
- $page_url = json_decode( $response_body )->link;
322
- }
323
-
324
- return $page_url;
325
- }
326
-
327
-
328
- /**
329
- * Determines whether the product catalog ID is valid.
330
- *
331
- * Returns true if the product catalog ID can be successfully retrieved using the Graph API.
332
- *
333
- * TODO: deprecate this methid in 1.11.0 or newer {WV 2020-03-12}
334
- *
335
- * @param int $product_catalog_id the ID of the product catalog
336
- * @return bool
337
- */
338
- public function validate_product_catalog( $product_catalog_id ) {
339
-
340
- try {
341
- $is_valid = $this->is_product_catalog_valid( $product_catalog_id );
342
- } catch ( Framework\SV_WC_API_Exception $e ) {
343
- $is_valid = false;
344
- }
345
-
346
- return $is_valid;
347
- }
348
-
349
-
350
- /**
351
- * Determines whether the product catalog ID is valid.
352
- *
353
- * Returns true if the product catalog ID can be successfully retrieved using the Graph API.
354
- *
355
- * @since 1.10.2
356
- *
357
- * @param int $product_catalog_id the ID of the product catalog
358
- * @return boolean
359
- * @throws Framework\SV_WC_API_Exception
360
- */
361
- public function is_product_catalog_valid( $product_catalog_id ) {
362
-
363
- $response = $this->perform_request( $this->build_url( $product_catalog_id ) );
364
-
365
- return 200 === (int) wp_remote_retrieve_response_code( $response );
366
- }
367
-
368
-
369
- // POST https://graph.facebook.com/vX.X/{product-catalog-id}/product_groups
370
- public function create_product_group( $product_catalog_id, $data ) {
371
- $url = $this->build_url( $product_catalog_id, '/product_groups' );
372
- return self::_post( $url, $data );
373
- }
374
-
375
- // POST https://graph.facebook.com/vX.X/{product-group-id}/products
376
- public function create_product_item( $product_group_id, $data ) {
377
- $url = $this->build_url( $product_group_id, '/products' );
378
- return self::_post( $url, $data );
379
- }
380
-
381
- public function update_product_group( $product_catalog_id, $data ) {
382
- $url = $this->build_url( $product_catalog_id );
383
- return self::_post( $url, $data );
384
- }
385
-
386
- public function update_product_item( $product_id, $data ) {
387
- $url = $this->build_url( $product_id );
388
- return self::_post( $url, $data );
389
- }
390
-
391
- public function delete_product_item( $product_item_id ) {
392
- $product_item_url = $this->build_url( $product_item_id );
393
- return self::_delete( $product_item_url );
394
- }
395
-
396
- public function delete_product_group( $product_group_id ) {
397
- $product_group_url = $this->build_url( $product_group_id, '?deletion_method=delete_items' );
398
- return self::_delete( $product_group_url );
399
- }
400
-
401
- // POST https://graph.facebook.com/vX.X/{product-catalog-id}/product_sets
402
- public function create_product_set_item( $product_catalog_id, $data ) {
403
- $url = $this->build_url( $product_catalog_id, '/product_sets' );
404
- return self::_post( $url, $data );
405
- }
406
-
407
- // POST https://graph.facebook.com/vX.X/{product-set-id}
408
- public function update_product_set_item( $product_set_id, $data ) {
409
- $url = $this->build_url( $product_set_id, '' );
410
- return self::_post( $url, $data );
411
- }
412
-
413
- public function delete_product_set_item( $product_set_id ) {
414
-
415
- $params = ( true === apply_filters( 'wc_facebook_commerce_allow_live_product_set_deletion', true, $product_set_id ) ) ? '?allow_live_product_set_deletion=true' : '';
416
-
417
- $url = $this->build_url( $product_set_id, $params );
418
-
419
- return self::_delete( $url );
420
- }
421
-
422
-
423
- public function log( $ems_id, $message, $error ) {
424
- $log_url = $this->build_url( $ems_id, '/log_events' );
425
-
426
- $data = array(
427
- 'message' => $message,
428
- 'error' => $error,
429
- );
430
-
431
- self::_post( $log_url, $data );
432
- }
433
-
434
- public function log_tip_event( $tip_id, $channel_id, $event ) {
435
- $tip_event_log_url = $this->build_url( '', '/log_tip_events' );
436
-
437
- $data = array(
438
- 'tip_id' => $tip_id,
439
- 'channel_id' => $channel_id,
440
- 'event' => $event,
441
- );
442
-
443
- self::_post( $tip_event_log_url, $data );
444
- }
445
-
446
- public function create_upload( $facebook_feed_id, $path_to_feed_file ) {
447
- $url = $this->build_url(
448
- $facebook_feed_id,
449
- '/uploads?access_token=' . $this->api_key
450
- );
451
-
452
- $data = array(
453
- 'file' => new CurlFile( $path_to_feed_file, 'text/csv' ),
454
- 'update_only' => true,
455
- );
456
-
457
- $curl = curl_init();
458
- curl_setopt_array(
459
- $curl,
460
- array(
461
- CURLOPT_URL => $url,
462
- CURLOPT_POST => 1,
463
- CURLOPT_POSTFIELDS => $data,
464
- CURLOPT_RETURNTRANSFER => 1,
465
- )
466
- );
467
- $response = curl_exec( $curl );
468
- if ( curl_errno( $curl ) ) {
469
- WC_Facebookcommerce_Utils::fblog( $response );
470
- return null;
471
- }
472
- return WC_Facebookcommerce_Utils::decode_json( $response, true );
473
- }
474
-
475
- public function create_feed( $facebook_catalog_id, $data ) {
476
- $url = $this->build_url( $facebook_catalog_id, '/product_feeds' );
477
- $url = $this->get_feed_endpoint_url( $facebook_catalog_id );
478
- // success API call will return {id: <product feed id>}
479
- // failure API will return {error: <error message>}
480
- return self::_post( $url, $data );
481
- }
482
-
483
- /**
484
- * Get all feed configurations for a given catalog id.
485
- *
486
- * @see https://developers.facebook.com/docs/marketing-api/reference/product-feed/
487
- * @since 2.6.0
488
- *
489
- * @param String $facebook_catalog_id Facebook Catalog Id.
490
- * @return Array Facebook feeds configurations.
491
- */
492
- public function read_feeds( $facebook_catalog_id ) {
493
- $url = $this->get_feed_endpoint_url( $facebook_catalog_id );
494
- return $this->_get( $url );
495
- }
496
-
497
- /**
498
- * Get general info about a feed (data source) configured in Facebook Business.
499
- *
500
- * @see https://developers.facebook.com/docs/marketing-api/reference/product-feed/
501
- * @since 2.6.0
502
- *
503
- * @param String $feed_id Feed Id.
504
- * @return Array Facebook feeds configurations.
505
- */
506
- public function read_feed_information( $feed_id ) {
507
- $url = $this->build_url( $feed_id, '/?fields=id,name,schedule,update_schedule,uploads' );
508
- return $this->_get( $url );
509
- }
510
-
511
- /**
512
- * Get metadata about a feed (data source) configured in Facebook Business.
513
- *
514
- * @see https://developers.facebook.com/docs/marketing-api/reference/product-feed/
515
- * @since 2.6.0
516
- *
517
- * @param String $feed_id Facebook Catalog Id.
518
- * @return Array Facebook feed metadata.
519
- */
520
- public function read_feed_metadata( $feed_id ) {
521
- $url = $this->build_url( $feed_id, '/?fields=created_time,latest_upload,product_count,schedule,update_schedule' );
522
- return $this->_get( $url );
523
- }
524
-
525
- /**
526
- * Get metadata about a recent feed upload.
527
- *
528
- * @see https://developers.facebook.com/docs/marketing-api/reference/product-feed-upload/
529
- * @since 2.6.0
530
- *
531
- * @param String $upload_id Feed Upload Id.
532
- * @return Array Feed upload metadata.
533
- */
534
- public function read_upload_metadata( $upload_id ) {
535
- $url = $this->build_url( $upload_id, '/?fields=error_count,warning_count,num_detected_items,num_persisted_items,url' );
536
- return $this->_get( $url );
537
- }
538
-
539
- /**
540
- * Create product_feeds graph edge url.
541
- *
542
- * @since 2.6.0
543
- *
544
- * @param String $facebook_catalog_id Facebook Catalog Id.
545
- * @return String Graph edge url.
546
- */
547
- public function get_feed_endpoint_url( $facebook_catalog_id ) {
548
- return $this->build_url( $facebook_catalog_id, '/product_feeds' );
549
- }
550
-
551
- public function get_upload_status( $facebook_upload_id ) {
552
- $url = $this->build_url( $facebook_upload_id, '/?fields=end_time' );
553
- // success API call will return
554
- // {id: <upload id>, end_time: <time when upload completes>}
555
- // failure API will return {error: <error message>}
556
- return self::_get( $url );
557
- }
558
-
559
- // success API call will return a JSON of tip info
560
- public function get_tip_info( $external_merchant_settings_id ) {
561
- $url = $this->build_url( $external_merchant_settings_id, '/?fields=connect_woo' );
562
- $response = self::_get( $url, $this->api_key );
563
- $data = array(
564
- 'response' => $response,
565
- );
566
- if ( is_wp_error( $response ) ) {
567
- $data['error_type'] = 'is_wp_error';
568
- WC_Facebookcommerce_Utils::fblog(
569
- 'Failed to get AYMT tip info via API.',
570
- $data,
571
- true
572
- );
573
- return;
574
- }
575
- if ( $response['response']['code'] != '200' ) {
576
- $data['error_type'] = 'Non-200 error code from FB';
577
- WC_Facebookcommerce_Utils::fblog(
578
- 'Failed to get AYMT tip info via API.',
579
- $data,
580
- true
581
- );
582
- return;
583
- }
584
-
585
- $response_body = wp_remote_retrieve_body( $response );
586
- $connect_woo =
587
- WC_Facebookcommerce_Utils::decode_json( $response_body )->connect_woo;
588
- if ( ! isset( $connect_woo ) ) {
589
- $data['error_type'] = 'Response body not set';
590
- WC_Facebookcommerce_Utils::fblog(
591
- 'Failed to get AYMT tip info via API.',
592
- $data,
593
- true
594
- );
595
- }
596
- return $connect_woo;
597
- }
598
-
599
- public function get_facebook_id( $facebook_catalog_id, $product_id ) {
600
- $param = 'catalog:' . (string) $facebook_catalog_id . ':' .
601
- base64_encode( $product_id ) . '/?fields=id,product_group{id}';
602
- $url = $this->build_url( '', $param );
603
- // success API call will return
604
- // {id: <fb product id>, product_group{id} <fb product group id>}
605
- // failure API will return {error: <error message>}
606
- return self::_get( $url );
607
- }
608
-
609
- public function check_product_info( $facebook_catalog_id, $product_id, $pr_v ) {
610
- $param = 'catalog:' . (string) $facebook_catalog_id . ':' .
611
- base64_encode( $product_id ) . '/?fields=id,name,description,price,' .
612
- 'sale_price,sale_price_start_date,sale_price_end_date,image_url,' .
613
- 'visibility';
614
- if ( $pr_v ) {
615
- $param = $param . ',additional_variant_attributes{value}';
616
- }
617
- $url = $this->build_url( '', $param );
618
- // success API call will return
619
- // {id: <fb product id>, name,description,price,sale_price,sale_price_start_date
620
- // sale_price_end_date
621
- // failure API will return {error: <error message>}
622
- return self::_get( $url );
623
- }
624
-
625
-
626
- /**
627
- * Gets the connected asset IDs.
628
- *
629
- * These will be things like pixel & page ID.
630
- *
631
- * @since 2.0.0
632
- *
633
- * @param string $external_business_id the connected external business ID
634
- * @return array
635
- * @throws Framework\SV_WC_API_Exception
636
- */
637
- public function get_asset_ids( $external_business_id ) {
638
-
639
- $url = $this->build_url( 'fbe_business/fbe_installs?fbe_external_business_id=', $external_business_id );
640
-
641
- $response = $this->perform_request( $url );
642
-
643
- $data = wp_remote_retrieve_body( $response );
644
- $data = json_decode( $data, true );
645
-
646
- if ( ! is_array( $data ) || empty( $data['data'][0] ) ) {
647
- throw new Framework\SV_WC_API_Exception( 'Data is missing' );
648
- }
649
-
650
- $ids = $data['data'][0];
651
-
652
- // normalize the page ID to match the others
653
- if ( ! empty( $ids['profiles'] ) && is_array( $ids['profiles'] ) ) {
654
- $ids['page_id'] = current( $ids['profiles'] );
655
- }
656
-
657
- return $ids;
658
- }
659
-
660
-
661
- public function set_default_variant( $product_group_id, $data ) {
662
- $url = $this->build_url( $product_group_id );
663
- return self::_post( $url, $data );
664
- }
665
-
666
- private function build_url( $field_id, $param = '', $api_version = '' ) {
667
- $api_url = self::GRAPH_API_URL;
668
- if ( ! empty( $api_version ) ) {
669
- $api_url = $api_url . $api_version . '/';
670
- } else {
671
- $api_url = $api_url . self::API_VERSION . '/';
672
- }
673
- return $api_url . (string) $field_id . $param;
674
- }
675
- }
676
-
677
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/fbinfobanner.php CHANGED
@@ -9,264 +9,225 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
17
- include_once 'includes/fbutils.php';
18
- }
19
 
20
- if ( ! class_exists( 'WC_Facebookcommerce_Info_Banner' ) ) :
21
 
22
- /**
23
- * FB Info Banner class
24
- */
25
- class WC_Facebookcommerce_Info_Banner {
26
 
27
- const FB_NO_TIP_EXISTS = 'No Tip Exist!';
28
- const DEFAULT_TIP_IMG_URL_PREFIX = 'https://www.facebook.com';
29
- const CHANNEL_ID = 2087541767986590;
30
 
31
- /** @var object Class Instance */
32
- private static $instance;
33
 
34
- /** @var string If the banner has been dismissed */
35
- private $external_merchant_settings_id;
36
- private $fbgraph;
37
- private $should_query_tip;
38
 
39
- /**
40
- * Get the class instance
41
- */
42
- public static function get_instance(
43
- $external_merchant_settings_id,
44
- $fbgraph,
45
- $should_query_tip = false ) {
46
- return null === self::$instance
47
- ? ( self::$instance = new self(
48
- $external_merchant_settings_id,
49
- $fbgraph,
50
- $should_query_tip
51
- ) )
52
- : self::$instance;
53
- }
54
 
55
- /**
56
- * Constructor
57
- */
58
- public function __construct(
59
- $external_merchant_settings_id,
60
- $fbgraph,
61
- $should_query_tip = false ) {
62
- $this->should_query_tip = $should_query_tip;
63
- $this->external_merchant_settings_id = $external_merchant_settings_id;
64
- $this->fbgraph = $fbgraph;
65
- add_action( 'wp_ajax_ajax_woo_infobanner_post_click', array( $this, 'ajax_woo_infobanner_post_click' ) );
66
- add_action( 'wp_ajax_ajax_woo_infobanner_post_xout', array( $this, 'ajax_woo_infobanner_post_xout' ) );
67
- add_action( 'admin_notices', array( $this, 'banner' ) );
68
- add_action( 'admin_init', array( $this, 'dismiss_banner' ) );
69
- }
70
 
71
- /**
72
- * Post click event when hit primary button.
73
- */
74
- function ajax_woo_infobanner_post_click() {
75
- WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
76
- 'post tip click event',
 
 
 
 
 
 
 
 
 
 
 
77
  true
78
  );
79
- check_ajax_referer( 'wc_facebook_infobanner_jsx' );
80
- $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
81
- $tip_id = isset( $tip_info->tip_id )
82
- ? $tip_info->tip_id
83
- : null;
84
- if ( $tip_id == null ) {
85
- WC_Facebookcommerce_Utils::fblog(
86
- 'Do not have tip id when click, sth went wrong',
87
- array( 'tip_info' => $tip_info ),
88
- true
89
- );
90
- } else {
91
- WC_Facebookcommerce_Utils::tip_events_log(
92
- $tip_id,
93
- self::CHANNEL_ID,
94
- 'click'
95
- );
96
- }
97
  }
 
98
 
99
- /**
100
- * Post xout event when hit dismiss button.
101
- */
102
- function ajax_woo_infobanner_post_xout() {
103
- WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
104
- 'post tip xout event',
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  true
106
  );
107
- check_ajax_referer( 'wc_facebook_infobanner_jsx' );
108
- $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
109
- $tip_id = isset( $tip_info->tip_id )
110
- ? $tip_info->tip_id
111
- : null;
112
- // Delete cached tip if xout.
113
- update_option( 'fb_info_banner_last_best_tip', '' );
114
- if ( $tip_id == null ) {
115
- WC_Facebookcommerce_Utils::fblog(
116
- 'Do not have tip id when xout, sth went wrong',
117
- array( 'tip_info' => $tip_info ),
118
- true
119
- );
120
- } else {
121
- WC_Facebookcommerce_Utils::tip_events_log(
122
- $tip_id,
123
- self::CHANNEL_ID,
124
- 'xout'
125
- );
126
- }
127
  }
 
128
 
129
- /**
130
- * Display a info banner on Woocommerce pages.
131
- */
132
- public function banner() {
133
- $screen = get_current_screen();
134
- if ( ! in_array(
135
- $screen->base,
136
- array(
137
- 'woocommerce_page_wc-reports',
138
- 'woocommerce_page_wc-settings',
139
- 'woocommerce_page_wc-status',
140
- )
141
- ) ||
142
- $screen->is_network || $screen->action ) {
143
- return;
144
- }
145
-
146
- $tip_info = null;
147
- if ( ! $this->should_query_tip ) {
148
- // If last query is less than 1 day, either has last best tip or default
149
- // tip pass time cap.
150
- $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
151
- } else {
152
- $tip_info = $this->fbgraph->get_tip_info(
153
- $this->external_merchant_settings_id
154
- );
155
- update_option( 'fb_info_banner_last_query_time', current_time( 'mysql' ) );
156
- }
157
-
158
- // Not render if no cached best tip, or no best tip returned from FB.
159
- if ( ! $tip_info || ( $tip_info === self::FB_NO_TIP_EXISTS ) ) {
160
- // Delete cached tip if should query and get no tip.
161
- delete_option( 'fb_info_banner_last_best_tip' );
162
- return;
163
- } else {
164
- // Get tip creatives via API
165
- if ( is_string( $tip_info ) ) {
166
- $tip_info = WC_Facebookcommerce_Utils::decode_json( $tip_info );
167
- }
168
- $tip_title = isset( $tip_info->tip_title->__html )
169
- ? $tip_info->tip_title->__html
170
- : null;
171
-
172
- $tip_body = isset( $tip_info->tip_body->__html )
173
- ? $tip_info->tip_body->__html
174
- : null;
175
-
176
- $tip_action_link = isset( $tip_info->tip_action_link )
177
- ? $tip_info->tip_action_link
178
- : null;
179
 
180
- $tip_action = isset( $tip_info->tip_action->__html )
181
- ? $tip_info->tip_action->__html
182
- : null;
 
 
 
 
 
 
183
 
184
- $tip_img_url = isset( $tip_info->tip_img_url )
 
 
 
 
 
 
 
 
 
 
 
185
  ? self::DEFAULT_TIP_IMG_URL_PREFIX . $tip_info->tip_img_url
186
  : null;
187
 
188
- if ( $tip_title == null || $tip_body == null || $tip_action_link == null
189
- || $tip_action == null || $tip_action == null ) {
190
- WC_Facebookcommerce_Utils::fblog(
191
- 'Unexpected response from FB for tip info.',
192
- array( 'tip_info' => $tip_info ),
193
- true
194
- );
195
- return;
196
- }
197
- update_option(
198
- 'fb_info_banner_last_best_tip',
199
- is_object( $tip_info ) || is_array( $tip_info )
200
- ? json_encode( $tip_info ) : $tip_info
201
  );
 
202
  }
203
-
204
- $dismiss_url = $this->dismiss_url();
205
-
206
- echo '<div class="updated fade">';
207
- echo '<div id="fbinfobanner">';
208
- echo '<div><img src="' . esc_url( $tip_img_url ) . '" class="iconDetails"></div>';
209
- echo '<p class = "tipTitle"><strong>' . esc_html( $tip_title ) . "</strong></p>\n";
210
- echo '<p class = "tipContent">' . esc_html( $tip_body ) . '</p>';
211
- echo '<p class = "tipButton">';
212
- echo '<a href="' . esc_url( $tip_action_link ) . '" class = "btn" onclick="fb_woo_infobanner_post_click(); return true;" title="' . esc_attr__( 'Click and redirect.', 'facebook-for-woocommerce' ) . '"> ' . esc_html( $tip_action ) . '</a>';
213
- echo '<a href="' . esc_url( $dismiss_url ) . '" class = "btn dismiss grey" onclick="fb_woo_infobanner_post_xout(); return true;" title="' . esc_attr__( 'Dismiss this notice.', 'facebook-for-woocommerce' ) . '"> ' . esc_html__( 'Dismiss', 'facebook-for-woocommerce' ) . '</a>';
214
- echo '</p></div></div>';
215
- }
216
-
217
- /**
218
- * Returns the url that the user clicks to remove the info banner
219
- *
220
- * @return (string)
221
- */
222
- private function dismiss_url() {
223
- $url = admin_url( 'admin.php' );
224
-
225
- $url = add_query_arg(
226
- array(
227
- 'page' => 'wc-settings',
228
- 'tab' => 'integration',
229
- 'wc-notice' => 'dismiss-fb-info-banner',
230
- ),
231
- $url
232
  );
233
-
234
- return wp_nonce_url( $url, 'woocommerce_info_banner_dismiss' );
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
- /**
239
- * Handles the action that dismisses the info banner.
240
- *
241
- * The banner will remain dismissed for at least one day and until a new info tip can be retrieved.
242
- *
243
- * @see \WC_Facebookcommerce_Integration::FB_TIP_QUERY
244
- * @see \WC_Facebookcommerce_Graph_API::get_tip_info()
245
- */
246
- public function dismiss_banner() {
 
 
 
 
 
 
 
 
 
 
247
 
248
- if ( ! isset( $_GET['wc-notice'] ) ) {
249
- return;
250
- }
251
 
252
- if ( 'dismiss-fb-info-banner' !== $_GET['wc-notice'] ) {
253
- return;
254
- }
 
 
 
 
 
255
 
256
- if ( ! check_admin_referer( 'woocommerce_info_banner_dismiss' ) ) {
257
- return;
258
- }
259
 
260
- // Delete cached tip if xout.
261
- delete_option( 'fb_info_banner_last_best_tip' );
262
- if ( wp_get_referer() ) {
263
- wp_safe_redirect( wp_get_referer() );
264
- } else {
265
- wp_safe_redirect( admin_url( 'admin.php?page=wc-settings&tab=integration' ) );
266
- }
267
  }
268
 
 
 
 
269
 
 
 
 
 
 
 
 
270
  }
271
 
272
- endif;
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
 
 
 
 
13
 
14
+ require_once 'includes/fbutils.php';
15
 
16
+ /**
17
+ * FB Info Banner class
18
+ */
19
+ class WC_Facebookcommerce_Info_Banner {
20
 
21
+ const FB_NO_TIP_EXISTS = 'No Tip Exist!';
22
+ const DEFAULT_TIP_IMG_URL_PREFIX = 'https://www.facebook.com';
23
+ const CHANNEL_ID = 2087541767986590;
24
 
25
+ /** @var object Class Instance */
26
+ private static $instance;
27
 
28
+ /** @var string If the banner has been dismissed */
29
+ private $external_merchant_settings_id;
30
+ private $should_query_tip;
 
31
 
32
+ /**
33
+ * Get the class instance
34
+ */
35
+ public static function get_instance( $external_merchant_settings_id, $should_query_tip = false ) {
36
+ return null === self::$instance
37
+ ? ( self::$instance = new self( $external_merchant_settings_id, $should_query_tip ) )
38
+ : self::$instance;
39
+ }
 
 
 
 
 
 
 
40
 
41
+ /**
42
+ * Constructor
43
+ */
44
+ public function __construct( $external_merchant_settings_id, $should_query_tip = false ) {
45
+ $this->should_query_tip = $should_query_tip;
46
+ $this->external_merchant_settings_id = $external_merchant_settings_id;
47
+ add_action( 'wp_ajax_ajax_woo_infobanner_post_click', array( $this, 'ajax_woo_infobanner_post_click' ) );
48
+ add_action( 'wp_ajax_ajax_woo_infobanner_post_xout', array( $this, 'ajax_woo_infobanner_post_xout' ) );
49
+ add_action( 'admin_notices', array( $this, 'banner' ) );
50
+ add_action( 'admin_init', array( $this, 'dismiss_banner' ) );
51
+ }
 
 
 
 
52
 
53
+ /**
54
+ * Post click event when hit primary button.
55
+ */
56
+ function ajax_woo_infobanner_post_click() {
57
+ WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
58
+ 'post tip click event',
59
+ true
60
+ );
61
+ check_ajax_referer( 'wc_facebook_infobanner_jsx' );
62
+ $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
63
+ $tip_id = isset( $tip_info->tip_id )
64
+ ? $tip_info->tip_id
65
+ : null;
66
+ if ( $tip_id == null ) {
67
+ WC_Facebookcommerce_Utils::fblog(
68
+ 'Do not have tip id when click, sth went wrong',
69
+ array( 'tip_info' => $tip_info ),
70
  true
71
  );
72
+ } else {
73
+ WC_Facebookcommerce_Utils::tip_events_log(
74
+ $tip_id,
75
+ self::CHANNEL_ID,
76
+ 'click'
77
+ );
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
+ }
80
 
81
+ /**
82
+ * Post xout event when hit dismiss button.
83
+ */
84
+ function ajax_woo_infobanner_post_xout() {
85
+ WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
86
+ 'post tip xout event',
87
+ true
88
+ );
89
+ check_ajax_referer( 'wc_facebook_infobanner_jsx' );
90
+ $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
91
+ $tip_id = isset( $tip_info->tip_id )
92
+ ? $tip_info->tip_id
93
+ : null;
94
+ // Delete cached tip if xout.
95
+ update_option( 'fb_info_banner_last_best_tip', '' );
96
+ if ( $tip_id == null ) {
97
+ WC_Facebookcommerce_Utils::fblog(
98
+ 'Do not have tip id when xout, sth went wrong',
99
+ array( 'tip_info' => $tip_info ),
100
  true
101
  );
102
+ } else {
103
+ WC_Facebookcommerce_Utils::tip_events_log(
104
+ $tip_id,
105
+ self::CHANNEL_ID,
106
+ 'xout'
107
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
+ }
110
 
111
+ /**
112
+ * Display a info banner on Woocommerce pages.
113
+ */
114
+ public function banner() {
115
+ $screen = get_current_screen();
116
+ if ( ! in_array(
117
+ $screen->base,
118
+ array(
119
+ 'woocommerce_page_wc-reports',
120
+ 'woocommerce_page_wc-settings',
121
+ 'woocommerce_page_wc-status',
122
+ )
123
+ ) ||
124
+ $screen->is_network || $screen->action ) {
125
+ return;
126
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ $tip_info = null;
129
+ if ( ! $this->should_query_tip ) {
130
+ // If last query is less than 1 day, either has last best tip or default
131
+ // tip pass time cap.
132
+ $tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
133
+ } else {
134
+ $tip_info = facebook_for_woocommerce()->get_api()->get_tip_info( $this->external_merchant_settings_id );
135
+ update_option( 'fb_info_banner_last_query_time', current_time( 'mysql' ) );
136
+ }
137
 
138
+ // Not render if no cached best tip, or no best tip returned from FB.
139
+ if ( ! $tip_info || ( $tip_info === self::FB_NO_TIP_EXISTS ) ) {
140
+ // Delete cached tip if should query and get no tip.
141
+ delete_option( 'fb_info_banner_last_best_tip' );
142
+ return;
143
+ } else {
144
+ // Get tip creatives via API
145
+ $tip_title = $tip_info->get_tip_title_html() ?? null;
146
+ $tip_body = $tip_info->get_tip_body_html() ?? null;
147
+ $tip_action_link = $tip_info->tip_action_link ?? null;
148
+ $tip_action = $tip_info->get_tip_action_html() ?? null;
149
+ $tip_img_url = isset( $tip_info->tip_img_url )
150
  ? self::DEFAULT_TIP_IMG_URL_PREFIX . $tip_info->tip_img_url
151
  : null;
152
 
153
+ if ( $tip_title == null || $tip_body == null || $tip_action_link == null || $tip_action == null || $tip_action == null ) {
154
+ WC_Facebookcommerce_Utils::fblog(
155
+ 'Unexpected response from FB for tip info.',
156
+ array( 'tip_info' => $tip_info ),
157
+ true
 
 
 
 
 
 
 
 
158
  );
159
+ return;
160
  }
161
+ update_option(
162
+ 'fb_info_banner_last_best_tip',
163
+ is_object( $tip_info ) || is_array( $tip_info )
164
+ ? json_encode( $tip_info ) : $tip_info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  );
 
 
166
  }
167
 
168
+ $dismiss_url = $this->dismiss_url();
169
+
170
+ echo '<div class="updated fade">';
171
+ echo '<div id="fbinfobanner">';
172
+ echo '<div><img src="' . esc_url( $tip_img_url ) . '" class="iconDetails"></div>';
173
+ echo '<p class = "tipTitle"><strong>' . esc_html( $tip_title ) . "</strong></p>\n";
174
+ echo '<p class = "tipContent">' . esc_html( $tip_body ) . '</p>';
175
+ echo '<p class = "tipButton">';
176
+ echo '<a href="' . esc_url( $tip_action_link ) . '" class = "btn" onclick="fb_woo_infobanner_post_click(); return true;" title="' . esc_attr__( 'Click and redirect.', 'facebook-for-woocommerce' ) . '"> ' . esc_html( $tip_action ) . '</a>';
177
+ echo '<a href="' . esc_url( $dismiss_url ) . '" class = "btn dismiss grey" onclick="fb_woo_infobanner_post_xout(); return true;" title="' . esc_attr__( 'Dismiss this notice.', 'facebook-for-woocommerce' ) . '"> ' . esc_html__( 'Dismiss', 'facebook-for-woocommerce' ) . '</a>';
178
+ echo '</p></div></div>';
179
+ }
180
 
181
+ /**
182
+ * Returns the url that the user clicks to remove the info banner
183
+ *
184
+ * @return (string)
185
+ */
186
+ private function dismiss_url() {
187
+ $url = admin_url( 'admin.php' );
188
+
189
+ $url = add_query_arg(
190
+ array(
191
+ 'page' => 'wc-settings',
192
+ 'tab' => 'integration',
193
+ 'wc-notice' => 'dismiss-fb-info-banner',
194
+ ),
195
+ $url
196
+ );
197
+
198
+ return wp_nonce_url( $url, 'woocommerce_info_banner_dismiss' );
199
+ }
200
 
 
 
 
201
 
202
+ /**
203
+ * Handles the action that dismisses the info banner.
204
+ *
205
+ * The banner will remain dismissed for at least one day and until a new info tip can be retrieved.
206
+ *
207
+ * @see \WC_Facebookcommerce_Integration::FB_TIP_QUERY
208
+ */
209
+ public function dismiss_banner() {
210
 
211
+ if ( ! isset( $_GET['wc-notice'] ) ) {
212
+ return;
213
+ }
214
 
215
+ if ( 'dismiss-fb-info-banner' !== $_GET['wc-notice'] ) {
216
+ return;
 
 
 
 
 
217
  }
218
 
219
+ if ( ! check_admin_referer( 'woocommerce_info_banner_dismiss' ) ) {
220
+ return;
221
+ }
222
 
223
+ // Delete cached tip if xout.
224
+ delete_option( 'fb_info_banner_last_best_tip' );
225
+ if ( wp_get_referer() ) {
226
+ wp_safe_redirect( wp_get_referer() );
227
+ } else {
228
+ wp_safe_redirect( admin_url( 'admin.php?page=wc-settings&tab=integration' ) );
229
+ }
230
  }
231
 
232
+
233
+ }
includes/fbproduct.php CHANGED
@@ -9,631 +9,609 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- use SkyVerge\WooCommerce\Facebook\Products;
13
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
14
 
15
- if ( ! defined( 'ABSPATH' ) ) {
16
- exit;
17
- }
18
-
19
- if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
20
- include_once 'includes/fbutils.php';
21
- }
22
-
23
- if ( ! class_exists( 'WC_Facebook_Product' ) ) :
24
-
25
- /**
26
- * Custom FB Product proxy class
27
- */
28
- class WC_Facebook_Product {
29
- // Used for the background sync
30
- const PRODUCT_PREP_TYPE_ITEMS_BATCH = 'items_batch';
31
- // Used for the background feed upload
32
- const PRODUCT_PREP_TYPE_FEED = 'feed';
33
- // Used for direct update and create calls
34
- const PRODUCT_PREP_TYPE_NORMAL = 'normal';
35
-
36
- // Should match facebook-commerce.php while we migrate that code over
37
- // to this object.
38
- const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
39
- const FB_PRODUCT_PRICE = 'fb_product_price';
40
- const FB_PRODUCT_IMAGE = 'fb_product_image';
41
- const FB_VARIANT_IMAGE = 'fb_image';
42
- const FB_VISIBILITY = 'fb_visibility';
43
- const FB_REMOVE_FROM_SYNC = 'fb_remove_from_sync';
44
-
45
- const MIN_DATE_1 = '1970-01-29';
46
- const MIN_DATE_2 = '1970-01-30';
47
- const MAX_DATE = '2038-01-17';
48
- const MAX_TIME = 'T23:59+00:00';
49
- const MIN_TIME = 'T00:00+00:00';
50
-
51
- static $use_checkout_url = array(
52
- 'simple' => 1,
53
- 'variable' => 1,
54
- 'variation' => 1,
55
- );
56
 
57
- public function __construct( $wpid, $parent_product = null ) {
58
 
59
- if ( $wpid instanceof WC_Product ) {
60
- $this->id = $wpid->get_id();
61
- $this->woo_product = $wpid;
62
- } else {
63
- $this->id = $wpid;
64
- $this->woo_product = wc_get_product( $wpid );
65
- }
66
-
67
- $this->fb_description = '';
68
- $this->gallery_urls = null;
69
- $this->fb_use_parent_image = null;
70
- $this->main_description = '';
71
- $this->sync_short_description = \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT === facebook_for_woocommerce()->get_integration()->get_product_description_mode();
72
-
73
- if ( $meta = get_post_meta( $this->id, self::FB_VISIBILITY, true ) ) {
74
- $this->fb_visibility = wc_string_to_bool( $meta );
75
- } else {
76
- $this->fb_visibility = '';
77
- // for products that haven't synced yet
78
- }
79
-
80
- // Variable products should use some data from the parent_product
81
- // For performance reasons, that data shouldn't be regenerated every time.
82
- if ( $parent_product ) {
83
- $this->gallery_urls = $parent_product->get_gallery_urls();
84
- $this->fb_use_parent_image = $parent_product->get_use_parent_image();
85
- $this->main_description = $parent_product->get_fb_description();
86
- }
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
- public function exists() {
90
- return ( $this->woo_product !== null && $this->woo_product !== false );
 
 
 
 
 
 
 
 
 
91
  }
92
 
93
- // Fall back to calling method on $woo_product
94
- public function __call( $function, $args ) {
95
- if ( $this->woo_product ) {
96
- return call_user_func_array( array( $this->woo_product, $function ), $args );
97
- } else {
98
- $e = new Exception();
99
- $backtrace = var_export( $e->getTraceAsString(), true );
100
- WC_Facebookcommerce_Utils::fblog(
101
- "Calling $function on Null Woo Object. Trace:\n" . $backtrace,
102
- array(),
103
- true
104
- );
105
- return null;
106
- }
107
  }
 
108
 
109
- public function get_gallery_urls() {
110
- if ( $this->gallery_urls === null ) {
111
- if ( is_callable( array( $this, 'get_gallery_image_ids' ) ) ) {
112
- $image_ids = $this->get_gallery_image_ids();
113
- } else {
114
- $image_ids = $this->get_gallery_attachment_ids();
115
- }
116
- $gallery_urls = array();
117
- foreach ( $image_ids as $image_id ) {
118
- $image_url = wp_get_attachment_url( $image_id );
119
- if ( ! empty( $image_url ) ) {
120
- array_push(
121
- $gallery_urls,
122
- WC_Facebookcommerce_Utils::make_url( $image_url )
123
- );
124
- }
125
- }
126
- $this->gallery_urls = array_filter( $gallery_urls );
127
- }
128
 
129
- return $this->gallery_urls;
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
 
131
 
132
- public function get_post_data() {
133
- if ( is_callable( 'get_post' ) ) {
134
- return get_post( $this->id );
 
135
  } else {
136
- return $this->get_post_data();
 
 
 
 
 
 
 
 
 
 
137
  }
 
138
  }
139
 
140
- public function get_fb_price( $for_items_batch = false ) {
141
- $product_price = Products::get_product_price( $this->woo_product );
142
 
143
- return $for_items_batch ? self::format_price_for_fb_items_batch( $product_price ) : $product_price;
 
 
 
 
144
  }
 
145
 
146
- private static function format_price_for_fb_items_batch( $price ) {
147
- // items_batch endpoint requires a string and a currency code
148
- $formatted = ( $price / 100.0 ) . ' ' . get_woocommerce_currency();
149
- return $formatted;
150
- }
151
 
 
 
152
 
153
- /**
154
- * Determines whether the current product is a WooCommerce Bookings product.
155
- *
156
- * TODO: add an integration that filters the Facebook price instead {WV 2020-07-22}
157
- *
158
- * @since 2.0.0
159
- *
160
- * @return bool
161
- */
162
- private function is_bookable_product() {
163
 
164
- return facebook_for_woocommerce()->is_plugin_active( 'woocommerce-bookings.php' ) && class_exists( 'WC_Product_Booking' ) && is_callable( 'is_wc_booking_product' ) && is_wc_booking_product( $this );
165
- }
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
- /**
169
- * Gets a list of image URLs to use for this product in Facebook sync.
170
- *
171
- * @return array
172
- */
173
- public function get_all_image_urls() {
174
 
175
- $image_urls = array();
 
 
 
 
 
176
 
177
- $product_image_url = wp_get_attachment_url( $this->woo_product->get_image_id() );
178
- $parent_product_image_url = null;
179
- $custom_image_url = $this->woo_product->get_meta( self::FB_PRODUCT_IMAGE );
180
 
181
- if ( $this->woo_product->is_type( 'variation' ) ) {
 
 
182
 
183
- if ( $parent_product = wc_get_product( $this->woo_product->get_parent_id() ) ) {
184
 
185
- $parent_product_image_url = wp_get_attachment_url( $parent_product->get_image_id() );
186
- }
187
- }
188
 
189
- switch ( $this->woo_product->get_meta( Products::PRODUCT_IMAGE_SOURCE_META_KEY ) ) {
 
 
190
 
191
- case Products::PRODUCT_IMAGE_SOURCE_CUSTOM:
192
- $image_urls = array( $custom_image_url, $product_image_url, $parent_product_image_url );
193
- break;
194
 
195
- case Products::PRODUCT_IMAGE_SOURCE_PARENT_PRODUCT:
196
- $image_urls = array( $parent_product_image_url, $product_image_url );
197
- break;
198
 
199
- case Products::PRODUCT_IMAGE_SOURCE_PRODUCT:
200
- default:
201
- $image_urls = array( $product_image_url, $parent_product_image_url );
202
- break;
203
- }
204
 
205
- $image_urls = array_merge( $image_urls, $this->get_gallery_urls() );
206
- $image_urls = array_filter( array_unique( $image_urls ) );
 
 
 
207
 
208
- // Regenerate $image_url PHP array indexes after filtering.
209
- // The array_filter does not touches indexes so if something gets removed we may end up with gaps.
210
- // Later parts of the code expect something to exist under the 0 index.
211
- $image_urls = array_values( $image_urls );
212
 
213
- if ( empty( $image_urls ) ) {
214
- $image_urls[] = wc_placeholder_img_src();
215
- }
 
216
 
217
- return $image_urls;
 
218
  }
219
 
 
 
220
 
221
- /**
222
- * Gets the list of additional image URLs for the product from the complete list of image URLs.
223
- *
224
- * It assumes the first URL will be used as the product image.
225
- * It returns 20 or less image URLs because Facebook doesn't allow more items on the additional_image_urls field.
226
- *
227
- * @since 2.0.2
228
- *
229
- * @param array $image_urls all image URLs for the product.
230
- * @return array
231
- */
232
- private function get_additional_image_urls( $image_urls ) {
233
-
234
- return array_slice( $image_urls, 1, 20 );
235
- }
236
-
237
-
238
- // Returns the parent image id for variable products only.
239
- public function get_parent_image_id() {
240
- if ( WC_Facebookcommerce_Utils::is_variation_type( $this->woo_product->get_type() ) ) {
241
- $parent_data = $this->get_parent_data();
242
- return $parent_data['image_id'];
243
- }
244
- return null;
245
  }
 
 
246
 
247
- public function set_description( $description ) {
248
- $description = stripslashes(
249
- WC_Facebookcommerce_Utils::clean_string( $description )
250
- );
251
- $this->fb_description = $description;
 
 
 
 
 
 
 
 
 
 
 
252
  update_post_meta(
253
  $this->id,
254
- self::FB_PRODUCT_DESCRIPTION,
255
- $description
256
  );
257
  }
 
258
 
259
- public function set_product_image( $image ) {
260
- if ( $image !== null && strlen( $image ) !== 0 ) {
261
- $image = WC_Facebookcommerce_Utils::clean_string( $image );
262
- $image = WC_Facebookcommerce_Utils::make_url( $image );
263
- update_post_meta(
264
- $this->id,
265
- self::FB_PRODUCT_IMAGE,
266
- $image
267
- );
268
- }
 
 
269
  }
 
270
 
271
- public function set_price( $price ) {
272
- if ( is_numeric( $price ) ) {
273
- update_post_meta(
274
- $this->id,
275
- self::FB_PRODUCT_PRICE,
276
- $price
277
- );
278
- } else {
279
- delete_post_meta(
280
- $this->id,
281
- self::FB_PRODUCT_PRICE
282
- );
283
- }
284
  }
 
 
285
 
286
- public function get_use_parent_image() {
287
- if ( $this->fb_use_parent_image === null ) {
288
- $variant_image_setting =
289
- get_post_meta( $this->id, self::FB_VARIANT_IMAGE, true );
290
- $this->fb_use_parent_image = ( $variant_image_setting ) ? true : false;
291
- }
292
- return $this->fb_use_parent_image;
 
 
 
 
 
293
  }
294
 
295
- public function set_use_parent_image( $setting ) {
296
- $this->fb_use_parent_image = ( $setting == 'yes' );
297
- update_post_meta(
298
- $this->id,
299
- self::FB_VARIANT_IMAGE,
300
- $this->fb_use_parent_image
301
- );
 
302
  }
303
 
304
- public function get_fb_description() {
305
- if ( $this->fb_description ) {
306
- return $this->fb_description;
307
- }
308
 
309
- $description = get_post_meta(
310
- $this->id,
311
- self::FB_PRODUCT_DESCRIPTION,
312
- true
313
- );
314
 
315
  if ( $description ) {
316
  return $description;
317
  }
 
 
 
 
318
 
319
- if ( WC_Facebookcommerce_Utils::is_variation_type( $this->woo_product->get_type() ) ) {
320
-
321
- $description = WC_Facebookcommerce_Utils::clean_string( $this->woo_product->get_description() );
322
 
323
- if ( $description ) {
324
- return $description;
325
- }
326
- if ( $this->main_description ) {
327
- return $this->main_description;
328
- }
329
- }
 
 
330
 
331
- $post = $this->get_post_data();
 
 
 
 
 
 
 
 
 
332
 
333
- $post_content = WC_Facebookcommerce_Utils::clean_string(
334
- $post->post_content
335
- );
336
- $post_excerpt = WC_Facebookcommerce_Utils::clean_string(
337
- $post->post_excerpt
338
- );
339
- $post_title = WC_Facebookcommerce_Utils::clean_string(
340
- $post->post_title
341
- );
342
 
343
- // Sanitize description
344
- if ( $post_content ) {
345
- $description = $post_content;
346
- }
347
- if ( $this->sync_short_description || ( $description == '' && $post_excerpt ) ) {
348
- $description = $post_excerpt;
349
- }
350
- if ( $description == '' ) {
351
- $description = $post_title;
352
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
- return $description;
 
 
 
 
 
 
 
355
  }
356
 
357
- /**
358
- * @param array $product_data
359
- * @param bool $for_items_batch
360
- *
361
- * @return array
362
- */
363
- public function add_sale_price( $product_data, $for_items_batch = false ) {
364
-
365
- $sale_price = $this->woo_product->get_sale_price();
366
- $sale_price_effective_date = '';
367
-
368
- $sale_start =
369
- ( $date = $this->woo_product->get_date_on_sale_from() )
370
- ? date_i18n( WC_DateTime::ATOM, $date->getOffsetTimestamp() )
371
- : self::MIN_DATE_1 . self::MIN_TIME;
372
-
373
- $sale_end =
374
- ( $date = $this->woo_product->get_date_on_sale_to() )
375
- ? date_i18n( WC_DateTime::ATOM, $date->getOffsetTimestamp() )
376
- : self::MAX_DATE . self::MAX_TIME;
377
-
378
- // check if sale exist
379
- if ( is_numeric( $sale_price ) && $sale_price > 0 ) {
380
- $sale_price_effective_date = $sale_start . '/' . $sale_end;
381
- $sale_price =
382
- intval( round( $this->get_price_plus_tax( $sale_price ) * 100 ) );
383
- }
384
 
385
- // check if sale is expired and sale time range is valid
386
- if ( $for_items_batch ) {
387
- $product_data['sale_price_effective_date'] = $sale_price_effective_date;
388
- $product_data['sale_price'] = is_numeric( $sale_price ) ? self::format_price_for_fb_items_batch( $sale_price ) : "";
389
- } else {
390
- $product_data['sale_price_start_date'] = $sale_start;
391
- $product_data['sale_price_end_date'] = $sale_end;
392
- $product_data['sale_price'] = is_numeric( $sale_price ) ? $sale_price : 0;
393
- }
394
 
395
- return $product_data;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  }
 
397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
 
399
- /**
400
- * Determines whether a product should be excluded from all-products sync or the feed file.
401
- *
402
- * @see SkyVerge\WooCommerce\Facebook\Products\Sync::create_or_update_all_products()
403
- * @see WC_Facebook_Product_Feed::write_product_feed_file()
404
- *
405
- * @deprecated 2.0.2
406
- */
407
- public function is_hidden() {
408
-
409
- wc_deprecated_function( __METHOD__, '2.0.2', 'Products::product_should_be_synced()' );
410
 
411
- return $this->woo_product instanceof \WC_Product && ! Products::product_should_be_synced( $this->woo_product );
 
 
 
 
 
 
 
 
 
 
412
  }
 
413
 
 
 
 
 
 
 
 
414
 
415
- public function get_price_plus_tax( $price ) {
416
- $woo_product = $this->woo_product;
417
- // // wc_get_price_including_tax exist for Woo > 2.7
418
- if ( function_exists( 'wc_get_price_including_tax' ) ) {
419
- $args = array(
420
- 'qty' => 1,
421
- 'price' => $price,
422
- );
423
- return get_option( 'woocommerce_tax_display_shop' ) === 'incl'
424
- ? wc_get_price_including_tax( $woo_product, $args )
425
- : wc_get_price_excluding_tax( $woo_product, $args );
426
- } else {
427
- return get_option( 'woocommerce_tax_display_shop' ) === 'incl'
428
- ? $woo_product->get_price_including_tax( 1, $price )
429
- : $woo_product->get_price_excluding_tax( 1, $price );
430
  }
 
 
431
  }
432
-
433
- public function get_grouped_product_option_names( $key, $option_values ) {
434
- // Convert all slug_names in $option_values into the visible names that
435
- // advertisers have set to be the display names for a given attribute value
436
- $terms = get_the_terms( $this->id, $key );
437
- return ! is_array( $terms ) ? array() : array_map(
438
- function ( $slug_name ) use ( $terms ) {
439
- foreach ( $terms as $term ) {
440
- if ( $term->slug === $slug_name ) {
441
- return $term->name;
442
- }
443
- }
444
- return $slug_name;
445
- },
446
- $option_values
447
  );
 
 
448
  }
 
449
 
450
-
451
- public function update_visibility( $is_product_page, $visible_box_checked ) {
452
- $visibility = get_post_meta( $this->id, self::FB_VISIBILITY, true );
453
- if ( $visibility && ! $is_product_page ) {
454
- // If the product was previously set to visible, keep it as visible
455
- // (unless we're on the product page)
456
- $this->fb_visibility = $visibility;
457
- } else {
458
- // If the product is not visible OR we're on the product page,
459
- // then update the visibility as needed.
460
- $this->fb_visibility = $visible_box_checked ? true : false;
461
- update_post_meta( $this->id, self::FB_VISIBILITY, $this->fb_visibility );
462
  }
463
- }
464
 
465
- // wrapper function to find item_id for default variation
466
- function find_matching_product_variation() {
467
- if ( is_callable( array( $this, 'get_default_attributes' ) ) ) {
468
- $default_attributes = $this->get_default_attributes();
469
- } else {
470
- $default_attributes = $this->get_variation_default_attributes();
471
- }
472
 
473
- if ( ! $default_attributes ) {
474
- return;
475
- }
476
- foreach ( $default_attributes as $key => $value ) {
477
- if ( strncmp( $key, 'attribute_', strlen( 'attribute_' ) ) === 0 ) {
478
- continue;
479
- }
480
- unset( $default_attributes[ $key ] );
481
- $default_attributes[ sprintf( 'attribute_%s', $key ) ] = $value;
482
- }
483
- if ( class_exists( 'WC_Data_Store' ) ) {
484
- // for >= woo 3.0.0
485
- $data_store = WC_Data_Store::load( 'product' );
486
- return $data_store->find_matching_product_variation(
487
- $this,
488
- $default_attributes
489
  );
490
- } else {
491
- return $this->get_matching_variation( $default_attributes );
492
- }
493
- }
494
 
495
- private function build_checkout_url( $product_url ) {
496
- // Use product_url for external/bundle product setting.
497
- $product_type = $this->get_type();
498
- if ( ! $product_type || ! isset( self::$use_checkout_url[ $product_type ] ) ) {
499
- $checkout_url = $product_url;
500
- } elseif ( wc_get_cart_url() ) {
501
- $char = '?';
502
- // Some merchant cart pages are actually a querystring
503
- if ( strpos( wc_get_cart_url(), '?' ) !== false ) {
504
- $char = '&';
505
- }
506
-
507
- $checkout_url = WC_Facebookcommerce_Utils::make_url(
508
- wc_get_cart_url() . $char
509
  );
510
 
511
- if ( WC_Facebookcommerce_Utils::is_variation_type( $this->get_type() ) ) {
512
- $query_data = array(
513
- 'add-to-cart' => $this->get_parent_id(),
514
- 'variation_id' => $this->get_id(),
515
- );
516
-
517
- $query_data = array_merge(
518
- $query_data,
519
- $this->get_variation_attributes()
520
- );
521
-
522
- } else {
523
- $query_data = array(
524
- 'add-to-cart' => $this->get_id(),
525
- );
526
- }
527
-
528
- $checkout_url = $checkout_url . http_build_query( $query_data );
529
-
530
  } else {
531
- $checkout_url = null;
532
- }//end if
533
- }
534
-
535
- /**
536
- * Gets product data to send to Facebook.
537
- *
538
- * @param string $retailer_id the retailer ID of the product
539
- * @param string $type_to_prepare_for whether the data is going to be used in a feed upload, an items_batch update or a direct api call
540
- * @return array
541
- */
542
- public function prepare_product( $retailer_id = null, $type_to_prepare_for = self::PRODUCT_PREP_TYPE_NORMAL ) {
543
-
544
- if ( ! $retailer_id ) {
545
- $retailer_id =
546
- WC_Facebookcommerce_Utils::get_fb_retailer_id( $this );
547
  }
548
- $image_urls = $this->get_all_image_urls();
549
 
550
- // Replace WordPress sanitization's ampersand with a real ampersand.
551
- $product_url = str_replace(
552
- '&amp%3B',
553
- '&',
554
- html_entity_decode( $this->get_permalink() )
555
- );
556
 
557
- $id = $this->get_id();
558
- if ( WC_Facebookcommerce_Utils::is_variation_type( $this->get_type() ) ) {
559
- $id = $this->get_parent_id();
560
- }
561
- $categories =
562
- WC_Facebookcommerce_Utils::get_product_categories( $id );
563
 
564
- // Get brand attribute.
565
- $brand = get_post_meta( $id, Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . 'brand', true );
566
- $brand_taxonomy = get_the_term_list( $id, 'product_brand', '', ', ' );
 
 
 
 
 
567
 
568
- if ( $brand ) {
569
- $brand = WC_Facebookcommerce_Utils::clean_string( $brand );
570
- } elseif ( !is_wp_error( $brand_taxonomy ) && $brand_taxonomy ) {
571
- $brand = WC_Facebookcommerce_Utils::clean_string( $brand_taxonomy );
572
- } else {
573
- $brand = wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() );
574
- }
575
 
576
- if ( self::PRODUCT_PREP_TYPE_ITEMS_BATCH === $type_to_prepare_for ) {
577
- $product_data = array(
578
- 'title' => WC_Facebookcommerce_Utils::clean_string(
579
- $this->get_title()
580
- ),
581
- 'description' => $this->get_fb_description(),
582
- 'image_link' => $image_urls[0],
583
- 'additional_image_link' => $this->get_additional_image_urls( $image_urls ),
584
- 'link' => $product_url,
585
- 'brand' => Framework\SV_WC_Helper::str_truncate( $brand, 100 ),
586
- 'retailer_id' => $retailer_id,
587
- 'price' => $this->get_fb_price( true ),
588
- 'availability' => $this->is_in_stock() ? 'in stock' : 'out of stock',
589
- 'visibility' => Products::is_product_visible( $this->woo_product ) ? \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_VISIBLE : \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN,
590
- );
591
- $product_data = $this->add_sale_price( $product_data, true );
592
- } else {
593
- $product_data = array(
594
- 'name' => WC_Facebookcommerce_Utils::clean_string(
595
- $this->get_title()
596
- ),
597
- 'description' => $this->get_fb_description(),
598
- 'image_url' => $image_urls[0],
599
- 'additional_image_urls' => $this->get_additional_image_urls( $image_urls ),
600
- 'url' => $product_url,
601
- 'category' => $categories['categories'],
602
- 'brand' => Framework\SV_WC_Helper::str_truncate( $brand, 100 ),
603
- 'retailer_id' => $retailer_id,
604
- 'price' => $this->get_fb_price(),
605
- 'currency' => get_woocommerce_currency(),
606
- 'availability' => $this->is_in_stock() ? 'in stock' : 'out of stock',
607
- 'visibility' => Products::is_product_visible( $this->woo_product ) ? \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_VISIBLE : \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN,
608
- );
609
- $product_data = $this->add_sale_price( $product_data );
610
- }//end if
611
 
612
- $google_product_category = Products::get_google_product_category_id( $this->woo_product );
613
- // Currently only items batch and feed support enhanced catalog fields
614
- if ( self::PRODUCT_PREP_TYPE_NORMAL !== $type_to_prepare_for && $google_product_category ) {
615
- $product_data['google_product_category'] = $google_product_category;
616
- $product_data = $this->apply_enhanced_catalog_fields_from_attributes( $product_data, $google_product_category );
617
- }
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
- // add the Commerce values (only stock quantity for the moment)
620
- if ( Products::is_product_ready_for_commerce( $this->woo_product ) ) {
621
- $product_data['quantity_to_sell_on_facebook'] = (int) max( 0, $this->woo_product->get_stock_quantity() );
622
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
623
 
624
- // Only use checkout URLs if they exist.
625
- $checkout_url = $this->build_checkout_url( $product_url );
626
- if ( $checkout_url ) {
627
- $product_data['checkout_url'] = $checkout_url;
628
- }
629
 
630
- // IF using WPML, set the product to hidden unless it is in the
631
- // default language. WPML >= 3.2 Supported.
632
- if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
633
- if ( class_exists( 'WC_Facebook_WPML_Injector' ) && WC_Facebook_WPML_Injector::should_hide( $id ) ) {
634
- $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
635
- }
 
 
 
 
 
636
  }
 
637
 
638
  // Exclude variations that are "virtual" products from export to Facebook &&
639
  // No Visibility Option for Variations
@@ -643,308 +621,306 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
643
  $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
644
  }
645
 
646
- if ( self::PRODUCT_PREP_TYPE_FEED !== $type_to_prepare_for ) {
647
- $this->prepare_variants_for_item( $product_data );
648
- } elseif (
649
- WC_Facebookcommerce_Utils::is_all_caps( $product_data['description'] )
650
- ) {
651
- $product_data['description'] =
652
- mb_strtolower( $product_data['description'] );
653
- }
654
-
655
- /**
656
- * Filters the generated product data.
657
- *
658
- * @param int $id Woocommerce product id
659
- * @param array $product_data An array of product data
660
- */
661
- return apply_filters(
662
- 'facebook_for_woocommerce_integration_prepare_product',
663
- $product_data,
664
- $id
665
- );
666
  }
667
 
668
  /**
669
- * Adds enhanced catalog fields to product data array. Separated from
670
- * the main function to make it easier to develop and debug, potentially
671
- * worth refactoring into main prepare_product function when complete.
672
- *
673
- * @param array $product_data map
674
- * @return array
675
- */
676
- private function apply_enhanced_catalog_fields_from_attributes( $product_data, $google_category_id ) {
677
- $google_category_id = $product_data['google_product_category'];
678
- $category_handler = facebook_for_woocommerce()->get_facebook_category_handler();
679
- if ( empty( $google_category_id ) || ! $category_handler->is_category( $google_category_id ) ) {
680
- return $product_data;
681
- }
682
- $enhanced_data = array();
683
 
684
- $category_attrs = $category_handler->get_attributes_with_fallback_to_parent_category( $google_category_id );
685
- $all_attributes = $this->get_matched_attributes_for_product( $this->woo_product, $category_attrs );
 
 
 
 
 
 
 
 
 
 
 
 
 
686
 
687
- foreach ( $all_attributes as $attribute ) {
688
- $value = Products::get_enhanced_catalog_attribute( $attribute['key'], $this->woo_product );
689
- $convert_to_array = (
690
- isset( $attribute['can_have_multiple_values'] ) &&
691
- true === $attribute['can_have_multiple_values'] &&
692
- 'string' === $attribute['type']
693
- );
694
 
695
- if ( ! empty( $value ) &&
696
- $category_handler->is_valid_value_for_attribute( $google_category_id, $attribute['key'], $value )
697
- ) {
698
- if ( $convert_to_array ) {
699
- $value = array_map( 'trim', explode( ',', $value ) );
700
- }
701
- $enhanced_data[ $attribute['key'] ] = $value;
 
 
 
 
 
 
702
  }
 
703
  }
704
-
705
- return array_merge( $product_data, $enhanced_data );
706
  }
707
 
 
 
708
 
709
- /**
710
- * Filters list of attributes to only those available for a given product
711
- *
712
- * @param \WC_Product $product WooCommerce Product
713
- * @param array $all_attributes List of Enhanced Catalog attributes to match
714
- * @return array
715
- */
716
- public function get_matched_attributes_for_product( $product, $all_attributes ) {
717
- $matched_attributes = array();
718
- $sanitized_keys = array_map(
719
- function( $key ) {
720
- return \WC_Facebookcommerce_Utils::sanitize_variant_name( $key, false );
721
- },
722
- array_keys( $product->get_attributes() )
723
- );
724
 
725
- $matched_attributes = array_filter(
726
- $all_attributes,
727
- function( $attribute ) use ( $sanitized_keys ) {
728
- return in_array( $attribute['key'], $sanitized_keys );
729
- }
730
- );
 
 
 
 
 
 
 
 
 
731
 
732
- return $matched_attributes;
733
- }
 
 
 
 
734
 
 
 
735
 
736
- /**
737
- * Normalizes variant data for Facebook.
738
- *
739
- * @param array $product_data variation product data
740
- * @return array
741
- */
742
- public function prepare_variants_for_item( &$product_data ) {
743
 
744
- /** @var \WC_Product_Variation $product */
745
- $product = $this;
 
 
 
 
 
746
 
747
- if ( ! $product->is_type( 'variation' ) ) {
748
- return array();
749
- }
750
 
751
- $attributes = $product->get_variation_attributes();
 
 
752
 
753
- if ( ! $attributes ) {
754
- return array();
755
- }
756
 
757
- $variant_names = array_keys( $attributes );
758
- $variant_data = array();
 
759
 
760
- // Loop through variants (size, color, etc) if they exist
761
- // For each product field type, pull the single variant
762
- foreach ( $variant_names as $original_variant_name ) {
763
 
764
- // don't handle any attributes that are designated as Commerce attributes
765
- if ( in_array( str_replace( 'attribute_', '', strtolower( $original_variant_name ) ), Products::get_distinct_product_attributes( $this->woo_product ), true ) ) {
766
- continue;
767
- }
768
 
769
- // Retrieve label name for attribute
770
- $label = wc_attribute_label( $original_variant_name, $product );
 
 
771
 
772
- // Clean up variant name (e.g. pa_color should be color)
773
- $new_name = \WC_Facebookcommerce_Utils::sanitize_variant_name( $original_variant_name, false );
774
 
775
- // Sometimes WC returns an array, sometimes it's an assoc array, depending
776
- // on what type of taxonomy it's using. array_values will guarantee we
777
- // only get a flat array of values.
778
- if ( $options = \WC_Facebookcommerce_Utils::get_variant_option_name( $this->id, $label, $attributes[ $original_variant_name ] ) ) {
779
 
780
- if ( is_array( $options ) ) {
 
 
 
781
 
782
- $option_values = array_values( $options );
783
 
784
- } else {
785
 
786
- $option_values = array( $options );
787
 
788
- // If this attribute has value 'any', options will be empty strings
789
- // Redirect to product page to select variants.
790
- // Reset checkout url since checkout_url (build from query data will
791
- // be invalid in this case.
792
- if ( count( $option_values ) === 1 && empty( $option_values[0] ) ) {
793
- $option_values[0] = 'any';
794
- $product_data['checkout_url'] = $product_data['url'];
795
- }
 
796
  }
 
797
 
798
- if ( \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER === $new_name && ! isset( $product_data[ \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER ] ) ) {
799
 
800
- // If we can't validate the gender, this will be null.
801
- $product_data[ $new_name ] = \WC_Facebookcommerce_Utils::validateGender( $option_values[0] );
802
- }
803
 
804
- switch ( $new_name ) {
805
-
806
- case \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER:
807
- // If we can't validate the GENDER field, we'll fall through to the
808
- // default case and set the gender into custom data.
809
- if ( $product_data[ $new_name ] ) {
810
-
811
- $variant_data[] = array(
812
- 'product_field' => $new_name,
813
- 'label' => $label,
814
- 'options' => $option_values,
815
- );
816
- }
817
-
818
- break;
819
-
820
- default:
821
- // This is for any custom_data.
822
- if ( ! isset( $product_data['custom_data'] ) ) {
823
- $product_data['custom_data'] = array();
824
- }
825
- $new_name = wc_attribute_label( $new_name, $product );
826
- $product_data['custom_data'][ $new_name ] = urldecode( $option_values[0] );
827
- break;
828
- }//end switch
829
- } else {
830
 
831
- \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $original_variant_name );
832
- continue;
833
- }//end if
834
- }//end foreach
835
 
836
- return $variant_data;
837
- }
 
 
 
 
838
 
 
839
 
840
- /**
841
- * Normalizes variable product variations data for Facebook.
842
- *
843
- * @param bool $feed_data whether this is used for feed data
844
- * @return array
845
- */
846
- public function prepare_variants_for_group( $feed_data = false ) {
 
 
 
847
 
848
- /** @var \WC_Product_Variable $product */
849
- $product = $this;
850
- $final_variants = array();
 
851
 
852
- try {
 
853
 
854
- if ( ! $product->is_type( 'variable' ) ) {
855
- throw new \Exception( 'prepare_variants_for_group called on non-variable product' );
856
- }
857
 
858
- $variation_attributes = $product->get_variation_attributes();
 
 
 
 
 
 
859
 
860
- if ( ! $variation_attributes ) {
861
- return array();
862
- }
863
 
864
- foreach ( array_keys( $product->get_attributes() ) as $name ) {
865
 
866
- $label = wc_attribute_label( $name, $product );
 
 
867
 
868
- if ( taxonomy_is_product_attribute( $name ) ) {
869
- $key = $name;
870
- } else {
871
- // variation_attributes keys are labels for custom attrs for some reason
872
- $key = $label;
873
- }
874
 
875
- if ( ! $key ) {
876
- throw new \Exception( "Critical error: can't get attribute name or label!" );
877
- }
878
 
879
- if ( isset( $variation_attributes[ $key ] ) ) {
880
- // array of the options (e.g. small, medium, large)
881
- $option_values = $variation_attributes[ $key ];
882
- } else {
883
- // skip variations without valid attribute options
884
- \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $name );
885
- continue;
886
- }
 
 
887
 
888
- // If this is a variable product, check default attribute.
889
- // If it's being used, show it as the first option on Facebook.
890
- if ( $first_option = $product->get_variation_default_attribute( $key ) ) {
891
 
892
- $index = array_search( $first_option, $option_values, false );
 
 
 
 
 
 
 
893
 
894
- unset( $option_values[ $index ] );
 
 
895
 
896
- array_unshift( $option_values, $first_option );
897
- }
898
 
899
- if ( function_exists( 'taxonomy_is_product_attribute' ) && taxonomy_is_product_attribute( $name ) ) {
900
- $option_values = $this->get_grouped_product_option_names( $key, $option_values );
901
- }
902
 
903
- switch ( $name ) {
 
904
 
905
- case Products::get_product_color_attribute( $this->woo_product ):
906
- $name = WC_Facebookcommerce_Utils::FB_VARIANT_COLOR;
907
- break;
908
 
909
- case Products::get_product_size_attribute( $this->woo_product ):
910
- $name = WC_Facebookcommerce_Utils::FB_VARIANT_SIZE;
911
- break;
912
 
913
- case Products::get_product_pattern_attribute( $this->woo_product ):
914
- $name = WC_Facebookcommerce_Utils::FB_VARIANT_PATTERN;
915
- break;
916
 
917
- default:
918
- /**
919
- * For API approach, product_field need to start with 'custom_data:'
920
- *
921
- * @link https://developers.facebook.com/docs/marketing-api/reference/product-variant/
922
- */
923
- $name = \WC_Facebookcommerce_Utils::sanitize_variant_name( $name );
924
- }//end switch
925
 
926
- // for feed uploading, product field should remove prefix 'custom_data:'
927
- if ( $feed_data ) {
928
- $name = str_replace( 'custom_data:', '', $name );
929
- }
930
 
931
- $final_variants[] = array(
932
- 'product_field' => $name,
933
- 'label' => $label,
934
- 'options' => $option_values,
935
- );
936
- }//end foreach
937
- } catch ( \Exception $e ) {
 
938
 
939
- \WC_Facebookcommerce_Utils::fblog( $e->getMessage() );
 
 
 
940
 
941
- return array();
942
- }//end try
 
 
 
 
 
943
 
944
- return $final_variants;
945
- }
946
 
 
 
947
 
 
948
  }
949
 
950
- endif;
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ require_once __DIR__ . '/fbutils.php';
 
13
 
14
+ use WooCommerce\Facebook\Framework\Helper;
15
+ use WooCommerce\Facebook\Products;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ defined( 'ABSPATH' ) || exit;
18
 
19
+ /**
20
+ * Custom FB Product proxy class
21
+ */
22
+ class WC_Facebook_Product {
23
+ // Used for the background sync
24
+ const PRODUCT_PREP_TYPE_ITEMS_BATCH = 'items_batch';
25
+ // Used for the background feed upload
26
+ const PRODUCT_PREP_TYPE_FEED = 'feed';
27
+ // Used for direct update and create calls
28
+ const PRODUCT_PREP_TYPE_NORMAL = 'normal';
29
+
30
+ // Should match facebook-commerce.php while we migrate that code over
31
+ // to this object.
32
+ const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
33
+ const FB_PRODUCT_PRICE = 'fb_product_price';
34
+ const FB_PRODUCT_IMAGE = 'fb_product_image';
35
+ const FB_VARIANT_IMAGE = 'fb_image';
36
+ const FB_VISIBILITY = 'fb_visibility';
37
+ const FB_REMOVE_FROM_SYNC = 'fb_remove_from_sync';
38
+
39
+ const MIN_DATE_1 = '1970-01-29';
40
+ const MIN_DATE_2 = '1970-01-30';
41
+ const MAX_DATE = '2038-01-17';
42
+ const MAX_TIME = 'T23:59+00:00';
43
+ const MIN_TIME = 'T00:00+00:00';
44
+
45
+ static $use_checkout_url = array(
46
+ 'simple' => 1,
47
+ 'variable' => 1,
48
+ 'variation' => 1,
49
+ );
50
+
51
+ public function __construct( $wpid, $parent_product = null ) {
52
+
53
+ if ( $wpid instanceof WC_Product ) {
54
+ $this->id = $wpid->get_id();
55
+ $this->woo_product = $wpid;
56
+ } else {
57
+ $this->id = $wpid;
58
+ $this->woo_product = wc_get_product( $wpid );
59
  }
60
 
61
+ $this->fb_description = '';
62
+ $this->gallery_urls = null;
63
+ $this->fb_use_parent_image = null;
64
+ $this->main_description = '';
65
+ $this->sync_short_description = \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT === facebook_for_woocommerce()->get_integration()->get_product_description_mode();
66
+
67
+ if ( $meta = get_post_meta( $this->id, self::FB_VISIBILITY, true ) ) {
68
+ $this->fb_visibility = wc_string_to_bool( $meta );
69
+ } else {
70
+ $this->fb_visibility = '';
71
+ // for products that haven't synced yet
72
  }
73
 
74
+ // Variable products should use some data from the parent_product
75
+ // For performance reasons, that data shouldn't be regenerated every time.
76
+ if ( $parent_product ) {
77
+ $this->gallery_urls = $parent_product->get_gallery_urls();
78
+ $this->fb_use_parent_image = $parent_product->get_use_parent_image();
79
+ $this->main_description = $parent_product->get_fb_description();
 
 
 
 
 
 
 
 
80
  }
81
+ }
82
 
83
+ public function exists() {
84
+ return ( $this->woo_product !== null && $this->woo_product !== false );
85
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ // Fall back to calling method on $woo_product
88
+ public function __call( $function, $args ) {
89
+ if ( $this->woo_product ) {
90
+ return call_user_func_array( array( $this->woo_product, $function ), $args );
91
+ } else {
92
+ $e = new Exception();
93
+ $backtrace = var_export( $e->getTraceAsString(), true );
94
+ WC_Facebookcommerce_Utils::fblog(
95
+ "Calling $function on Null Woo Object. Trace:\n" . $backtrace,
96
+ array(),
97
+ true
98
+ );
99
+ return null;
100
  }
101
+ }
102
 
103
+ public function get_gallery_urls() {
104
+ if ( $this->gallery_urls === null ) {
105
+ if ( is_callable( array( $this, 'get_gallery_image_ids' ) ) ) {
106
+ $image_ids = $this->get_gallery_image_ids();
107
  } else {
108
+ $image_ids = $this->get_gallery_attachment_ids();
109
+ }
110
+ $gallery_urls = array();
111
+ foreach ( $image_ids as $image_id ) {
112
+ $image_url = wp_get_attachment_url( $image_id );
113
+ if ( ! empty( $image_url ) ) {
114
+ array_push(
115
+ $gallery_urls,
116
+ WC_Facebookcommerce_Utils::make_url( $image_url )
117
+ );
118
+ }
119
  }
120
+ $this->gallery_urls = array_filter( $gallery_urls );
121
  }
122
 
123
+ return $this->gallery_urls;
124
+ }
125
 
126
+ public function get_post_data() {
127
+ if ( is_callable( 'get_post' ) ) {
128
+ return get_post( $this->id );
129
+ } else {
130
+ return $this->get_post_data();
131
  }
132
+ }
133
 
134
+ public function get_fb_price( $for_items_batch = false ) {
135
+ $product_price = Products::get_product_price( $this->woo_product );
 
 
 
136
 
137
+ return $for_items_batch ? self::format_price_for_fb_items_batch( $product_price ) : $product_price;
138
+ }
139
 
140
+ private static function format_price_for_fb_items_batch( $price ) {
141
+ // items_batch endpoint requires a string and a currency code
142
+ $formatted = ( $price / 100.0 ) . ' ' . get_woocommerce_currency();
143
+ return $formatted;
144
+ }
 
 
 
 
 
145
 
 
 
146
 
147
+ /**
148
+ * Determines whether the current product is a WooCommerce Bookings product.
149
+ *
150
+ * TODO: add an integration that filters the Facebook price instead {WV 2020-07-22}
151
+ *
152
+ * @since 2.0.0
153
+ *
154
+ * @return bool
155
+ */
156
+ private function is_bookable_product() {
157
+
158
+ return facebook_for_woocommerce()->is_plugin_active( 'woocommerce-bookings.php' ) && class_exists( 'WC_Product_Booking' ) && is_callable( 'is_wc_booking_product' ) && is_wc_booking_product( $this );
159
+ }
160
 
 
 
 
 
 
 
161
 
162
+ /**
163
+ * Gets a list of image URLs to use for this product in Facebook sync.
164
+ *
165
+ * @return array
166
+ */
167
+ public function get_all_image_urls() {
168
 
169
+ $image_urls = array();
 
 
170
 
171
+ $product_image_url = wp_get_attachment_url( $this->woo_product->get_image_id() );
172
+ $parent_product_image_url = null;
173
+ $custom_image_url = $this->woo_product->get_meta( self::FB_PRODUCT_IMAGE );
174
 
175
+ if ( $this->woo_product->is_type( 'variation' ) ) {
176
 
177
+ if ( $parent_product = wc_get_product( $this->woo_product->get_parent_id() ) ) {
 
 
178
 
179
+ $parent_product_image_url = wp_get_attachment_url( $parent_product->get_image_id() );
180
+ }
181
+ }
182
 
183
+ switch ( $this->woo_product->get_meta( Products::PRODUCT_IMAGE_SOURCE_META_KEY ) ) {
 
 
184
 
185
+ case Products::PRODUCT_IMAGE_SOURCE_CUSTOM:
186
+ $image_urls = array( $custom_image_url, $product_image_url, $parent_product_image_url );
187
+ break;
188
 
189
+ case Products::PRODUCT_IMAGE_SOURCE_PARENT_PRODUCT:
190
+ $image_urls = array( $parent_product_image_url, $product_image_url );
191
+ break;
 
 
192
 
193
+ case Products::PRODUCT_IMAGE_SOURCE_PRODUCT:
194
+ default:
195
+ $image_urls = array( $product_image_url, $parent_product_image_url );
196
+ break;
197
+ }
198
 
199
+ $image_urls = array_merge( $image_urls, $this->get_gallery_urls() );
200
+ $image_urls = array_filter( array_unique( $image_urls ) );
 
 
201
 
202
+ // Regenerate $image_url PHP array indexes after filtering.
203
+ // The array_filter does not touches indexes so if something gets removed we may end up with gaps.
204
+ // Later parts of the code expect something to exist under the 0 index.
205
+ $image_urls = array_values( $image_urls );
206
 
207
+ if ( empty( $image_urls ) ) {
208
+ $image_urls[] = wc_placeholder_img_src();
209
  }
210
 
211
+ return $image_urls;
212
+ }
213
 
214
+
215
+ /**
216
+ * Gets the list of additional image URLs for the product from the complete list of image URLs.
217
+ *
218
+ * It assumes the first URL will be used as the product image.
219
+ * It returns 20 or less image URLs because Facebook doesn't allow more items on the additional_image_urls field.
220
+ *
221
+ * @since 2.0.2
222
+ *
223
+ * @param array $image_urls all image URLs for the product.
224
+ * @return array
225
+ */
226
+ private function get_additional_image_urls( $image_urls ) {
227
+
228
+ return array_slice( $image_urls, 1, 20 );
229
+ }
230
+
231
+
232
+ // Returns the parent image id for variable products only.
233
+ public function get_parent_image_id() {
234
+ if ( WC_Facebookcommerce_Utils::is_variation_type( $this->woo_product->get_type() ) ) {
235
+ $parent_data = $this->get_parent_data();
236
+ return $parent_data['image_id'];
 
237
  }
238
+ return null;
239
+ }
240
 
241
+ public function set_description( $description ) {
242
+ $description = stripslashes(
243
+ WC_Facebookcommerce_Utils::clean_string( $description )
244
+ );
245
+ $this->fb_description = $description;
246
+ update_post_meta(
247
+ $this->id,
248
+ self::FB_PRODUCT_DESCRIPTION,
249
+ $description
250
+ );
251
+ }
252
+
253
+ public function set_product_image( $image ) {
254
+ if ( $image !== null && strlen( $image ) !== 0 ) {
255
+ $image = WC_Facebookcommerce_Utils::clean_string( $image );
256
+ $image = WC_Facebookcommerce_Utils::make_url( $image );
257
  update_post_meta(
258
  $this->id,
259
+ self::FB_PRODUCT_IMAGE,
260
+ $image
261
  );
262
  }
263
+ }
264
 
265
+ public function set_price( $price ) {
266
+ if ( is_numeric( $price ) ) {
267
+ update_post_meta(
268
+ $this->id,
269
+ self::FB_PRODUCT_PRICE,
270
+ $price
271
+ );
272
+ } else {
273
+ delete_post_meta(
274
+ $this->id,
275
+ self::FB_PRODUCT_PRICE
276
+ );
277
  }
278
+ }
279
 
280
+ public function get_use_parent_image() {
281
+ if ( $this->fb_use_parent_image === null ) {
282
+ $variant_image_setting =
283
+ get_post_meta( $this->id, self::FB_VARIANT_IMAGE, true );
284
+ $this->fb_use_parent_image = ( $variant_image_setting ) ? true : false;
 
 
 
 
 
 
 
 
285
  }
286
+ return $this->fb_use_parent_image;
287
+ }
288
 
289
+ public function set_use_parent_image( $setting ) {
290
+ $this->fb_use_parent_image = ( $setting == 'yes' );
291
+ update_post_meta(
292
+ $this->id,
293
+ self::FB_VARIANT_IMAGE,
294
+ $this->fb_use_parent_image
295
+ );
296
+ }
297
+
298
+ public function get_fb_description() {
299
+ if ( $this->fb_description ) {
300
+ return $this->fb_description;
301
  }
302
 
303
+ $description = get_post_meta(
304
+ $this->id,
305
+ self::FB_PRODUCT_DESCRIPTION,
306
+ true
307
+ );
308
+
309
+ if ( $description ) {
310
+ return $description;
311
  }
312
 
313
+ if ( WC_Facebookcommerce_Utils::is_variation_type( $this->woo_product->get_type() ) ) {
 
 
 
314
 
315
+ $description = WC_Facebookcommerce_Utils::clean_string( $this->woo_product->get_description() );
 
 
 
 
316
 
317
  if ( $description ) {
318
  return $description;
319
  }
320
+ if ( $this->main_description ) {
321
+ return $this->main_description;
322
+ }
323
+ }
324
 
325
+ $post = $this->get_post_data();
 
 
326
 
327
+ $post_content = WC_Facebookcommerce_Utils::clean_string(
328
+ $post->post_content
329
+ );
330
+ $post_excerpt = WC_Facebookcommerce_Utils::clean_string(
331
+ $post->post_excerpt
332
+ );
333
+ $post_title = WC_Facebookcommerce_Utils::clean_string(
334
+ $post->post_title
335
+ );
336
 
337
+ // Sanitize description
338
+ if ( $post_content ) {
339
+ $description = $post_content;
340
+ }
341
+ if ( $this->sync_short_description || ( $description == '' && $post_excerpt ) ) {
342
+ $description = $post_excerpt;
343
+ }
344
+ if ( $description == '' ) {
345
+ $description = $post_title;
346
+ }
347
 
348
+ return $description;
349
+ }
 
 
 
 
 
 
 
350
 
351
+ /**
352
+ * @param array $product_data
353
+ * @param bool $for_items_batch
354
+ *
355
+ * @return array
356
+ */
357
+ public function add_sale_price( $product_data, $for_items_batch = false ) {
358
+
359
+ $sale_price = $this->woo_product->get_sale_price();
360
+ $sale_price_effective_date = '';
361
+
362
+ $sale_start =
363
+ ( $date = $this->woo_product->get_date_on_sale_from() )
364
+ ? date_i18n( WC_DateTime::ATOM, $date->getOffsetTimestamp() )
365
+ : self::MIN_DATE_1 . self::MIN_TIME;
366
+
367
+ $sale_end =
368
+ ( $date = $this->woo_product->get_date_on_sale_to() )
369
+ ? date_i18n( WC_DateTime::ATOM, $date->getOffsetTimestamp() )
370
+ : self::MAX_DATE . self::MAX_TIME;
371
+
372
+ // check if sale exist
373
+ if ( is_numeric( $sale_price ) && $sale_price > 0 ) {
374
+ $sale_price_effective_date = $sale_start . '/' . $sale_end;
375
+ $sale_price =
376
+ intval( round( $this->get_price_plus_tax( $sale_price ) * 100 ) );
377
+ }
378
 
379
+ // check if sale is expired and sale time range is valid
380
+ if ( $for_items_batch ) {
381
+ $product_data['sale_price_effective_date'] = $sale_price_effective_date;
382
+ $product_data['sale_price'] = is_numeric( $sale_price ) ? self::format_price_for_fb_items_batch( $sale_price ) : "";
383
+ } else {
384
+ $product_data['sale_price_start_date'] = $sale_start;
385
+ $product_data['sale_price_end_date'] = $sale_end;
386
+ $product_data['sale_price'] = is_numeric( $sale_price ) ? $sale_price : 0;
387
  }
388
 
389
+ return $product_data;
390
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
 
 
 
 
 
 
 
 
 
 
392
 
393
+ public function get_price_plus_tax( $price ) {
394
+ $woo_product = $this->woo_product;
395
+ // // wc_get_price_including_tax exist for Woo > 2.7
396
+ if ( function_exists( 'wc_get_price_including_tax' ) ) {
397
+ $args = array(
398
+ 'qty' => 1,
399
+ 'price' => $price,
400
+ );
401
+ return get_option( 'woocommerce_tax_display_shop' ) === 'incl'
402
+ ? wc_get_price_including_tax( $woo_product, $args )
403
+ : wc_get_price_excluding_tax( $woo_product, $args );
404
+ } else {
405
+ return get_option( 'woocommerce_tax_display_shop' ) === 'incl'
406
+ ? $woo_product->get_price_including_tax( 1, $price )
407
+ : $woo_product->get_price_excluding_tax( 1, $price );
408
  }
409
+ }
410
 
411
+ public function get_grouped_product_option_names( $key, $option_values ) {
412
+ // Convert all slug_names in $option_values into the visible names that
413
+ // advertisers have set to be the display names for a given attribute value
414
+ $terms = get_the_terms( $this->id, $key );
415
+ return ! is_array( $terms ) ? array() : array_map(
416
+ function ( $slug_name ) use ( $terms ) {
417
+ foreach ( $terms as $term ) {
418
+ if ( $term->slug === $slug_name ) {
419
+ return $term->name;
420
+ }
421
+ }
422
+ return $slug_name;
423
+ },
424
+ $option_values
425
+ );
426
+ }
427
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
+ public function update_visibility( $is_product_page, $visible_box_checked ) {
430
+ $visibility = get_post_meta( $this->id, self::FB_VISIBILITY, true );
431
+ if ( $visibility && ! $is_product_page ) {
432
+ // If the product was previously set to visible, keep it as visible
433
+ // (unless we're on the product page)
434
+ $this->fb_visibility = $visibility;
435
+ } else {
436
+ // If the product is not visible OR we're on the product page,
437
+ // then update the visibility as needed.
438
+ $this->fb_visibility = $visible_box_checked ? true : false;
439
+ update_post_meta( $this->id, self::FB_VISIBILITY, $this->fb_visibility );
440
  }
441
+ }
442
 
443
+ // wrapper function to find item_id for default variation
444
+ function find_matching_product_variation() {
445
+ if ( is_callable( array( $this, 'get_default_attributes' ) ) ) {
446
+ $default_attributes = $this->get_default_attributes();
447
+ } else {
448
+ $default_attributes = $this->get_variation_default_attributes();
449
+ }
450
 
451
+ if ( ! $default_attributes ) {
452
+ return;
453
+ }
454
+ foreach ( $default_attributes as $key => $value ) {
455
+ if ( strncmp( $key, 'attribute_', strlen( 'attribute_' ) ) === 0 ) {
456
+ continue;
 
 
 
 
 
 
 
 
 
457
  }
458
+ unset( $default_attributes[ $key ] );
459
+ $default_attributes[ sprintf( 'attribute_%s', $key ) ] = $value;
460
  }
461
+ if ( class_exists( 'WC_Data_Store' ) ) {
462
+ // for >= woo 3.0.0
463
+ $data_store = WC_Data_Store::load( 'product' );
464
+ return $data_store->find_matching_product_variation(
465
+ $this,
466
+ $default_attributes
 
 
 
 
 
 
 
 
 
467
  );
468
+ } else {
469
+ return $this->get_matching_variation( $default_attributes );
470
  }
471
+ }
472
 
473
+ private function build_checkout_url( $product_url ) {
474
+ // Use product_url for external/bundle product setting.
475
+ $product_type = $this->get_type();
476
+ if ( ! $product_type || ! isset( self::$use_checkout_url[ $product_type ] ) ) {
477
+ $checkout_url = $product_url;
478
+ } elseif ( wc_get_cart_url() ) {
479
+ $char = '?';
480
+ // Some merchant cart pages are actually a querystring
481
+ if ( strpos( wc_get_cart_url(), '?' ) !== false ) {
482
+ $char = '&';
 
 
483
  }
 
484
 
485
+ $checkout_url = WC_Facebookcommerce_Utils::make_url(
486
+ wc_get_cart_url() . $char
487
+ );
 
 
 
 
488
 
489
+ if ( WC_Facebookcommerce_Utils::is_variation_type( $this->get_type() ) ) {
490
+ $query_data = array(
491
+ 'add-to-cart' => $this->get_parent_id(),
492
+ 'variation_id' => $this->get_id(),
 
 
 
 
 
 
 
 
 
 
 
 
493
  );
 
 
 
 
494
 
495
+ $query_data = array_merge(
496
+ $query_data,
497
+ $this->get_variation_attributes()
 
 
 
 
 
 
 
 
 
 
 
498
  );
499
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  } else {
501
+ $query_data = array(
502
+ 'add-to-cart' => $this->get_id(),
503
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  }
 
505
 
506
+ $checkout_url = $checkout_url . http_build_query( $query_data );
 
 
 
 
 
507
 
508
+ } else {
509
+ $checkout_url = null;
510
+ }//end if
511
+ }
 
 
512
 
513
+ /**
514
+ * Gets product data to send to Facebook.
515
+ *
516
+ * @param string $retailer_id the retailer ID of the product
517
+ * @param string $type_to_prepare_for whether the data is going to be used in a feed upload, an items_batch update or a direct api call
518
+ * @return array
519
+ */
520
+ public function prepare_product( $retailer_id = null, $type_to_prepare_for = self::PRODUCT_PREP_TYPE_NORMAL ) {
521
 
522
+ if ( ! $retailer_id ) {
523
+ $retailer_id =
524
+ WC_Facebookcommerce_Utils::get_fb_retailer_id( $this );
525
+ }
526
+ $image_urls = $this->get_all_image_urls();
 
 
527
 
528
+ // Replace WordPress sanitization's ampersand with a real ampersand.
529
+ $product_url = str_replace(
530
+ '&amp%3B',
531
+ '&',
532
+ html_entity_decode( $this->get_permalink() )
533
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
 
535
+ $id = $this->get_id();
536
+ if ( WC_Facebookcommerce_Utils::is_variation_type( $this->get_type() ) ) {
537
+ $id = $this->get_parent_id();
538
+ }
539
+ $categories =
540
+ WC_Facebookcommerce_Utils::get_product_categories( $id );
541
+
542
+ // Get brand attribute.
543
+ $brand = get_post_meta( $id, Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . 'brand', true );
544
+ $brand_taxonomy = get_the_term_list( $id, 'product_brand', '', ', ' );
545
+
546
+ if ( $brand ) {
547
+ $brand = WC_Facebookcommerce_Utils::clean_string( $brand );
548
+ } elseif ( !is_wp_error( $brand_taxonomy ) && $brand_taxonomy ) {
549
+ $brand = WC_Facebookcommerce_Utils::clean_string( $brand_taxonomy );
550
+ } else {
551
+ $brand = wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() );
552
+ }
553
 
554
+ if ( self::PRODUCT_PREP_TYPE_ITEMS_BATCH === $type_to_prepare_for ) {
555
+ $product_data = array(
556
+ 'title' => WC_Facebookcommerce_Utils::clean_string(
557
+ $this->get_title()
558
+ ),
559
+ 'description' => $this->get_fb_description(),
560
+ 'image_link' => $image_urls[0],
561
+ 'additional_image_link' => $this->get_additional_image_urls( $image_urls ),
562
+ 'link' => $product_url,
563
+ 'brand' => Helper::str_truncate( $brand, 100 ),
564
+ 'retailer_id' => $retailer_id,
565
+ 'price' => $this->get_fb_price( true ),
566
+ 'availability' => $this->is_in_stock() ? 'in stock' : 'out of stock',
567
+ 'visibility' => Products::is_product_visible( $this->woo_product ) ? \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_VISIBLE : \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN,
568
+ );
569
+ $product_data = $this->add_sale_price( $product_data, true );
570
+ } else {
571
+ $product_data = array(
572
+ 'name' => WC_Facebookcommerce_Utils::clean_string(
573
+ $this->get_title()
574
+ ),
575
+ 'description' => $this->get_fb_description(),
576
+ 'image_url' => $image_urls[0],
577
+ 'additional_image_urls' => $this->get_additional_image_urls( $image_urls ),
578
+ 'url' => $product_url,
579
+ 'category' => $categories['categories'],
580
+ 'brand' => Helper::str_truncate( $brand, 100 ),
581
+ 'retailer_id' => $retailer_id,
582
+ 'price' => $this->get_fb_price(),
583
+ 'currency' => get_woocommerce_currency(),
584
+ 'availability' => $this->is_in_stock() ? 'in stock' : 'out of stock',
585
+ 'visibility' => Products::is_product_visible( $this->woo_product ) ? \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_VISIBLE : \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN,
586
+ );
587
+ $product_data = $this->add_sale_price( $product_data );
588
+ }//end if
589
+
590
+ $google_product_category = Products::get_google_product_category_id( $this->woo_product );
591
+ // Currently only items batch and feed support enhanced catalog fields
592
+ if ( self::PRODUCT_PREP_TYPE_NORMAL !== $type_to_prepare_for && $google_product_category ) {
593
+ $product_data['google_product_category'] = $google_product_category;
594
+ $product_data = $this->apply_enhanced_catalog_fields_from_attributes( $product_data, $google_product_category );
595
+ }
596
 
597
+ // add the Commerce values (only stock quantity for the moment)
598
+ if ( Products::is_product_ready_for_commerce( $this->woo_product ) ) {
599
+ $product_data['quantity_to_sell_on_facebook'] = (int) max( 0, $this->woo_product->get_stock_quantity() );
600
+ }
 
601
 
602
+ // Only use checkout URLs if they exist.
603
+ $checkout_url = $this->build_checkout_url( $product_url );
604
+ if ( $checkout_url ) {
605
+ $product_data['checkout_url'] = $checkout_url;
606
+ }
607
+
608
+ // IF using WPML, set the product to hidden unless it is in the
609
+ // default language. WPML >= 3.2 Supported.
610
+ if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
611
+ if ( class_exists( 'WC_Facebook_WPML_Injector' ) && WC_Facebook_WPML_Injector::should_hide( $id ) ) {
612
+ $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
613
  }
614
+ }
615
 
616
  // Exclude variations that are "virtual" products from export to Facebook &&
617
  // No Visibility Option for Variations
621
  $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
622
  }
623
 
624
+ if ( self::PRODUCT_PREP_TYPE_FEED !== $type_to_prepare_for ) {
625
+ $this->prepare_variants_for_item( $product_data );
626
+ } elseif (
627
+ WC_Facebookcommerce_Utils::is_all_caps( $product_data['description'] )
628
+ ) {
629
+ $product_data['description'] =
630
+ mb_strtolower( $product_data['description'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  }
632
 
633
  /**
634
+ * Filters the generated product data.
635
+ *
636
+ * @param int $id Woocommerce product id
637
+ * @param array $product_data An array of product data
638
+ */
639
+ return apply_filters(
640
+ 'facebook_for_woocommerce_integration_prepare_product',
641
+ $product_data,
642
+ $id
643
+ );
644
+ }
 
 
 
645
 
646
+ /**
647
+ * Adds enhanced catalog fields to product data array. Separated from
648
+ * the main function to make it easier to develop and debug, potentially
649
+ * worth refactoring into main prepare_product function when complete.
650
+ *
651
+ * @param array $product_data map
652
+ * @return array
653
+ */
654
+ private function apply_enhanced_catalog_fields_from_attributes( $product_data, $google_category_id ) {
655
+ $google_category_id = $product_data['google_product_category'];
656
+ $category_handler = facebook_for_woocommerce()->get_facebook_category_handler();
657
+ if ( empty( $google_category_id ) || ! $category_handler->is_category( $google_category_id ) ) {
658
+ return $product_data;
659
+ }
660
+ $enhanced_data = array();
661
 
662
+ $category_attrs = $category_handler->get_attributes_with_fallback_to_parent_category( $google_category_id );
663
+ $all_attributes = $this->get_matched_attributes_for_product( $this->woo_product, $category_attrs );
 
 
 
 
 
664
 
665
+ foreach ( $all_attributes as $attribute ) {
666
+ $value = Products::get_enhanced_catalog_attribute( $attribute['key'], $this->woo_product );
667
+ $convert_to_array = (
668
+ isset( $attribute['can_have_multiple_values'] ) &&
669
+ true === $attribute['can_have_multiple_values'] &&
670
+ 'string' === $attribute['type']
671
+ );
672
+
673
+ if ( ! empty( $value ) &&
674
+ $category_handler->is_valid_value_for_attribute( $google_category_id, $attribute['key'], $value )
675
+ ) {
676
+ if ( $convert_to_array ) {
677
+ $value = array_map( 'trim', explode( ',', $value ) );
678
  }
679
+ $enhanced_data[ $attribute['key'] ] = $value;
680
  }
 
 
681
  }
682
 
683
+ return array_merge( $product_data, $enhanced_data );
684
+ }
685
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
 
687
+ /**
688
+ * Filters list of attributes to only those available for a given product
689
+ *
690
+ * @param \WC_Product $product WooCommerce Product
691
+ * @param array $all_attributes List of Enhanced Catalog attributes to match
692
+ * @return array
693
+ */
694
+ public function get_matched_attributes_for_product( $product, $all_attributes ) {
695
+ $matched_attributes = array();
696
+ $sanitized_keys = array_map(
697
+ function( $key ) {
698
+ return \WC_Facebookcommerce_Utils::sanitize_variant_name( $key, false );
699
+ },
700
+ array_keys( $product->get_attributes() )
701
+ );
702
 
703
+ $matched_attributes = array_filter(
704
+ $all_attributes,
705
+ function( $attribute ) use ( $sanitized_keys ) {
706
+ return in_array( $attribute['key'], $sanitized_keys );
707
+ }
708
+ );
709
 
710
+ return $matched_attributes;
711
+ }
712
 
 
 
 
 
 
 
 
713
 
714
+ /**
715
+ * Normalizes variant data for Facebook.
716
+ *
717
+ * @param array $product_data variation product data
718
+ * @return array
719
+ */
720
+ public function prepare_variants_for_item( &$product_data ) {
721
 
722
+ /** @var \WC_Product_Variation $product */
723
+ $product = $this;
 
724
 
725
+ if ( ! $product->is_type( 'variation' ) ) {
726
+ return array();
727
+ }
728
 
729
+ $attributes = $product->get_variation_attributes();
 
 
730
 
731
+ if ( ! $attributes ) {
732
+ return array();
733
+ }
734
 
735
+ $variant_names = array_keys( $attributes );
736
+ $variant_data = array();
 
737
 
738
+ // Loop through variants (size, color, etc) if they exist
739
+ // For each product field type, pull the single variant
740
+ foreach ( $variant_names as $original_variant_name ) {
 
741
 
742
+ // don't handle any attributes that are designated as Commerce attributes
743
+ if ( in_array( str_replace( 'attribute_', '', strtolower( $original_variant_name ) ), Products::get_distinct_product_attributes( $this->woo_product ), true ) ) {
744
+ continue;
745
+ }
746
 
747
+ // Retrieve label name for attribute
748
+ $label = wc_attribute_label( $original_variant_name, $product );
749
 
750
+ // Clean up variant name (e.g. pa_color should be color)
751
+ $new_name = \WC_Facebookcommerce_Utils::sanitize_variant_name( $original_variant_name, false );
 
 
752
 
753
+ // Sometimes WC returns an array, sometimes it's an assoc array, depending
754
+ // on what type of taxonomy it's using. array_values will guarantee we
755
+ // only get a flat array of values.
756
+ if ( $options = \WC_Facebookcommerce_Utils::get_variant_option_name( $this->id, $label, $attributes[ $original_variant_name ] ) ) {
757
 
758
+ if ( is_array( $options ) ) {
759
 
760
+ $option_values = array_values( $options );
761
 
762
+ } else {
763
 
764
+ $option_values = array( $options );
765
+
766
+ // If this attribute has value 'any', options will be empty strings
767
+ // Redirect to product page to select variants.
768
+ // Reset checkout url since checkout_url (build from query data will
769
+ // be invalid in this case.
770
+ if ( count( $option_values ) === 1 && empty( $option_values[0] ) ) {
771
+ $option_values[0] = 'any';
772
+ $product_data['checkout_url'] = $product_data['url'];
773
  }
774
+ }
775
 
776
+ if ( \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER === $new_name && ! isset( $product_data[ \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER ] ) ) {
777
 
778
+ // If we can't validate the gender, this will be null.
779
+ $product_data[ $new_name ] = \WC_Facebookcommerce_Utils::validateGender( $option_values[0] );
780
+ }
781
 
782
+ switch ( $new_name ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
783
 
784
+ case \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER:
785
+ // If we can't validate the GENDER field, we'll fall through to the
786
+ // default case and set the gender into custom data.
787
+ if ( $product_data[ $new_name ] ) {
788
 
789
+ $variant_data[] = array(
790
+ 'product_field' => $new_name,
791
+ 'label' => $label,
792
+ 'options' => $option_values,
793
+ );
794
+ }
795
 
796
+ break;
797
 
798
+ default:
799
+ // This is for any custom_data.
800
+ if ( ! isset( $product_data['custom_data'] ) ) {
801
+ $product_data['custom_data'] = array();
802
+ }
803
+ $new_name = wc_attribute_label( $new_name, $product );
804
+ $product_data['custom_data'][ $new_name ] = urldecode( $option_values[0] );
805
+ break;
806
+ }//end switch
807
+ } else {
808
 
809
+ \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $original_variant_name );
810
+ continue;
811
+ }//end if
812
+ }//end foreach
813
 
814
+ return $variant_data;
815
+ }
816
 
 
 
 
817
 
818
+ /**
819
+ * Normalizes variable product variations data for Facebook.
820
+ *
821
+ * @param bool $feed_data whether this is used for feed data
822
+ * @return array
823
+ */
824
+ public function prepare_variants_for_group( $feed_data = false ) {
825
 
826
+ /** @var \WC_Product_Variable $product */
827
+ $product = $this;
828
+ $final_variants = array();
829
 
830
+ try {
831
 
832
+ if ( ! $product->is_type( 'variable' ) ) {
833
+ throw new \Exception( 'prepare_variants_for_group called on non-variable product' );
834
+ }
835
 
836
+ $variation_attributes = $product->get_variation_attributes();
 
 
 
 
 
837
 
838
+ if ( ! $variation_attributes ) {
839
+ return array();
840
+ }
841
 
842
+ foreach ( array_keys( $product->get_attributes() ) as $name ) {
843
+
844
+ $label = wc_attribute_label( $name, $product );
845
+
846
+ if ( taxonomy_is_product_attribute( $name ) ) {
847
+ $key = $name;
848
+ } else {
849
+ // variation_attributes keys are labels for custom attrs for some reason
850
+ $key = $label;
851
+ }
852
 
853
+ if ( ! $key ) {
854
+ throw new \Exception( "Critical error: can't get attribute name or label!" );
855
+ }
856
 
857
+ if ( isset( $variation_attributes[ $key ] ) ) {
858
+ // array of the options (e.g. small, medium, large)
859
+ $option_values = $variation_attributes[ $key ];
860
+ } else {
861
+ // skip variations without valid attribute options
862
+ \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $name );
863
+ continue;
864
+ }
865
 
866
+ // If this is a variable product, check default attribute.
867
+ // If it's being used, show it as the first option on Facebook.
868
+ if ( $first_option = $product->get_variation_default_attribute( $key ) ) {
869
 
870
+ $index = array_search( $first_option, $option_values, false );
 
871
 
872
+ unset( $option_values[ $index ] );
 
 
873
 
874
+ array_unshift( $option_values, $first_option );
875
+ }
876
 
877
+ if ( function_exists( 'taxonomy_is_product_attribute' ) && taxonomy_is_product_attribute( $name ) ) {
878
+ $option_values = $this->get_grouped_product_option_names( $key, $option_values );
879
+ }
880
 
881
+ switch ( $name ) {
 
 
882
 
883
+ case Products::get_product_color_attribute( $this->woo_product ):
884
+ $name = WC_Facebookcommerce_Utils::FB_VARIANT_COLOR;
885
+ break;
886
 
887
+ case Products::get_product_size_attribute( $this->woo_product ):
888
+ $name = WC_Facebookcommerce_Utils::FB_VARIANT_SIZE;
889
+ break;
 
 
 
 
 
890
 
891
+ case Products::get_product_pattern_attribute( $this->woo_product ):
892
+ $name = WC_Facebookcommerce_Utils::FB_VARIANT_PATTERN;
893
+ break;
 
894
 
895
+ default:
896
+ /**
897
+ * For API approach, product_field need to start with 'custom_data:'
898
+ *
899
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-variant/
900
+ */
901
+ $name = \WC_Facebookcommerce_Utils::sanitize_variant_name( $name );
902
+ }//end switch
903
 
904
+ // for feed uploading, product field should remove prefix 'custom_data:'
905
+ if ( $feed_data ) {
906
+ $name = str_replace( 'custom_data:', '', $name );
907
+ }
908
 
909
+ $final_variants[] = array(
910
+ 'product_field' => $name,
911
+ 'label' => $label,
912
+ 'options' => $option_values,
913
+ );
914
+ }//end foreach
915
+ } catch ( \Exception $e ) {
916
 
917
+ \WC_Facebookcommerce_Utils::fblog( $e->getMessage() );
 
918
 
919
+ return array();
920
+ }//end try
921
 
922
+ return $final_variants;
923
  }
924
 
925
+
926
+ }
includes/fbproductfeed.php CHANGED
@@ -9,760 +9,626 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
 
16
- use SkyVerge\WooCommerce\Facebook\Products;
17
- use SkyVerge\WooCommerce\Facebook\Products\Feed;
18
- use SkyVerge\WooCommerce\PluginFramework\v5_10_0 as Framework;
19
 
20
- if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
 
 
 
21
 
22
- /**
23
- * Initial Sync by Facebook feed class
24
- */
25
- class WC_Facebook_Product_Feed {
26
 
 
 
 
 
 
 
 
 
27
 
28
- /** @var string product catalog feed file directory inside the uploads folder */
29
- const UPLOADS_DIRECTORY = 'facebook_for_woocommerce';
30
- const FILE_NAME = 'product_catalog_%s.csv';
31
- const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
32
- const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
33
- const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
34
- const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
35
- const FB_VISIBILITY = 'fb_visibility';
36
 
37
- private $has_default_product_count = 0;
38
- private $no_default_product_count = 0;
 
 
 
 
 
 
 
 
39
 
40
- /**
41
- * WC_Facebook_Product_Feed constructor.
42
- *
43
- * @param string|null $facebook_catalog_id Facebook catalog ID, if any
44
- * @param \WC_Facebookcommerce_Graph_API|null $fbgraph Facebook Graph API instance
45
- * @param string|null $feed_id Facebook feed ID, if any
46
- */
47
- public function __construct( $facebook_catalog_id = null, $fbgraph = null, $feed_id = null ) {
 
 
48
 
49
- $this->facebook_catalog_id = $facebook_catalog_id;
50
- $this->fbgraph = $fbgraph;
51
- $this->feed_id = $feed_id;
52
- }
53
 
54
- /**
55
- * Generates the product catalog feed.
56
- *
57
- * This replaces any previously generated feed file.
58
- *
59
- * @since 1.11.0
60
- */
61
- public function generate_feed() {
62
- $profiling_logger = facebook_for_woocommerce()->get_profiling_logger();
63
- $profiling_logger->start( 'generate_feed' );
64
-
65
- \WC_Facebookcommerce_Utils::log( 'Generating a fresh product feed file' );
66
 
67
- try {
68
 
69
- $start_time = microtime( true );
70
 
71
- $this->generate_productfeed_file();
 
72
 
73
- $generation_time = microtime( true ) - $start_time;
74
- facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( $generation_time );
75
 
76
- \WC_Facebookcommerce_Utils::log( 'Product feed file generated' );
77
 
78
- } catch ( \Exception $exception ) {
 
 
79
 
80
- \WC_Facebookcommerce_Utils::log( $exception->getMessage() );
81
- // Feed generation failed - clear the generation time to track that there's an issue.
82
- facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( -1 );
83
 
84
- }
 
85
 
86
- $profiling_logger->stop( 'generate_feed' );
87
- }
 
 
 
 
 
 
88
 
89
  /**
90
- * Gets the product catalog feed file path.
91
  *
92
  * @since 1.11.0
93
  *
94
- * @return string
95
  */
96
- public function get_file_path() {
97
-
98
- /**
99
- * Filters the product catalog feed file path.
100
- *
101
- * @since 1.11.0
102
- *
103
- * @param string $file_path the file path
104
- */
105
- return apply_filters( 'wc_facebook_product_catalog_feed_file_path', "{$this->get_file_directory()}/{$this->get_file_name()}" );
106
- }
107
 
108
 
 
 
 
 
 
 
 
 
 
109
  /**
110
- * Gets the product catalog temporary feed file path.
111
  *
112
  * @since 1.11.3
113
  *
114
- * @return string
115
  */
116
- public function get_temp_file_path() {
117
-
118
- /**
119
- * Filters the product catalog temporary feed file path.
120
- *
121
- * @since 1.11.3
122
- *
123
- * @param string $file_path the temporary file path
124
- */
125
- return apply_filters( 'wc_facebook_product_catalog_temp_feed_file_path', "{$this->get_file_directory()}/{$this->get_temp_file_name()}" );
126
- }
127
 
128
 
129
- /**
130
- * Gets the product catalog feed file directory.
131
- *
132
- * @since 1.11.0
133
- *
134
- * @return string
135
- */
136
- public function get_file_directory() {
137
 
138
- $uploads_directory = wp_upload_dir( null, false );
139
 
140
- return trailingslashit( $uploads_directory['basedir'] ) . self::UPLOADS_DIRECTORY;
141
- }
 
 
 
 
 
 
 
 
 
 
142
 
 
143
 
144
  /**
145
- * Gets the product catalog feed file name.
146
  *
147
  * @since 1.11.0
148
  *
149
- * @return string
150
  */
151
- public function get_file_name() {
152
-
153
- $file_name = sprintf( self::FILE_NAME, wp_hash( Feed::get_feed_secret() ) );
154
-
155
- /**
156
- * Filters the product catalog feed file name.
157
- *
158
- * @since 1.11.0
159
- *
160
- * @param string $file_name the file name
161
- */
162
- return apply_filters( 'wc_facebook_product_catalog_feed_file_name', $file_name );
163
- }
164
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  /**
167
- * Gets the product catalog temporary feed file name.
168
  *
169
  * @since 1.11.3
170
  *
171
- * @return string
172
  */
173
- public function get_temp_file_name() {
174
-
175
- $file_name = sprintf( self::FILE_NAME, 'temp_' . wp_hash( Feed::get_feed_secret() ) );
176
-
177
- /**
178
- * Filters the product catalog temporary feed file name.
179
- *
180
- * @since 1.11.3
181
- *
182
- * @param string $file_name the temporary file name
183
- */
184
- return apply_filters( 'wc_facebook_product_catalog_temp_feed_file_name', $file_name );
185
- }
186
-
187
-
188
- public function sync_all_products_using_feed() {
189
- $start_time = microtime( true );
190
- $this->log_feed_progress( 'Sync all products using feed' );
191
-
192
- try {
193
-
194
- if ( ! $this->generate_productfeed_file() ) {
195
- throw new Framework\SV_WC_Plugin_Exception( 'Feed file not generated' );
196
- }
197
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
198
-
199
- $this->log_feed_progress(
200
- 'Failure - Sync all products using feed. ' . $exception->getMessage()
201
- );
202
- return false;
203
- }
204
 
205
- $this->log_feed_progress( 'Sync all products using feed, feed file generated' );
206
 
207
- if ( ! $this->feed_id ) {
208
- $this->feed_id = $this->create_feed();
209
- if ( ! $this->feed_id ) {
210
- $this->log_feed_progress(
211
- 'Failure - Sync all products using feed, facebook feed not created'
212
- );
213
- return false;
214
- }
215
- $this->log_feed_progress(
216
- 'Sync all products using feed, facebook feed created'
217
- );
218
- } else {
219
- $this->log_feed_progress(
220
- 'Sync all products using feed, facebook feed already exists.'
221
- );
222
- }
223
-
224
- $this->upload_id = $this->create_upload( $this->feed_id );
225
- if ( ! $this->upload_id ) {
226
- $this->log_feed_progress(
227
- 'Failure - Sync all products using feed, facebook upload not created'
228
- );
229
- return false;
230
- }
231
- $this->log_feed_progress(
232
- 'Sync all products using feed, facebook upload created'
233
- );
234
 
235
- $total_product_count =
236
- $this->has_default_product_count + $this->no_default_product_count;
237
- $default_product_percentage =
238
- ( $total_product_count == 0 || $this->has_default_product_count == 0 )
239
- ? 0
240
- : $this->has_default_product_count / $total_product_count * 100;
241
- $time_spent = microtime( true ) - $start_time;
242
- $data = array();
243
- // Only log performance if this store has products in order to get average
244
- // performance.
245
- if ( $total_product_count != 0 ) {
246
- $data = array(
247
- 'sync_time' => $time_spent,
248
- 'total' => $total_product_count,
249
- 'default_product_percentage' => $default_product_percentage,
250
- );
251
- }
252
- $this->log_feed_progress( 'Complete - Sync all products using feed.', $data );
253
- return true;
254
- }
255
 
 
 
 
 
 
 
 
256
 
257
- /**
258
- * Gets the product IDs that will be included in the feed file.
259
- *
260
- * @since 1.11.0
261
- *
262
- * @return int[]
263
- */
264
- private function get_product_ids() {
265
- return \WC_Facebookcommerce_Utils::get_all_product_ids_for_sync();
266
  }
267
 
 
268
 
269
- /**
270
- * Generates the product catalog feed file.
271
- *
272
- * @return bool
273
- * @throws Framework\SV_WC_Plugin_Exception
274
- */
275
- public function generate_productfeed_file() {
276
-
277
- if ( ! wp_mkdir_p( $this->get_file_directory() ) ) {
278
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not create product catalog feed directory', 'facebook-for-woocommerce' ), 500 );
279
- }
280
 
281
- $this->create_files_to_protect_product_feed_directory();
282
 
283
- return $this->write_product_feed_file( $this->get_product_ids() );
284
- }
 
 
 
 
285
 
 
286
 
287
- /**
288
- * Creates files in the catalog feed directory to prevent directory listing and hotlinking.
289
- *
290
- * @since 1.11.0
291
- */
292
- public function create_files_to_protect_product_feed_directory() {
293
-
294
- $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
295
-
296
- $files = array(
297
- array(
298
- 'base' => $catalog_feed_directory,
299
- 'file' => 'index.html',
300
- 'content' => '',
301
- ),
302
- array(
303
- 'base' => $catalog_feed_directory,
304
- 'file' => '.htaccess',
305
- 'content' => 'deny from all',
306
- ),
307
- );
308
 
309
- foreach ( $files as $file ) {
310
 
311
- if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
312
 
313
- if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
314
 
315
- fwrite( $file_handle, $file['content'] );
316
- fclose( $file_handle );
317
- }
318
  }
319
  }
320
  }
 
321
 
322
 
323
- /**
324
- * Writes the product catalog feed file with data for the given product IDs.
325
- *
326
- * @since 1.11.0
327
- *
328
- * @param int[] $wp_ids product IDs
329
- * @return bool
330
- */
331
- public function write_product_feed_file( $wp_ids ) {
332
 
333
- try {
334
 
335
- // Step 1: Prepare the temporary empty feed file with header row.
336
- $temp_feed_file = $this->prepare_temporary_feed_file();
337
 
338
- // Step 2: Write products feed into the temporary feed file.
339
- $this->write_products_feed_to_temp_file( $wp_ids, $temp_feed_file );
340
 
341
- // Step 3: Rename temporary feed file to final feed file.
342
- $this->rename_temporary_feed_file_to_final_feed_file();
343
 
344
- $written = true;
345
 
346
- } catch ( Exception $e ) {
347
 
348
- WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
349
 
350
- $written = false;
351
 
352
- // close the temporary file
353
- if ( ! empty( $temp_feed_file ) && is_resource( $temp_feed_file ) ) {
354
 
355
- fclose( $temp_feed_file );
356
- }
357
 
358
- // delete the temporary file
359
- if ( ! empty( $temp_file_path ) && file_exists( $temp_file_path ) ) {
360
 
361
- unlink( $temp_file_path );
362
- }
363
  }
364
-
365
- return $written;
366
  }
367
 
368
- /**
369
- * Prepare a fresh empty temporary feed file with the header row.
370
- *
371
- * @since 2.6.6
372
- *
373
- * @throws Framework\SV_WC_Plugin_Exception We can't open the file or the file is not writable.
374
- * @return resource A file pointer resource.
375
- */
376
- public function prepare_temporary_feed_file() {
377
- $temp_file_path = $this->get_temp_file_path();
378
- $temp_feed_file = @fopen( $temp_file_path, 'w' );
379
 
380
- // check if we can open the temporary feed file
381
- if ( false === $temp_feed_file || ! is_writable( $temp_file_path ) ) {
382
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog temporary feed file for writing', 'facebook-for-woocommerce' ), 500 );
383
- }
 
 
 
 
 
 
 
384
 
385
- $file_path = $this->get_file_path();
 
 
 
386
 
387
- // check if we will be able to write to the final feed file
388
- if ( file_exists( $file_path ) && ! is_writable( $file_path ) ) {
389
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog feed file for writing', 'facebook-for-woocommerce' ), 500 );
390
- }
391
 
392
- fwrite( $temp_feed_file, $this->get_product_feed_header_row() );
393
- return $temp_feed_file;
 
394
  }
395
 
396
- /**
397
- * Write products feed into a file.
398
- *
399
- * @since 2.6.6
400
- *
401
- * @return void
402
- */
403
- public function write_products_feed_to_temp_file( $wp_ids, $temp_feed_file ) {
404
- $product_group_attribute_variants = array();
405
 
406
- foreach ( $wp_ids as $wp_id ) {
 
 
 
 
 
 
 
 
407
 
408
- $woo_product = new WC_Facebook_Product( $wp_id );
409
 
410
- // Skip if we don't have a valid product object.
411
- if ( ! $woo_product->woo_product instanceof \WC_Product ) {
412
- continue;
413
- }
414
 
415
- // Skip if not enabled for sync.
416
- if ( ! facebook_for_woocommerce()->get_product_sync_validator( $woo_product->woo_product )->passes_all_checks() ) {
417
- continue;
418
- }
419
-
420
- $product_data_as_feed_row = $this->prepare_product_for_feed(
421
- $woo_product,
422
- $product_group_attribute_variants
423
- );
424
 
425
- if ( ! empty( $temp_feed_file ) ) {
426
- fwrite( $temp_feed_file, $product_data_as_feed_row );
427
- }
428
  }
429
 
430
- wp_reset_postdata();
 
 
 
431
 
432
  if ( ! empty( $temp_feed_file ) ) {
433
- fclose( $temp_feed_file );
434
  }
435
  }
436
 
437
- /**
438
- * Rename temporary feed file into the final feed file.
439
- * This is the last step fo the feed generation procedure.
440
- *
441
- * @since 2.6.6
442
- *
443
- * @return void
444
- */
445
- public function rename_temporary_feed_file_to_final_feed_file() {
446
- $file_path = $this->get_file_path();
447
- $temp_file_path = $this->get_temp_file_path();
448
- if ( ! empty( $temp_file_path ) && ! empty( $file_path ) ) {
449
 
450
- $renamed = rename( $temp_file_path, $file_path );
451
-
452
- if ( empty( $renamed ) ) {
453
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not rename the product catalog feed file', 'facebook-for-woocommerce' ), 500 );
454
- }
455
- }
456
  }
 
457
 
458
- public function get_product_feed_header_row() {
459
- return 'id,title,description,image_link,link,product_type,' .
460
- 'brand,price,availability,item_group_id,checkout_url,' .
461
- 'additional_image_link,sale_price_effective_date,sale_price,condition,' .
462
- 'visibility,gender,color,size,pattern,google_product_category,default_product,variant' . PHP_EOL;
463
- }
 
 
 
 
 
 
464
 
 
465
 
466
- /**
467
- * Assembles product payload in feed upload for initial sync.
468
- *
469
- * @param \WC_Facebook_Product $woo_product WooCommerce product object normalized by Facebook
470
- * @param array $attribute_variants passed by reference
471
- * @return string product feed line data
472
- */
473
- private function prepare_product_for_feed( $woo_product, &$attribute_variants ) {
474
 
475
- $product_data = $woo_product->prepare_product( null, \WC_Facebook_Product::PRODUCT_PREP_TYPE_FEED );
476
- $item_group_id = $product_data['retailer_id'];
 
 
 
 
477
 
478
- // prepare variant column for variable products
479
- $product_data['variant'] = '';
480
 
481
- if ( $woo_product->is_type( 'variation' ) ) {
 
 
 
 
 
 
 
482
 
483
- $parent_id = $woo_product->get_parent_id();
 
484
 
485
- if ( ! isset( $attribute_variants[ $parent_id ] ) ) {
 
486
 
487
- $parent_product = new \WC_Facebook_Product( $parent_id );
488
- $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
489
- $variation_id = $parent_product->find_matching_product_variation();
490
- $variants_for_group = $parent_product->prepare_variants_for_group( true );
491
- $parent_attribute_values = array(
492
- 'gallery_urls' => $gallery_urls,
493
- 'default_variant_id' => $variation_id,
494
- 'item_group_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product ),
495
- );
496
 
497
- foreach ( $variants_for_group as $variant ) {
498
- if ( isset( $variant['product_field'], $variant['options'] ) ) {
499
- $parent_attribute_values[ $variant['product_field'] ] = $variant['options'];
500
- }
501
- }
502
 
503
- // cache product group variants
504
- $attribute_variants[ $parent_id ] = $parent_attribute_values;
505
 
506
- } else {
 
 
 
 
 
 
 
 
507
 
508
- $parent_attribute_values = $attribute_variants[ $parent_id ];
 
 
 
509
  }
510
 
511
- $variants_for_item = $woo_product->prepare_variants_for_item( $product_data );
512
- $variant_feed_column = array();
513
-
514
- foreach ( $variants_for_item as $variant_array ) {
515
-
516
- static::format_variant_for_feed(
517
- $variant_array['product_field'],
518
- $variant_array['options'][0],
519
- $parent_attribute_values,
520
- $variant_feed_column
521
- );
522
- }
523
 
524
- if ( isset( $product_data['custom_data'] ) && is_array( $product_data['custom_data'] ) ) {
525
 
526
- foreach ( $product_data['custom_data'] as $product_field => $value ) {
 
527
 
528
- static::format_variant_for_feed(
529
- $product_field,
530
- $value,
531
- $parent_attribute_values,
532
- $variant_feed_column
533
- );
534
- }
535
- }
536
 
537
- if ( ! empty( $variant_feed_column ) ) {
538
- $product_data['variant'] = '"' . implode( ',', $variant_feed_column ) . '"';
539
- }
540
 
541
- if ( isset( $parent_attribute_values['gallery_urls'] ) ) {
542
- $product_data['additional_image_urls'] = array_merge( $product_data['additional_image_urls'], $parent_attribute_values['gallery_urls'] );
543
- }
 
 
 
 
544
 
545
- if ( isset( $parent_attribute_values['item_group_id'] ) ) {
546
- $item_group_id = $parent_attribute_values['item_group_id'];
547
- }
548
 
549
- $product_data['default_product'] = $parent_attribute_values['default_variant_id'] == $woo_product->id ? 'default' : '';
550
 
551
- // If this group has default variant value, log this product item
552
- if ( isset( $parent_attribute_values['default_variant_id'] ) && ! empty( $parent_attribute_values['default_variant_id'] ) ) {
553
- $this->has_default_product_count++;
554
- } else {
555
- $this->no_default_product_count++;
 
556
  }
557
  }
558
 
559
- // log simple product
560
- if ( ! isset( $product_data['default_product'] ) ) {
561
-
562
- $this->no_default_product_count++;
563
-
564
- $product_data['default_product'] = '';
565
  }
566
 
567
- // when dealing with the feed file, only set out-of-stock products as hidden
568
- if ( Products::product_should_be_deleted( $woo_product->woo_product ) ) {
569
- $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
570
  }
571
 
572
- // Sale price, only format if we have a sale price set for the product, else leave as empty ('').
573
- $sale_price = static::get_value_from_product_data( $product_data, 'sale_price', '' );
574
- $sale_price_effective_date = '';
575
- if ( is_numeric( $sale_price ) && $sale_price > 0 ) {
576
- $sale_price_effective_date = static::get_value_from_product_data( $product_data, 'sale_price_start_date' ) . '/' . $this->get_value_from_product_data( $product_data, 'sale_price_end_date' );
577
- $sale_price = static::format_price_for_feed(
578
- $sale_price,
579
- static::get_value_from_product_data( $product_data, 'currency' )
580
- );
581
  }
582
 
583
- return $product_data['retailer_id'] . ',' .
584
- static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'name' ) ) . ',' .
585
- static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'description' ) ) . ',' .
586
- static::get_value_from_product_data( $product_data, 'image_url' ) . ',' .
587
- static::get_value_from_product_data( $product_data, 'url' ) . ',' .
588
- static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'category' ) ) . ',' .
589
- static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'brand' ) ) . ',' .
590
- static::format_price_for_feed(
591
- static::get_value_from_product_data( $product_data, 'price', 0 ),
592
- static::get_value_from_product_data( $product_data, 'currency' )
593
- ) . ',' .
594
- static::get_value_from_product_data( $product_data, 'availability' ) . ',' .
595
- $item_group_id . ',' .
596
- static::get_value_from_product_data( $product_data, 'checkout_url' ) . ',' .
597
- static::format_additional_image_url( static::get_value_from_product_data( $product_data, 'additional_image_urls' ) ) . ',' .
598
- $sale_price_effective_date . ',' .
599
- $sale_price . ',' .
600
- 'new' . ',' .
601
- static::get_value_from_product_data( $product_data, 'visibility' ) . ',' .
602
- static::get_value_from_product_data( $product_data, 'gender' ) . ',' .
603
- static::get_value_from_product_data( $product_data, 'color' ) . ',' .
604
- static::get_value_from_product_data( $product_data, 'size' ) . ',' .
605
- static::get_value_from_product_data( $product_data, 'pattern' ) . ',' .
606
- static::get_value_from_product_data( $product_data, 'google_product_category' ) . ',' .
607
- static::get_value_from_product_data( $product_data, 'default_product' ) . ',' .
608
- static::get_value_from_product_data( $product_data, 'variant' ) . PHP_EOL;
609
- }
610
-
611
 
612
- private function create_feed() {
613
- $result = $this->fbgraph->create_feed(
614
- $this->facebook_catalog_id,
615
- array( 'name' => self::FEED_NAME )
616
- );
617
- if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
618
- $this->log_feed_progress( json_encode( $result ) );
619
- return null;
620
- }
621
- $decode_result = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
622
- $feed_id = $decode_result->id;
623
- if ( ! $feed_id ) {
624
- $this->log_feed_progress(
625
- 'Response from creating feed not return feed id!'
626
- );
627
- return null;
628
  }
629
- return $feed_id;
630
  }
631
 
632
- private function create_upload( $facebook_feed_id ) {
633
- $result = $this->fbgraph->create_upload(
634
- $facebook_feed_id,
635
- $this->get_file_path()
636
- );
637
- if ( is_null( $result ) || ! isset( $result['id'] ) || ! $result['id'] ) {
638
- $this->log_feed_progress( json_encode( $result ) );
639
- return null;
640
- }
641
- $upload_id = $result['id'];
642
- return $upload_id;
643
- }
644
 
645
- private static function format_additional_image_url( $product_image_urls ) {
646
- // returns the top 10 additional image urls separated by a comma
647
- // according to feed api rules
648
- $product_image_urls = array_slice(
649
- $product_image_urls,
650
- 0,
651
- self::FB_ADDITIONAL_IMAGES_FOR_FEED
652
- );
653
- if ( $product_image_urls ) {
654
- return '"' . implode( ',', $product_image_urls ) . '"';
655
- } else {
656
- return '';
657
- }
658
- }
659
 
660
- private static function format_string_for_feed( $text ) {
661
- if ( (bool) $text ) {
662
- return '"' . str_replace( '"', "'", $text ) . '"';
663
- } else {
664
- return '';
665
- }
666
  }
667
 
668
- private static function format_price_for_feed( $value, $currency ) {
669
- return (string) ( round( $value / (float) 100, 2 ) ) . $currency;
 
670
  }
671
 
672
- private static function format_variant_for_feed(
673
- $product_field,
674
- $value,
675
- $parent_attribute_values,
676
- &$variant_feed_column ) {
677
- if ( ! array_key_exists( $product_field, $parent_attribute_values ) ) {
678
- return;
679
- }
680
- array_push(
681
- $variant_feed_column,
682
- $product_field . ':' .
683
- implode( '/', $parent_attribute_values[ $product_field ] ) . ':' .
684
- $value
685
  );
686
  }
687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
- /**
690
- * Gets the value from the product data.
691
- *
692
- * This method is used to avoid PHP undefined index notices.
693
- *
694
- * @since 2.1.0
695
- *
696
- * @param array $product_data the product data retrieved from a Woo product passed by reference
697
- * @param string $index the data index
698
- * @param mixed $return_if_not_set the value to be returned if product data has no index (default to '')
699
- * @return mixed|string the data value or an empty string
700
- */
701
- private static function get_value_from_product_data( &$product_data, $index, $return_if_not_set = '' ) {
702
-
703
- return isset( $product_data[ $index ] ) ? $product_data[ $index ] : $return_if_not_set;
704
  }
 
705
 
 
 
 
 
 
 
 
706
 
707
- /**
708
- * Gets the status of the configured feed upload.
709
- *
710
- * The status indicator is one of 'in progress', 'complete', or 'error'.
711
- *
712
- * @param array $settings
713
- * @return string
714
- */
715
- public function is_upload_complete( &$settings ) {
716
-
717
- $upload_id = facebook_for_woocommerce()->get_integration()->get_upload_id();
718
- $result = $this->fbgraph->get_upload_status( $upload_id );
719
-
720
- if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
721
-
722
- $this->log_feed_progress( json_encode( $result ) );
723
 
724
- return 'error';
725
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
726
 
727
- $response_body = json_decode( wp_remote_retrieve_body( $result ) );
728
- $upload_status = 'error';
729
 
730
- if ( isset( $response_body->end_time ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
731
 
732
- $settings['upload_end_time'] = $response_body->end_time;
 
733
 
734
- $upload_status = 'complete';
735
 
736
- } elseif ( 200 === (int) wp_remote_retrieve_response_code( $result ) ) {
 
 
 
 
 
 
 
 
737
 
738
- $upload_status = 'in progress';
739
- }
740
 
741
- return $upload_status;
 
 
742
  }
743
 
 
744
 
745
- // Log progress in local log file and FB.
746
- public function log_feed_progress( $msg, $object = array() ) {
747
- WC_Facebookcommerce_Utils::fblog( $msg, $object );
748
- $msg = empty( $object ) ? $msg : $msg . json_encode( $object );
749
- WC_Facebookcommerce_Utils::log( $msg );
750
  }
751
 
752
- /**
753
- * @deprecated in favor of WC_Facebookcommerce_Utils::get_all_product_ids_for_sync() due to duplicate functionality
754
- */
755
- public function get_product_wpid() {
756
 
757
- wc_deprecated_function( __METHOD__, '2.4.0', '\\WC_Facebookcommerce_Utils::get_all_product_ids_for_sync()' );
758
 
759
- $post_ids = WC_Facebookcommerce_Utils::get_wp_posts(
760
- null,
761
- null,
762
- array( 'product', 'product_variation' )
763
- );
764
- return $post_ids;
765
- }
766
  }
767
-
768
- endif;
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
13
 
14
+ use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException;
15
+ use WooCommerce\Facebook\Products;
16
+ use WooCommerce\Facebook\Products\Feed;
17
 
18
+ /**
19
+ * Initial Sync by Facebook feed class
20
+ */
21
+ class WC_Facebook_Product_Feed {
22
 
 
 
 
 
23
 
24
+ /** @var string product catalog feed file directory inside the uploads folder */
25
+ const UPLOADS_DIRECTORY = 'facebook_for_woocommerce';
26
+ const FILE_NAME = 'product_catalog_%s.csv';
27
+ const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
28
+ const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
29
+ const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
30
+ const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
31
+ const FB_VISIBILITY = 'fb_visibility';
32
 
33
+ private $has_default_product_count = 0;
34
+ private $no_default_product_count = 0;
 
 
 
 
 
 
35
 
36
+ /**
37
+ * WC_Facebook_Product_Feed constructor.
38
+ *
39
+ * @param string|null $facebook_catalog_id Facebook catalog ID, if any
40
+ * @param string|null $feed_id Facebook feed ID, if any
41
+ */
42
+ public function __construct( $facebook_catalog_id = null, $feed_id = null ) {
43
+ $this->facebook_catalog_id = $facebook_catalog_id;
44
+ $this->feed_id = $feed_id;
45
+ }
46
 
47
+ /**
48
+ * Generates the product catalog feed.
49
+ *
50
+ * This replaces any previously generated feed file.
51
+ *
52
+ * @since 1.11.0
53
+ */
54
+ public function generate_feed() {
55
+ $profiling_logger = facebook_for_woocommerce()->get_profiling_logger();
56
+ $profiling_logger->start( 'generate_feed' );
57
 
58
+ \WC_Facebookcommerce_Utils::log( 'Generating a fresh product feed file' );
 
 
 
59
 
60
+ try {
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ $start_time = microtime( true );
63
 
64
+ $this->generate_productfeed_file();
65
 
66
+ $generation_time = microtime( true ) - $start_time;
67
+ facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( $generation_time );
68
 
69
+ \WC_Facebookcommerce_Utils::log( 'Product feed file generated' );
 
70
 
71
+ } catch ( \Exception $exception ) {
72
 
73
+ \WC_Facebookcommerce_Utils::log( $exception->getMessage() );
74
+ // Feed generation failed - clear the generation time to track that there's an issue.
75
+ facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( -1 );
76
 
77
+ }
 
 
78
 
79
+ $profiling_logger->stop( 'generate_feed' );
80
+ }
81
 
82
+ /**
83
+ * Gets the product catalog feed file path.
84
+ *
85
+ * @since 1.11.0
86
+ *
87
+ * @return string
88
+ */
89
+ public function get_file_path() {
90
 
91
  /**
92
+ * Filters the product catalog feed file path.
93
  *
94
  * @since 1.11.0
95
  *
96
+ * @param string $file_path the file path
97
  */
98
+ return apply_filters( 'wc_facebook_product_catalog_feed_file_path', "{$this->get_file_directory()}/{$this->get_file_name()}" );
99
+ }
 
 
 
 
 
 
 
 
 
100
 
101
 
102
+ /**
103
+ * Gets the product catalog temporary feed file path.
104
+ *
105
+ * @since 1.11.3
106
+ *
107
+ * @return string
108
+ */
109
+ public function get_temp_file_path() {
110
+
111
  /**
112
+ * Filters the product catalog temporary feed file path.
113
  *
114
  * @since 1.11.3
115
  *
116
+ * @param string $file_path the temporary file path
117
  */
118
+ return apply_filters( 'wc_facebook_product_catalog_temp_feed_file_path', "{$this->get_file_directory()}/{$this->get_temp_file_name()}" );
119
+ }
 
 
 
 
 
 
 
 
 
120
 
121
 
122
+ /**
123
+ * Gets the product catalog feed file directory.
124
+ *
125
+ * @since 1.11.0
126
+ *
127
+ * @return string
128
+ */
129
+ public function get_file_directory() {
130
 
131
+ $uploads_directory = wp_upload_dir( null, false );
132
 
133
+ return trailingslashit( $uploads_directory['basedir'] ) . self::UPLOADS_DIRECTORY;
134
+ }
135
+
136
+
137
+ /**
138
+ * Gets the product catalog feed file name.
139
+ *
140
+ * @since 1.11.0
141
+ *
142
+ * @return string
143
+ */
144
+ public function get_file_name() {
145
 
146
+ $file_name = sprintf( self::FILE_NAME, wp_hash( Feed::get_feed_secret() ) );
147
 
148
  /**
149
+ * Filters the product catalog feed file name.
150
  *
151
  * @since 1.11.0
152
  *
153
+ * @param string $file_name the file name
154
  */
155
+ return apply_filters( 'wc_facebook_product_catalog_feed_file_name', $file_name );
156
+ }
157
+
 
 
 
 
 
 
 
 
 
 
158
 
159
+ /**
160
+ * Gets the product catalog temporary feed file name.
161
+ *
162
+ * @since 1.11.3
163
+ *
164
+ * @return string
165
+ */
166
+ public function get_temp_file_name() {
167
+
168
+ $file_name = sprintf( self::FILE_NAME, 'temp_' . wp_hash( Feed::get_feed_secret() ) );
169
 
170
  /**
171
+ * Filters the product catalog temporary feed file name.
172
  *
173
  * @since 1.11.3
174
  *
175
+ * @param string $file_name the temporary file name
176
  */
177
+ return apply_filters( 'wc_facebook_product_catalog_temp_feed_file_name', $file_name );
178
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
 
180
 
181
+ /**
182
+ * Gets the product IDs that will be included in the feed file.
183
+ *
184
+ * @since 1.11.0
185
+ *
186
+ * @return int[]
187
+ */
188
+ private function get_product_ids() {
189
+ return \WC_Facebookcommerce_Utils::get_all_product_ids_for_sync();
190
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ /**
194
+ * Generates the product catalog feed file.
195
+ *
196
+ * @return bool
197
+ * @throws PluginException
198
+ */
199
+ public function generate_productfeed_file() {
200
 
201
+ if ( ! wp_mkdir_p( $this->get_file_directory() ) ) {
202
+ throw new PluginException( __( 'Could not create product catalog feed directory', 'facebook-for-woocommerce' ), 500 );
 
 
 
 
 
 
 
203
  }
204
 
205
+ $this->create_files_to_protect_product_feed_directory();
206
 
207
+ return $this->write_product_feed_file( $this->get_product_ids() );
208
+ }
 
 
 
 
 
 
 
 
 
209
 
 
210
 
211
+ /**
212
+ * Creates files in the catalog feed directory to prevent directory listing and hotlinking.
213
+ *
214
+ * @since 1.11.0
215
+ */
216
+ public function create_files_to_protect_product_feed_directory() {
217
 
218
+ $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
219
 
220
+ $files = array(
221
+ array(
222
+ 'base' => $catalog_feed_directory,
223
+ 'file' => 'index.html',
224
+ 'content' => '',
225
+ ),
226
+ array(
227
+ 'base' => $catalog_feed_directory,
228
+ 'file' => '.htaccess',
229
+ 'content' => 'deny from all',
230
+ ),
231
+ );
 
 
 
 
 
 
 
 
 
232
 
233
+ foreach ( $files as $file ) {
234
 
235
+ if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
236
 
237
+ if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
238
 
239
+ fwrite( $file_handle, $file['content'] );
240
+ fclose( $file_handle );
 
241
  }
242
  }
243
  }
244
+ }
245
 
246
 
247
+ /**
248
+ * Writes the product catalog feed file with data for the given product IDs.
249
+ *
250
+ * @since 1.11.0
251
+ *
252
+ * @param int[] $wp_ids product IDs
253
+ * @return bool
254
+ */
255
+ public function write_product_feed_file( $wp_ids ) {
256
 
257
+ try {
258
 
259
+ // Step 1: Prepare the temporary empty feed file with header row.
260
+ $temp_feed_file = $this->prepare_temporary_feed_file();
261
 
262
+ // Step 2: Write products feed into the temporary feed file.
263
+ $this->write_products_feed_to_temp_file( $wp_ids, $temp_feed_file );
264
 
265
+ // Step 3: Rename temporary feed file to final feed file.
266
+ $this->rename_temporary_feed_file_to_final_feed_file();
267
 
268
+ $written = true;
269
 
270
+ } catch ( Exception $e ) {
271
 
272
+ WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
273
 
274
+ $written = false;
275
 
276
+ // close the temporary file
277
+ if ( ! empty( $temp_feed_file ) && is_resource( $temp_feed_file ) ) {
278
 
279
+ fclose( $temp_feed_file );
280
+ }
281
 
282
+ // delete the temporary file
283
+ if ( ! empty( $temp_file_path ) && file_exists( $temp_file_path ) ) {
284
 
285
+ unlink( $temp_file_path );
 
286
  }
 
 
287
  }
288
 
289
+ return $written;
290
+ }
 
 
 
 
 
 
 
 
 
291
 
292
+ /**
293
+ * Prepare a fresh empty temporary feed file with the header row.
294
+ *
295
+ * @since 2.6.6
296
+ *
297
+ * @throws PluginException We can't open the file or the file is not writable.
298
+ * @return resource A file pointer resource.
299
+ */
300
+ public function prepare_temporary_feed_file() {
301
+ $temp_file_path = $this->get_temp_file_path();
302
+ $temp_feed_file = @fopen( $temp_file_path, 'w' );
303
 
304
+ // check if we can open the temporary feed file
305
+ if ( false === $temp_feed_file || ! is_writable( $temp_file_path ) ) {
306
+ throw new PluginException( __( 'Could not open the product catalog temporary feed file for writing', 'facebook-for-woocommerce' ), 500 );
307
+ }
308
 
309
+ $file_path = $this->get_file_path();
 
 
 
310
 
311
+ // check if we will be able to write to the final feed file
312
+ if ( file_exists( $file_path ) && ! is_writable( $file_path ) ) {
313
+ throw new PluginException( __( 'Could not open the product catalog feed file for writing', 'facebook-for-woocommerce' ), 500 );
314
  }
315
 
316
+ fwrite( $temp_feed_file, $this->get_product_feed_header_row() );
317
+ return $temp_feed_file;
318
+ }
 
 
 
 
 
 
319
 
320
+ /**
321
+ * Write products feed into a file.
322
+ *
323
+ * @since 2.6.6
324
+ *
325
+ * @return void
326
+ */
327
+ public function write_products_feed_to_temp_file( $wp_ids, $temp_feed_file ) {
328
+ $product_group_attribute_variants = array();
329
 
330
+ foreach ( $wp_ids as $wp_id ) {
331
 
332
+ $woo_product = new WC_Facebook_Product( $wp_id );
 
 
 
333
 
334
+ // Skip if we don't have a valid product object.
335
+ if ( ! $woo_product->woo_product instanceof \WC_Product ) {
336
+ continue;
337
+ }
 
 
 
 
 
338
 
339
+ // Skip if not enabled for sync.
340
+ if ( ! facebook_for_woocommerce()->get_product_sync_validator( $woo_product->woo_product )->passes_all_checks() ) {
341
+ continue;
342
  }
343
 
344
+ $product_data_as_feed_row = $this->prepare_product_for_feed(
345
+ $woo_product,
346
+ $product_group_attribute_variants
347
+ );
348
 
349
  if ( ! empty( $temp_feed_file ) ) {
350
+ fwrite( $temp_feed_file, $product_data_as_feed_row );
351
  }
352
  }
353
 
354
+ wp_reset_postdata();
 
 
 
 
 
 
 
 
 
 
 
355
 
356
+ if ( ! empty( $temp_feed_file ) ) {
357
+ fclose( $temp_feed_file );
 
 
 
 
358
  }
359
+ }
360
 
361
+ /**
362
+ * Rename temporary feed file into the final feed file.
363
+ * This is the last step fo the feed generation procedure.
364
+ *
365
+ * @since 2.6.6
366
+ *
367
+ * @return void
368
+ */
369
+ public function rename_temporary_feed_file_to_final_feed_file() {
370
+ $file_path = $this->get_file_path();
371
+ $temp_file_path = $this->get_temp_file_path();
372
+ if ( ! empty( $temp_file_path ) && ! empty( $file_path ) ) {
373
 
374
+ $renamed = rename( $temp_file_path, $file_path );
375
 
376
+ if ( empty( $renamed ) ) {
377
+ throw new PluginException( __( 'Could not rename the product catalog feed file', 'facebook-for-woocommerce' ), 500 );
378
+ }
379
+ }
380
+ }
 
 
 
381
 
382
+ public function get_product_feed_header_row() {
383
+ return 'id,title,description,image_link,link,product_type,' .
384
+ 'brand,price,availability,item_group_id,checkout_url,' .
385
+ 'additional_image_link,sale_price_effective_date,sale_price,condition,' .
386
+ 'visibility,gender,color,size,pattern,google_product_category,default_product,variant' . PHP_EOL;
387
+ }
388
 
 
 
389
 
390
+ /**
391
+ * Assembles product payload in feed upload for initial sync.
392
+ *
393
+ * @param \WC_Facebook_Product $woo_product WooCommerce product object normalized by Facebook
394
+ * @param array $attribute_variants passed by reference
395
+ * @return string product feed line data
396
+ */
397
+ private function prepare_product_for_feed( $woo_product, &$attribute_variants ) {
398
 
399
+ $product_data = $woo_product->prepare_product( null, \WC_Facebook_Product::PRODUCT_PREP_TYPE_FEED );
400
+ $item_group_id = $product_data['retailer_id'];
401
 
402
+ // prepare variant column for variable products
403
+ $product_data['variant'] = '';
404
 
405
+ if ( $woo_product->is_type( 'variation' ) ) {
 
 
 
 
 
 
 
 
406
 
407
+ $parent_id = $woo_product->get_parent_id();
 
 
 
 
408
 
409
+ if ( ! isset( $attribute_variants[ $parent_id ] ) ) {
 
410
 
411
+ $parent_product = new \WC_Facebook_Product( $parent_id );
412
+ $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
413
+ $variation_id = $parent_product->find_matching_product_variation();
414
+ $variants_for_group = $parent_product->prepare_variants_for_group( true );
415
+ $parent_attribute_values = array(
416
+ 'gallery_urls' => $gallery_urls,
417
+ 'default_variant_id' => $variation_id,
418
+ 'item_group_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product ),
419
+ );
420
 
421
+ foreach ( $variants_for_group as $variant ) {
422
+ if ( isset( $variant['product_field'], $variant['options'] ) ) {
423
+ $parent_attribute_values[ $variant['product_field'] ] = $variant['options'];
424
+ }
425
  }
426
 
427
+ // cache product group variants
428
+ $attribute_variants[ $parent_id ] = $parent_attribute_values;
 
 
 
 
 
 
 
 
 
 
429
 
430
+ } else {
431
 
432
+ $parent_attribute_values = $attribute_variants[ $parent_id ];
433
+ }
434
 
435
+ $variants_for_item = $woo_product->prepare_variants_for_item( $product_data );
436
+ $variant_feed_column = array();
 
 
 
 
 
 
437
 
438
+ foreach ( $variants_for_item as $variant_array ) {
 
 
439
 
440
+ static::format_variant_for_feed(
441
+ $variant_array['product_field'],
442
+ $variant_array['options'][0],
443
+ $parent_attribute_values,
444
+ $variant_feed_column
445
+ );
446
+ }
447
 
448
+ if ( isset( $product_data['custom_data'] ) && is_array( $product_data['custom_data'] ) ) {
 
 
449
 
450
+ foreach ( $product_data['custom_data'] as $product_field => $value ) {
451
 
452
+ static::format_variant_for_feed(
453
+ $product_field,
454
+ $value,
455
+ $parent_attribute_values,
456
+ $variant_feed_column
457
+ );
458
  }
459
  }
460
 
461
+ if ( ! empty( $variant_feed_column ) ) {
462
+ $product_data['variant'] = '"' . implode( ',', $variant_feed_column ) . '"';
 
 
 
 
463
  }
464
 
465
+ if ( isset( $parent_attribute_values['gallery_urls'] ) ) {
466
+ $product_data['additional_image_urls'] = array_merge( $product_data['additional_image_urls'], $parent_attribute_values['gallery_urls'] );
 
467
  }
468
 
469
+ if ( isset( $parent_attribute_values['item_group_id'] ) ) {
470
+ $item_group_id = $parent_attribute_values['item_group_id'];
 
 
 
 
 
 
 
471
  }
472
 
473
+ $product_data['default_product'] = $parent_attribute_values['default_variant_id'] == $woo_product->id ? 'default' : '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
 
475
+ // If this group has default variant value, log this product item
476
+ if ( isset( $parent_attribute_values['default_variant_id'] ) && ! empty( $parent_attribute_values['default_variant_id'] ) ) {
477
+ $this->has_default_product_count++;
478
+ } else {
479
+ $this->no_default_product_count++;
 
 
 
 
 
 
 
 
 
 
 
480
  }
 
481
  }
482
 
483
+ // log simple product
484
+ if ( ! isset( $product_data['default_product'] ) ) {
 
 
 
 
 
 
 
 
 
 
485
 
486
+ $this->no_default_product_count++;
 
 
 
 
 
 
 
 
 
 
 
 
 
487
 
488
+ $product_data['default_product'] = '';
 
 
 
 
 
489
  }
490
 
491
+ // when dealing with the feed file, only set out-of-stock products as hidden
492
+ if ( Products::product_should_be_deleted( $woo_product->woo_product ) ) {
493
+ $product_data['visibility'] = \WC_Facebookcommerce_Integration::FB_SHOP_PRODUCT_HIDDEN;
494
  }
495
 
496
+ // Sale price, only format if we have a sale price set for the product, else leave as empty ('').
497
+ $sale_price = static::get_value_from_product_data( $product_data, 'sale_price', '' );
498
+ $sale_price_effective_date = '';
499
+ if ( is_numeric( $sale_price ) && $sale_price > 0 ) {
500
+ $sale_price_effective_date = static::get_value_from_product_data( $product_data, 'sale_price_start_date' ) . '/' . $this->get_value_from_product_data( $product_data, 'sale_price_end_date' );
501
+ $sale_price = static::format_price_for_feed(
502
+ $sale_price,
503
+ static::get_value_from_product_data( $product_data, 'currency' )
 
 
 
 
 
504
  );
505
  }
506
 
507
+ return $product_data['retailer_id'] . ',' .
508
+ static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'name' ) ) . ',' .
509
+ static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'description' ) ) . ',' .
510
+ static::get_value_from_product_data( $product_data, 'image_url' ) . ',' .
511
+ static::get_value_from_product_data( $product_data, 'url' ) . ',' .
512
+ static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'category' ) ) . ',' .
513
+ static::format_string_for_feed( static::get_value_from_product_data( $product_data, 'brand' ) ) . ',' .
514
+ static::format_price_for_feed(
515
+ static::get_value_from_product_data( $product_data, 'price', 0 ),
516
+ static::get_value_from_product_data( $product_data, 'currency' )
517
+ ) . ',' .
518
+ static::get_value_from_product_data( $product_data, 'availability' ) . ',' .
519
+ $item_group_id . ',' .
520
+ static::get_value_from_product_data( $product_data, 'checkout_url' ) . ',' .
521
+ static::format_additional_image_url( static::get_value_from_product_data( $product_data, 'additional_image_urls' ) ) . ',' .
522
+ $sale_price_effective_date . ',' .
523
+ $sale_price . ',' .
524
+ 'new' . ',' .
525
+ static::get_value_from_product_data( $product_data, 'visibility' ) . ',' .
526
+ static::get_value_from_product_data( $product_data, 'gender' ) . ',' .
527
+ static::get_value_from_product_data( $product_data, 'color' ) . ',' .
528
+ static::get_value_from_product_data( $product_data, 'size' ) . ',' .
529
+ static::get_value_from_product_data( $product_data, 'pattern' ) . ',' .
530
+ static::get_value_from_product_data( $product_data, 'google_product_category' ) . ',' .
531
+ static::get_value_from_product_data( $product_data, 'default_product' ) . ',' .
532
+ static::get_value_from_product_data( $product_data, 'variant' ) . PHP_EOL;
533
+ }
534
 
535
+ private static function format_additional_image_url( $product_image_urls ) {
536
+ // returns the top 10 additional image urls separated by a comma
537
+ // according to feed api rules
538
+ $product_image_urls = array_slice(
539
+ $product_image_urls,
540
+ 0,
541
+ self::FB_ADDITIONAL_IMAGES_FOR_FEED
542
+ );
543
+ if ( $product_image_urls ) {
544
+ return '"' . implode( ',', $product_image_urls ) . '"';
545
+ } else {
546
+ return '';
 
 
 
547
  }
548
+ }
549
 
550
+ private static function format_string_for_feed( $text ) {
551
+ if ( (bool) $text ) {
552
+ return '"' . str_replace( '"', "'", $text ) . '"';
553
+ } else {
554
+ return '';
555
+ }
556
+ }
557
 
558
+ private static function format_price_for_feed( $value, $currency ) {
559
+ return (string) ( round( $value / (float) 100, 2 ) ) . $currency;
560
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
561
 
562
+ private static function format_variant_for_feed(
563
+ $product_field,
564
+ $value,
565
+ $parent_attribute_values,
566
+ &$variant_feed_column ) {
567
+ if ( ! array_key_exists( $product_field, $parent_attribute_values ) ) {
568
+ return;
569
+ }
570
+ array_push(
571
+ $variant_feed_column,
572
+ $product_field . ':' .
573
+ implode( '/', $parent_attribute_values[ $product_field ] ) . ':' .
574
+ $value
575
+ );
576
+ }
577
 
 
 
578
 
579
+ /**
580
+ * Gets the value from the product data.
581
+ *
582
+ * This method is used to avoid PHP undefined index notices.
583
+ *
584
+ * @since 2.1.0
585
+ *
586
+ * @param array $product_data the product data retrieved from a Woo product passed by reference
587
+ * @param string $index the data index
588
+ * @param mixed $return_if_not_set the value to be returned if product data has no index (default to '')
589
+ * @return mixed|string the data value or an empty string
590
+ */
591
+ private static function get_value_from_product_data( &$product_data, $index, $return_if_not_set = '' ) {
592
 
593
+ return isset( $product_data[ $index ] ) ? $product_data[ $index ] : $return_if_not_set;
594
+ }
595
 
 
596
 
597
+ /**
598
+ * Gets the status of the configured feed upload.
599
+ *
600
+ * The status indicator is one of 'in progress', 'complete', or 'error'.
601
+ *
602
+ * @param array $settings
603
+ * @return string
604
+ */
605
+ public function is_upload_complete( &$settings ) {
606
 
607
+ $upload_id = facebook_for_woocommerce()->get_integration()->get_upload_id();
608
+ $result = facebook_for_woocommerce()->get_api()->read_upload( $upload_id );
609
 
610
+ if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
611
+ $this->log_feed_progress( json_encode( $result ) );
612
+ return 'error';
613
  }
614
 
615
+ $upload_status = 'error';
616
 
617
+ if ( isset( $result->end_time ) ) {
618
+ $settings['upload_end_time'] = $result->end_time;
619
+ $upload_status = 'complete';
620
+ } elseif ( 200 === (int) wp_remote_retrieve_response_code( $result ) ) {
621
+ $upload_status = 'in progress';
622
  }
623
 
624
+ return $upload_status;
625
+ }
 
 
626
 
 
627
 
628
+ // Log progress in local log file and FB.
629
+ public function log_feed_progress( $msg, $object = array() ) {
630
+ WC_Facebookcommerce_Utils::fblog( $msg, $object );
631
+ $msg = empty( $object ) ? $msg : $msg . json_encode( $object );
632
+ WC_Facebookcommerce_Utils::log( $msg );
 
 
633
  }
634
+ }
 
includes/fbutils.php CHANGED
@@ -9,623 +9,604 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- use SkyVerge\WooCommerce\Facebook\Events\Normalizer;
17
 
18
- if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  /**
21
- * FB Graph API helper functions
 
 
 
 
 
 
22
  */
23
- class WC_Facebookcommerce_Utils {
24
-
25
- const FB_RETAILER_ID_PREFIX = 'wc_post_id_';
26
- const PLUGIN_VERSION = \WC_Facebookcommerce::VERSION; // TODO: remove this in v2.0.0 {CW 2020-02-06}
27
-
28
- // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}
29
- const FB_VARIANT_IMAGE = 'fb_image';
30
- const FB_VARIANT_SIZE = 'size';
31
- const FB_VARIANT_COLOR = 'color';
32
- const FB_VARIANT_COLOUR = 'colour';
33
- const FB_VARIANT_PATTERN = 'pattern';
34
- const FB_VARIANT_GENDER = 'gender';
35
-
36
- public static $ems = null;
37
- public static $fbgraph = null;
38
- public static $store_name = null;
39
-
40
- public static $validGenderArray =
41
- array(
42
- 'male' => 1,
43
- 'female' => 1,
44
- 'unisex' => 1,
45
- );
46
- /**
47
- * WooCommerce 2.1 support for wc_enqueue_js
48
- *
49
- * @since 1.2.1
50
- *
51
- * @access public
52
- * @param string $code
53
- * @return void
54
- */
55
- public static function wc_enqueue_js( $code ) {
56
- global $wc_queued_js;
57
 
58
- if ( function_exists( 'wc_enqueue_js' ) && empty( $wc_queued_js ) ) {
59
- wc_enqueue_js( $code );
60
- } else {
61
- $wc_queued_js = $code . "\n" . $wc_queued_js;
62
- }
63
  }
 
64
 
65
- /**
66
- * Validate URLs, make relative URLs absolute
67
- *
68
- * @access public
69
- * @param string $url
70
- * @return string
71
- */
72
- public static function make_url( $url ) {
73
- if (
74
- // The first check incorrectly fails for URLs with special chars.
75
- ! filter_var( $url, FILTER_VALIDATE_URL ) &&
76
- substr( $url, 0, 4 ) !== 'http'
77
- ) {
78
- return get_site_url() . $url;
79
- } else {
80
- return $url;
81
- }
82
  }
 
83
 
84
- /**
85
- * Product ID for Dynamic Ads on Facebook can be SKU or wc_post_id_123
86
- * This function should be used to get retailer_id based on a WC_Product
87
- * from WooCommerce
88
- *
89
- * @access public
90
- * @param WC_Product $woo_product
91
- * @return string
92
- */
93
- public static function get_fb_retailer_id( $woo_product ) {
94
- $woo_id = $woo_product->get_id();
95
-
96
- /*
97
- * Call $woo_product->get_id() instead of ->id to account for Variable
98
- * products, which have their own variant_ids.
99
- */
100
- $fb_retailer_id = $woo_product->get_sku() ?
101
- $woo_product->get_sku() . '_' . $woo_id :
102
- self::FB_RETAILER_ID_PREFIX . $woo_id;
103
-
104
- /**
105
- * Filter facebook retailer id value.
106
- * This can be used to match retailer id generated by other Facebook plugins.
107
- *
108
- * @since 2.6.12
109
- * @param string Facebook Retailer ID.
110
- * @param WC_Product WooCommerce product.
111
- */
112
- return apply_filters( 'wc_facebook_fb_retailer_id', $fb_retailer_id, $woo_product );
113
- }
114
 
115
- /**
116
- * Return categories for products/pixel
117
- *
118
- * @access public
119
- * @param String $id
120
- * @return Array
121
  */
122
- public static function get_product_categories( $wpid ) {
123
- $category_path = wp_get_post_terms(
124
- $wpid,
125
- 'product_cat',
126
- array( 'fields' => 'all' )
127
- );
128
- $content_category = array_values(
129
- array_map(
130
- function( $item ) {
131
- return $item->name;
132
- },
133
- $category_path
134
- )
135
- );
136
- $content_category_slice = array_slice( $content_category, -1 );
137
- $categories =
138
- empty( $content_category ) ? '""' : implode( ', ', $content_category );
139
- return array(
140
- 'name' => array_pop( $content_category_slice ),
141
- 'categories' => $categories,
142
- );
143
- }
144
 
145
  /**
146
- * Returns content id to match on for Pixel fires.
 
147
  *
148
- * @access public
149
- * @param WC_Product $woo_product
150
- * @return array
151
  */
152
- public static function get_fb_content_ids( $woo_product ) {
153
- return array( self::get_fb_retailer_id( $woo_product ) );
154
- }
155
 
156
- /**
157
- * Clean up strings for FB Graph POSTing.
158
- * This function should will:
159
- * 1. Replace newlines chars/nbsp with a real space
160
- * 2. strip_tags()
161
- * 3. trim()
162
- *
163
- * @access public
164
- * @param String string
165
- * @return string
166
- */
167
- public static function clean_string( $string ) {
168
-
169
- /**
170
- * Filters whether the shortcodes should be applied for a string when syncing a product or be stripped out.
171
- *
172
- * @since 2.6.19
173
- *
174
- * @param bool $apply_shortcodes Shortcodes are applied if set to `true` and stripped out if set to `false`.
175
- * @param string $string String to clean up.
176
- */
177
- $apply_shortcodes = apply_filters( 'wc_facebook_string_apply_shortcodes', false, $string );
178
- if ( $apply_shortcodes ) {
179
- // Apply active shortcodes
180
- $string = do_shortcode( $string );
181
- } else {
182
- // Strip out active shortcodes
183
- $string = strip_shortcodes( $string );
184
- }
185
 
186
- $string = str_replace( array( '&amp%3B', '&amp;' ), '&', $string );
187
- $string = str_replace( array( "\r", '&nbsp;', "\t" ), ' ', $string );
188
- $string = wp_strip_all_tags( $string, false ); // true == remove line breaks
189
- return $string;
190
- }
 
 
 
 
 
191
 
192
- /**
193
- * Returns flat array of woo IDs for variable products, or
194
- * an array with a single woo ID for simple products.
195
- *
196
- * @access public
197
- * @param WC_Product $woo_product
198
- * @return array
199
- */
200
- public static function get_product_array( $woo_product ) {
201
- $result = array();
202
- if ( self::is_variable_type( $woo_product->get_type() ) ) {
203
- foreach ( $woo_product->get_children() as $item_id ) {
204
- array_push( $result, $item_id );
205
- }
206
- return $result;
207
- } else {
208
- return array( $woo_product->get_id() );
209
- }
210
- }
211
 
212
  /**
213
- * Returns true if WooCommerce plugin found.
 
 
214
  *
215
- * @access public
216
- * @return bool
217
  */
218
- public static function isWoocommerceIntegration() {
219
- return class_exists( 'WooCommerce' );
 
 
 
 
 
220
  }
221
 
222
- /**
223
- * Returns integration dependent name.
224
- *
225
- * @access public
226
- * @return string
227
- */
228
- public static function getIntegrationName() {
229
- if ( self::isWoocommerceIntegration() ) {
230
- return 'WooCommerce';
231
- } else {
232
- return 'WordPress';
 
 
 
 
 
 
 
 
233
  }
 
 
 
234
  }
 
235
 
236
- /**
237
- * Returns user info for the current WP user.
238
- *
239
- * @access public
240
- * @param AAMSettings $aam_settings
241
- * @return array
242
- */
243
- public static function get_user_info( $aam_settings ) {
244
- $current_user = wp_get_current_user();
245
- if ( 0 === $current_user->ID || $aam_settings == null || ! $aam_settings->get_enable_automatic_matching() ) {
246
- // User not logged in or pixel not configured with automatic advance matching
247
- return array();
248
- } else {
249
- // Keys documented in
250
- // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
251
- $user_data = array(
252
- 'em' => $current_user->user_email,
253
- 'fn' => $current_user->user_firstname,
254
- 'ln' => $current_user->user_lastname,
255
- 'external_id' => strval( $current_user->ID ),
256
- );
257
- $user_id = $current_user->ID;
258
- $user_data['ct'] = get_user_meta( $user_id, 'billing_city', true );
259
- $user_data['zp'] = get_user_meta( $user_id, 'billing_postcode', true );
260
- $user_data['country'] = get_user_meta( $user_id, 'billing_country', true );
261
- $user_data['st'] = get_user_meta( $user_id, 'billing_state', true );
262
- $user_data['ph'] = get_user_meta( $user_id, 'billing_phone', true );
263
- // Each field that is not present in AAM settings or is empty is deleted from user data
264
- foreach ( $user_data as $field => $value ) {
265
- if ( $value === null || $value === ''
266
- || ! in_array( $field, $aam_settings->get_enabled_automatic_matching_fields() )
267
- ) {
268
- unset( $user_data[ $field ] );
269
- }
270
- }
271
- // Country is a special case, it is returned as country in AAM settings
272
- // But used as cn in pixel
273
- if ( array_key_exists( 'country', $user_data ) ) {
274
- $country = $user_data['country'];
275
- $user_data['cn'] = $country;
276
- unset( $user_data['country'] );
277
- }
278
- $user_data = Normalizer::normalize_array( $user_data, true );
279
- return $user_data;
280
- }
281
  }
 
282
 
283
- /**
284
- * Utility function for development logging.
285
- */
286
- public static function fblog(
287
- $message,
288
- $object = array(),
289
- $error = false,
290
- $ems = '' ) {
291
- if ( $error ) {
292
- $object['plugin_version'] = self::PLUGIN_VERSION;
293
- $object['php_version'] = phpversion();
294
- }
295
- $message = json_encode(
296
- array(
297
- 'message' => $message,
298
- 'object' => $object,
299
- )
 
 
 
300
  );
301
- $ems = $ems ?: self::$ems;
302
- if ( $ems ) {
303
- self::$fbgraph->log(
304
- $ems,
305
- $message,
306
- $error
307
- );
308
- } else {
309
- error_log(
310
- 'external merchant setting is null, something wrong here: ' .
311
- $message
312
- );
 
313
  }
314
- }
315
-
316
- /**
317
- * Utility function for development Tip Events logging.
318
- */
319
- public static function tip_events_log(
320
- $tip_id,
321
- $channel_id,
322
- $event,
323
- $ems = '' ) {
324
-
325
- $ems = $ems ?: self::$ems;
326
- if ( $ems ) {
327
- self::$fbgraph->log_tip_event(
328
- $tip_id,
329
- $channel_id,
330
- $event
331
- );
332
- } else {
333
- error_log( 'external merchant setting is null' );
334
  }
 
 
335
  }
 
336
 
337
- public static function is_variation_type( $type ) {
338
- return $type == 'variation' || $type == 'subscription_variation';
 
 
 
 
 
 
 
 
 
339
  }
340
-
341
- public static function is_variable_type( $type ) {
342
- return $type == 'variable' || $type == 'variable-subscription';
 
 
 
 
 
 
 
 
 
 
 
343
  }
 
344
 
345
- public static function check_woo_ajax_permissions( $action_text, $die ) {
346
- if ( ! current_user_can( 'manage_woocommerce' ) ) {
347
- self::log(
348
- 'Non manage_woocommerce user attempting to' . $action_text . '!',
349
- array(),
350
- true
351
- );
352
- if ( $die ) {
353
- wp_die();
354
- }
355
- return false;
356
- }
357
- return true;
358
  }
 
359
 
360
- /**
361
- * Returns true if id is a positive non-zero integer
362
- *
363
- * @access public
364
- * @param string $pixel_id
365
- * @return bool
366
- */
367
- public static function is_valid_id( $pixel_id ) {
368
- return isset( $pixel_id ) && is_numeric( $pixel_id ) && (int) $pixel_id > 0;
369
- }
370
 
371
- /**
372
- * Helper function to query posts.
373
- */
374
- public static function get_wp_posts(
375
- $product_group_id = null,
376
- $compare_condition = null,
377
- $post_type = 'product' ) {
378
- $args = array(
379
- 'fields' => 'ids',
380
- 'meta_query' => array(
381
- ( ( $product_group_id ) ?
382
- array(
383
- 'key' => $product_group_id,
384
- 'compare' => $compare_condition,
385
- ) : array()
386
- ),
387
- ),
388
- 'post_status' => 'publish',
389
- 'post_type' => $post_type,
390
- 'posts_per_page' => -1,
391
  );
392
- return get_posts( $args );
 
 
 
393
  }
 
 
394
 
395
- /**
396
- * Helper log function for debugging
397
- */
398
- public static function log( $message ) {
 
 
 
 
 
 
399
 
400
- // if this file is being included outside the plugin, or the plugin setting is disabled
401
- if ( ! function_exists( 'facebook_for_woocommerce' ) || ! facebook_for_woocommerce()->get_integration()->is_debug_mode_enabled() ) {
402
- return;
403
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
- if ( is_array( $message ) || is_object( $message ) ) {
406
- $message = json_encode( $message );
407
- } else {
408
- $message = sanitize_textarea_field( $message );
409
- }
410
 
411
- facebook_for_woocommerce()->log( $message );
 
 
412
  }
413
 
414
- // Return store name with sanitized apostrophe
415
- public static function get_store_name() {
416
- if ( self::$store_name ) {
417
- return self::$store_name;
418
- }
419
- $name = trim(
420
- str_replace(
421
- "'",
422
- "\u{2019}",
423
- html_entity_decode(
424
- get_bloginfo( 'name' ),
425
- ENT_QUOTES,
426
- 'UTF-8'
427
- )
428
- )
429
- );
430
- if ( $name ) {
431
- self::$store_name = $name;
432
- return $name;
433
- }
434
- // Fallback to site url
435
- $url = get_site_url();
436
- if ( $url ) {
437
- self::$store_name = parse_url( $url, PHP_URL_HOST );
438
- return self::$store_name;
439
- }
440
- // If site url doesn't exist, fall back to http host.
441
- if ( $_SERVER['HTTP_HOST'] ) {
442
- self::$store_name = $_SERVER['HTTP_HOST'];
443
- return self::$store_name;
444
- }
445
-
446
- // If http host doesn't exist, fall back to local host name.
447
- $url = gethostname();
448
- self::$store_name = $url;
449
- return ( self::$store_name ) ? ( self::$store_name ) : 'A Store Has No Name';
450
  }
451
 
 
 
452
 
453
- /**
454
- * Get visible name for variant attribute rather than the slug
455
- *
456
- * @param int $wp_id Post ID.
457
- * @param string $label Attribute label.
458
- * @param string $default_value Default value to use if the term has no name.
459
- * @return string Term name or the default value.
460
- */
461
- public static function get_variant_option_name( $wp_id, $label, $default_value ) {
462
- $meta = get_post_meta( $wp_id, $label, true );
463
- $attribute_name = str_replace( 'attribute_', '', $label );
464
- $term = get_term_by( 'slug', $meta, $attribute_name );
465
- return $term && $term->name ? $term->name : $default_value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  }
467
 
468
- /**
469
- * Get all products for synchronization tasks.
470
- *
471
- * Warning: While changing this code please make sure that it scales properly.
472
- * Sites with big product catalogs should not experience memory problems.
473
- *
474
- * @return array IDs of all product for synchronization.
475
- */
476
- public static function get_all_product_ids_for_sync() {
477
- // Get all published products ids. This includes parent products of variations.
478
- $product_args = array(
479
- 'fields' => 'ids',
480
- 'post_status' => 'publish',
481
- 'post_type' => 'product',
482
- 'posts_per_page' => -1,
483
- );
484
- $product_ids = get_posts( $product_args );
485
-
486
- // Get all variations ids with their parents ids.
487
- $variation_args = array(
488
- 'fields' => 'id=>parent',
489
- 'post_status' => 'publish',
490
- 'post_type' => 'product_variation',
491
- 'posts_per_page' => -1,
492
- );
493
- $variation_products = get_posts( $variation_args );
494
 
495
- /*
496
- * Collect all parent products.
497
- * Exclude variations which parents are not 'publish'.
498
- */
499
- $parent_product_ids = array();
500
- foreach ( $variation_products as $post_id => $parent_id ) {
501
- /*
502
- * Keep track of all parents to remove them from the list of products to sync.
503
- * Use key to automatically remove duplicated items.
504
- */
505
- $parent_product_ids[ $parent_id ] = true;
506
-
507
- // Include variations with published parents only.
508
- if ( in_array( $parent_id, $product_ids ) ) {
509
- $product_ids[] = $post_id;
510
- }
511
- }
512
 
513
- // Remove parent products because those can't be represented as Product Items.
514
- return array_diff( $product_ids, array_keys( $parent_product_ids ) );
515
- }
 
 
 
 
 
 
 
 
 
 
 
516
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
  /*
519
- * Change variant product field name from Woo taxonomy to FB name
 
520
  */
521
- public static function sanitize_variant_name( $name, $use_custom_data = true ) {
522
- $name = str_replace( array( 'attribute_', 'pa_' ), '', strtolower( $name ) );
 
 
 
 
 
523
 
524
- // British spelling
525
- if ( $name === self::FB_VARIANT_COLOUR ) {
526
- $name = self::FB_VARIANT_COLOR;
527
  }
 
528
 
529
- if ( $use_custom_data ) {
530
- switch ( $name ) {
531
- case self::FB_VARIANT_SIZE:
532
- case self::FB_VARIANT_COLOR:
533
- case self::FB_VARIANT_GENDER:
534
- case self::FB_VARIANT_PATTERN:
535
- break;
536
- default:
537
- $name = 'custom_data:' . strtolower( $name );
538
- break;
539
- }
540
- }
541
 
542
- return $name;
 
 
 
 
 
 
 
 
 
543
  }
544
 
545
- public static function validateGender( $gender ) {
546
- if ( $gender && ! isset( self::$validGenderArray[ $gender ] ) ) {
547
- $first_char = strtolower( substr( $gender, 0, 1 ) );
548
- // Men, Man, Boys
549
- if ( $first_char === 'm' || $first_char === 'b' ) {
550
- return 'male';
551
- }
552
- // Women, Woman, Female, Ladies
553
- if ( $first_char === 'w' || $first_char === 'f' || $first_char === 'l' ) {
554
- return 'female';
555
- }
556
- if ( $first_char === 'u' ) {
557
- return 'unisex';
558
- }
559
- if ( strlen( $gender ) >= 3 ) {
560
- $gender = strtolower( substr( $gender, 0, 3 ) );
561
- if ( $gender === 'gir' || $gender === 'her' ) {
562
- return 'female';
563
- }
564
- if ( $gender === 'him' || $gender === 'his' || $gender == 'guy' ) {
565
- return 'male';
566
- }
567
- }
568
- return null;
569
  }
570
- return $gender;
571
  }
572
 
573
- public static function get_fbid_post_meta( $wp_id, $fbid_type ) {
574
- return get_post_meta( $wp_id, $fbid_type, true );
575
- }
576
 
577
- public static function is_all_caps( $value ) {
578
- if ( $value === null || $value === '' ) {
579
- return true;
 
 
 
 
 
 
 
580
  }
581
- if ( preg_match( '/[^\\p{Common}\\p{Latin}]/u', $value ) ) {
582
- // Contains non-western characters
583
- // So, it can't be all uppercase
584
- return false;
585
  }
586
- $latin_string = preg_replace( '/[^\\p{Latin}]/u', '', $value );
587
- if ( $latin_string === '' ) {
588
- // Symbols only
589
- return true;
 
 
 
 
590
  }
591
- return strtoupper( $latin_string ) === $latin_string;
592
  }
 
 
593
 
594
- public static function decode_json( $json_string, $assoc = false ) {
595
- // Plugin requires 5.6.0 but for some user use 5.5.9 JSON_BIGINT_AS_STRING
596
- // will cause 502 issue when redirect.
597
- return version_compare( phpversion(), '5.6.0' ) >= 0
598
- ? json_decode( $json_string, $assoc, 512, JSON_BIGINT_AS_STRING )
599
- : json_decode( $json_string, $assoc, 512 );
600
- }
601
 
602
- public static function set_test_fail_reason( $msg, $trace ) {
603
- $reason_msg = get_transient( 'facebook_plugin_test_fail' );
604
- if ( $reason_msg ) {
605
- $msg = $reason_msg . PHP_EOL . $msg;
606
- }
607
- set_transient( 'facebook_plugin_test_fail', $msg );
608
- set_transient( 'facebook_plugin_test_stack_trace', $trace );
 
609
  }
 
 
 
 
 
 
 
610
 
611
- /**
612
- * Helper function to check time cap.
613
- */
614
- public static function check_time_cap( $from, $date_cap ) {
615
- if ( $from == null ) {
616
- return true;
617
- }
618
- $now = new DateTime( current_time( 'mysql' ) );
619
- $diff_in_day = $now->diff( new DateTime( $from ) )->format( '%a' );
620
- return is_numeric( $diff_in_day ) && (int) $diff_in_day > $date_cap;
 
 
621
  }
 
 
 
622
 
623
- public static function get_cached_best_tip() {
624
- $cached_best_tip = self::decode_json(
625
- get_option( 'fb_info_banner_last_best_tip', '' )
626
- );
627
- return $cached_best_tip;
 
628
  }
 
 
 
629
  }
630
 
631
- endif;
 
 
 
 
 
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
 
 
13
 
14
+ use WooCommerce\Facebook\Events\AAMSettings;
15
+ use WooCommerce\Facebook\Events\Normalizer;
16
 
17
+ /**
18
+ * FB Graph API helper functions
19
+ */
20
+ class WC_Facebookcommerce_Utils {
21
+
22
+ const FB_RETAILER_ID_PREFIX = 'wc_post_id_';
23
+ const PLUGIN_VERSION = \WC_Facebookcommerce::VERSION; // TODO: remove this in v2.0.0 {CW 2020-02-06}
24
+
25
+ // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}
26
+ const FB_VARIANT_IMAGE = 'fb_image';
27
+ const FB_VARIANT_SIZE = 'size';
28
+ const FB_VARIANT_COLOR = 'color';
29
+ const FB_VARIANT_COLOUR = 'colour';
30
+ const FB_VARIANT_PATTERN = 'pattern';
31
+ const FB_VARIANT_GENDER = 'gender';
32
+
33
+ public static $ems = null;
34
+ public static $store_name = null;
35
+
36
+ public static $validGenderArray =
37
+ array(
38
+ 'male' => 1,
39
+ 'female' => 1,
40
+ 'unisex' => 1,
41
+ );
42
  /**
43
+ * WooCommerce 2.1 support for wc_enqueue_js
44
+ *
45
+ * @since 1.2.1
46
+ *
47
+ * @access public
48
+ * @param string $code
49
+ * @return void
50
  */
51
+ public static function wc_enqueue_js( $code ) {
52
+ global $wc_queued_js;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ if ( function_exists( 'wc_enqueue_js' ) && empty( $wc_queued_js ) ) {
55
+ wc_enqueue_js( $code );
56
+ } else {
57
+ $wc_queued_js = $code . "\n" . $wc_queued_js;
 
58
  }
59
+ }
60
 
61
+ /**
62
+ * Validate URLs, make relative URLs absolute
63
+ *
64
+ * @access public
65
+ * @param string $url
66
+ * @return string
67
+ */
68
+ public static function make_url( $url ) {
69
+ if (
70
+ // The first check incorrectly fails for URLs with special chars.
71
+ ! filter_var( $url, FILTER_VALIDATE_URL ) &&
72
+ substr( $url, 0, 4 ) !== 'http'
73
+ ) {
74
+ return get_site_url() . $url;
75
+ } else {
76
+ return $url;
 
77
  }
78
+ }
79
 
80
+ /**
81
+ * Product ID for Dynamic Ads on Facebook can be SKU or wc_post_id_123
82
+ * This function should be used to get retailer_id based on a WC_Product
83
+ * from WooCommerce
84
+ *
85
+ * @access public
86
+ * @param WC_Product|WC_Facebook_Product $woo_product
87
+ * @return string
88
+ */
89
+ public static function get_fb_retailer_id( $woo_product ) {
90
+ $woo_id = $woo_product->get_id();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ /*
93
+ * Call $woo_product->get_id() instead of ->id to account for Variable
94
+ * products, which have their own variant_ids.
 
 
 
95
  */
96
+ $fb_retailer_id = $woo_product->get_sku() ?
97
+ $woo_product->get_sku() . '_' . $woo_id :
98
+ self::FB_RETAILER_ID_PREFIX . $woo_id;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  /**
101
+ * Filter facebook retailer id value.
102
+ * This can be used to match retailer id generated by other Facebook plugins.
103
  *
104
+ * @since 2.6.12
105
+ * @param string Facebook Retailer ID.
106
+ * @param WC_Product WooCommerce product.
107
  */
108
+ return apply_filters( 'wc_facebook_fb_retailer_id', $fb_retailer_id, $woo_product );
109
+ }
 
110
 
111
+ /**
112
+ * Return categories for products/pixel
113
+ *
114
+ * @access public
115
+ * @param String $id
116
+ * @return Array
117
+ */
118
+ public static function get_product_categories( $wpid ) {
119
+ $category_path = wp_get_post_terms(
120
+ $wpid,
121
+ 'product_cat',
122
+ array( 'fields' => 'all' )
123
+ );
124
+ $content_category = array_values(
125
+ array_map(
126
+ function( $item ) {
127
+ return $item->name;
128
+ },
129
+ $category_path
130
+ )
131
+ );
132
+ $content_category_slice = array_slice( $content_category, -1 );
133
+ $categories =
134
+ empty( $content_category ) ? '""' : implode( ', ', $content_category );
135
+ return array(
136
+ 'name' => array_pop( $content_category_slice ),
137
+ 'categories' => $categories,
138
+ );
139
+ }
140
 
141
+ /**
142
+ * Returns content id to match on for Pixel fires.
143
+ *
144
+ * @access public
145
+ * @param WC_Product $woo_product
146
+ * @return array
147
+ */
148
+ public static function get_fb_content_ids( $woo_product ) {
149
+ return array( self::get_fb_retailer_id( $woo_product ) );
150
+ }
151
 
152
+ /**
153
+ * Clean up strings for FB Graph POSTing.
154
+ * This function should will:
155
+ * 1. Replace newlines chars/nbsp with a real space
156
+ * 2. strip_tags()
157
+ * 3. trim()
158
+ *
159
+ * @access public
160
+ * @param String string
161
+ * @return string
162
+ */
163
+ public static function clean_string( $string ) {
 
 
 
 
 
 
 
164
 
165
  /**
166
+ * Filters whether the shortcodes should be applied for a string when syncing a product or be stripped out.
167
+ *
168
+ * @since 2.6.19
169
  *
170
+ * @param bool $apply_shortcodes Shortcodes are applied if set to `true` and stripped out if set to `false`.
171
+ * @param string $string String to clean up.
172
  */
173
+ $apply_shortcodes = apply_filters( 'wc_facebook_string_apply_shortcodes', false, $string );
174
+ if ( $apply_shortcodes ) {
175
+ // Apply active shortcodes
176
+ $string = do_shortcode( $string );
177
+ } else {
178
+ // Strip out active shortcodes
179
+ $string = strip_shortcodes( $string );
180
  }
181
 
182
+ $string = str_replace( array( '&amp%3B', '&amp;' ), '&', $string );
183
+ $string = str_replace( array( "\r", '&nbsp;', "\t" ), ' ', $string );
184
+ $string = wp_strip_all_tags( $string, false ); // true == remove line breaks
185
+ return $string;
186
+ }
187
+
188
+ /**
189
+ * Returns flat array of woo IDs for variable products, or
190
+ * an array with a single woo ID for simple products.
191
+ *
192
+ * @access public
193
+ * @param WC_Product|WC_Facebook_Product $woo_product
194
+ * @return array
195
+ */
196
+ public static function get_product_array( $woo_product ) {
197
+ $result = [];
198
+ if ( self::is_variable_type( $woo_product->get_type() ) ) {
199
+ foreach ( $woo_product->get_children() as $item_id ) {
200
+ array_push( $result, $item_id );
201
  }
202
+ return $result;
203
+ } else {
204
+ return array( $woo_product->get_id() );
205
  }
206
+ }
207
 
208
+ /**
209
+ * Returns true if WooCommerce plugin found.
210
+ *
211
+ * @access public
212
+ * @return bool
213
+ */
214
+ public static function isWoocommerceIntegration() {
215
+ return class_exists( 'WooCommerce' );
216
+ }
217
+
218
+ /**
219
+ * Returns integration dependent name.
220
+ *
221
+ * @access public
222
+ * @return string
223
+ */
224
+ public static function getIntegrationName() {
225
+ if ( self::isWoocommerceIntegration() ) {
226
+ return 'WooCommerce';
227
+ } else {
228
+ return 'WordPress';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
+ }
231
 
232
+ /**
233
+ * Returns user info for the current WP user.
234
+ *
235
+ * @access public
236
+ * @param AAMSettings $aam_settings
237
+ * @return array
238
+ */
239
+ public static function get_user_info( $aam_settings ) {
240
+ $current_user = wp_get_current_user();
241
+ if ( 0 === $current_user->ID || $aam_settings == null || ! $aam_settings->get_enable_automatic_matching() ) {
242
+ // User not logged in or pixel not configured with automatic advance matching
243
+ return [];
244
+ } else {
245
+ // Keys documented in
246
+ // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
247
+ $user_data = array(
248
+ 'em' => $current_user->user_email,
249
+ 'fn' => $current_user->user_firstname,
250
+ 'ln' => $current_user->user_lastname,
251
+ 'external_id' => strval( $current_user->ID ),
252
  );
253
+ $user_id = $current_user->ID;
254
+ $user_data['ct'] = get_user_meta( $user_id, 'billing_city', true );
255
+ $user_data['zp'] = get_user_meta( $user_id, 'billing_postcode', true );
256
+ $user_data['country'] = get_user_meta( $user_id, 'billing_country', true );
257
+ $user_data['st'] = get_user_meta( $user_id, 'billing_state', true );
258
+ $user_data['ph'] = get_user_meta( $user_id, 'billing_phone', true );
259
+ // Each field that is not present in AAM settings or is empty is deleted from user data
260
+ foreach ( $user_data as $field => $value ) {
261
+ if ( $value === null || $value === ''
262
+ || ! in_array( $field, $aam_settings->get_enabled_automatic_matching_fields() )
263
+ ) {
264
+ unset( $user_data[ $field ] );
265
+ }
266
  }
267
+ // Country is a special case, it is returned as country in AAM settings
268
+ // But used as cn in pixel
269
+ if ( array_key_exists( 'country', $user_data ) ) {
270
+ $country = $user_data['country'];
271
+ $user_data['cn'] = $country;
272
+ unset( $user_data['country'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
+ $user_data = Normalizer::normalize_array( $user_data, true );
275
+ return $user_data;
276
  }
277
+ }
278
 
279
+ /**
280
+ * Utility function for development logging.
281
+ */
282
+ public static function fblog(
283
+ $message,
284
+ $object = [],
285
+ $error = false,
286
+ $ems = '' ) {
287
+ if ( $error ) {
288
+ $object['plugin_version'] = self::PLUGIN_VERSION;
289
+ $object['php_version'] = phpversion();
290
  }
291
+ $message = json_encode(
292
+ array(
293
+ 'message' => $message,
294
+ 'object' => $object,
295
+ )
296
+ );
297
+ $ems = $ems ?: self::$ems;
298
+ if ( $ems ) {
299
+ facebook_for_woocommerce()->get_api()->log( $ems, $message, $error );
300
+ } else {
301
+ error_log(
302
+ 'external merchant setting is null, something wrong here: ' .
303
+ $message
304
+ );
305
  }
306
+ }
307
 
308
+ /**
309
+ * Utility function for development Tip Events logging.
310
+ */
311
+ public static function tip_events_log( $tip_id, $channel_id, $event, $ems = '' ) {
312
+ $ems = $ems ?: self::$ems;
313
+ if ( $ems ) {
314
+ facebook_for_woocommerce()->get_api()->log_tip_event( $tip_id, $channel_id, $event );
315
+ } else {
316
+ error_log( 'external merchant setting is null' );
 
 
 
 
317
  }
318
+ }
319
 
320
+ public static function is_variation_type( $type ) {
321
+ return $type == 'variation' || $type == 'subscription_variation';
322
+ }
 
 
 
 
 
 
 
323
 
324
+ public static function is_variable_type( $type ) {
325
+ return $type == 'variable' || $type == 'variable-subscription';
326
+ }
327
+
328
+ public static function check_woo_ajax_permissions( $action_text, $die ) {
329
+ if ( ! current_user_can( 'manage_woocommerce' ) ) {
330
+ self::log(
331
+ 'Non manage_woocommerce user attempting to' . $action_text . '!',
332
+ [],
333
+ true
 
 
 
 
 
 
 
 
 
 
334
  );
335
+ if ( $die ) {
336
+ wp_die();
337
+ }
338
+ return false;
339
  }
340
+ return true;
341
+ }
342
 
343
+ /**
344
+ * Returns true if id is a positive non-zero integer
345
+ *
346
+ * @access public
347
+ * @param string $pixel_id
348
+ * @return bool
349
+ */
350
+ public static function is_valid_id( $pixel_id ) {
351
+ return isset( $pixel_id ) && is_numeric( $pixel_id ) && (int) $pixel_id > 0;
352
+ }
353
 
354
+ /**
355
+ * Helper function to query posts.
356
+ */
357
+ public static function get_wp_posts(
358
+ $product_group_id = null,
359
+ $compare_condition = null,
360
+ $post_type = 'product' ) {
361
+ $args = array(
362
+ 'fields' => 'ids',
363
+ 'meta_query' => array(
364
+ ( ( $product_group_id ) ?
365
+ array(
366
+ 'key' => $product_group_id,
367
+ 'compare' => $compare_condition,
368
+ ) : []
369
+ ),
370
+ ),
371
+ 'post_status' => 'publish',
372
+ 'post_type' => $post_type,
373
+ 'posts_per_page' => -1,
374
+ );
375
+ return get_posts( $args );
376
+ }
377
 
378
+ /**
379
+ * Helper log function for debugging
380
+ */
381
+ public static function log( $message ) {
 
382
 
383
+ // if this file is being included outside the plugin, or the plugin setting is disabled
384
+ if ( ! function_exists( 'facebook_for_woocommerce' ) || ! facebook_for_woocommerce()->get_integration()->is_debug_mode_enabled() ) {
385
+ return;
386
  }
387
 
388
+ if ( is_array( $message ) || is_object( $message ) ) {
389
+ $message = json_encode( $message );
390
+ } else {
391
+ $message = sanitize_textarea_field( $message );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  }
393
 
394
+ facebook_for_woocommerce()->log( $message );
395
+ }
396
 
397
+ // Return store name with sanitized apostrophe
398
+ public static function get_store_name() {
399
+ if ( self::$store_name ) {
400
+ return self::$store_name;
401
+ }
402
+ $name = trim(
403
+ str_replace(
404
+ "'",
405
+ "\u{2019}",
406
+ html_entity_decode(
407
+ get_bloginfo( 'name' ),
408
+ ENT_QUOTES,
409
+ 'UTF-8'
410
+ )
411
+ )
412
+ );
413
+ if ( $name ) {
414
+ self::$store_name = $name;
415
+ return $name;
416
+ }
417
+ // Fallback to site url
418
+ $url = get_site_url();
419
+ if ( $url ) {
420
+ self::$store_name = parse_url( $url, PHP_URL_HOST );
421
+ return self::$store_name;
422
+ }
423
+ // If site url doesn't exist, fall back to http host.
424
+ if ( $_SERVER['HTTP_HOST'] ) {
425
+ self::$store_name = $_SERVER['HTTP_HOST'];
426
+ return self::$store_name;
427
  }
428
 
429
+ // If http host doesn't exist, fall back to local host name.
430
+ $url = gethostname();
431
+ self::$store_name = $url;
432
+ return ( self::$store_name ) ? ( self::$store_name ) : 'A Store Has No Name';
433
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
+ /**
437
+ * Get visible name for variant attribute rather than the slug
438
+ *
439
+ * @param int $wp_id Post ID.
440
+ * @param string $label Attribute label.
441
+ * @param string $default_value Default value to use if the term has no name.
442
+ * @return string Term name or the default value.
443
+ */
444
+ public static function get_variant_option_name( $wp_id, $label, $default_value ) {
445
+ $meta = get_post_meta( $wp_id, $label, true );
446
+ $attribute_name = str_replace( 'attribute_', '', $label );
447
+ $term = get_term_by( 'slug', $meta, $attribute_name );
448
+ return $term && $term->name ? $term->name : $default_value;
449
+ }
450
 
451
+ /**
452
+ * Get all products for synchronization tasks.
453
+ *
454
+ * Warning: While changing this code please make sure that it scales properly.
455
+ * Sites with big product catalogs should not experience memory problems.
456
+ *
457
+ * @return array IDs of all product for synchronization.
458
+ */
459
+ public static function get_all_product_ids_for_sync() {
460
+ // Get all published products ids. This includes parent products of variations.
461
+ $product_args = array(
462
+ 'fields' => 'ids',
463
+ 'post_status' => 'publish',
464
+ 'post_type' => 'product',
465
+ 'posts_per_page' => -1,
466
+ );
467
+ $product_ids = get_posts( $product_args );
468
+
469
+ // Get all variations ids with their parents ids.
470
+ $variation_args = array(
471
+ 'fields' => 'id=>parent',
472
+ 'post_status' => 'publish',
473
+ 'post_type' => 'product_variation',
474
+ 'posts_per_page' => -1,
475
+ );
476
+ $variation_products = get_posts( $variation_args );
477
 
478
  /*
479
+ * Collect all parent products.
480
+ * Exclude variations which parents are not 'publish'.
481
  */
482
+ $parent_product_ids = [];
483
+ foreach ( $variation_products as $post_id => $parent_id ) {
484
+ /*
485
+ * Keep track of all parents to remove them from the list of products to sync.
486
+ * Use key to automatically remove duplicated items.
487
+ */
488
+ $parent_product_ids[ $parent_id ] = true;
489
 
490
+ // Include variations with published parents only.
491
+ if ( in_array( $parent_id, $product_ids ) ) {
492
+ $product_ids[] = $post_id;
493
  }
494
+ }
495
 
496
+ // Remove parent products because those can't be represented as Product Items.
497
+ return array_diff( $product_ids, array_keys( $parent_product_ids ) );
498
+ }
 
 
 
 
 
 
 
 
 
499
 
500
+
501
+ /*
502
+ * Change variant product field name from Woo taxonomy to FB name
503
+ */
504
+ public static function sanitize_variant_name( $name, $use_custom_data = true ) {
505
+ $name = str_replace( array( 'attribute_', 'pa_' ), '', strtolower( $name ) );
506
+
507
+ // British spelling
508
+ if ( $name === self::FB_VARIANT_COLOUR ) {
509
+ $name = self::FB_VARIANT_COLOR;
510
  }
511
 
512
+ if ( $use_custom_data ) {
513
+ switch ( $name ) {
514
+ case self::FB_VARIANT_SIZE:
515
+ case self::FB_VARIANT_COLOR:
516
+ case self::FB_VARIANT_GENDER:
517
+ case self::FB_VARIANT_PATTERN:
518
+ break;
519
+ default:
520
+ $name = 'custom_data:' . strtolower( $name );
521
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  }
 
523
  }
524
 
525
+ return $name;
526
+ }
 
527
 
528
+ public static function validateGender( $gender ) {
529
+ if ( $gender && ! isset( self::$validGenderArray[ $gender ] ) ) {
530
+ $first_char = strtolower( substr( $gender, 0, 1 ) );
531
+ // Men, Man, Boys
532
+ if ( $first_char === 'm' || $first_char === 'b' ) {
533
+ return 'male';
534
+ }
535
+ // Women, Woman, Female, Ladies
536
+ if ( $first_char === 'w' || $first_char === 'f' || $first_char === 'l' ) {
537
+ return 'female';
538
  }
539
+ if ( $first_char === 'u' ) {
540
+ return 'unisex';
 
 
541
  }
542
+ if ( strlen( $gender ) >= 3 ) {
543
+ $gender = strtolower( substr( $gender, 0, 3 ) );
544
+ if ( $gender === 'gir' || $gender === 'her' ) {
545
+ return 'female';
546
+ }
547
+ if ( $gender === 'him' || $gender === 'his' || $gender == 'guy' ) {
548
+ return 'male';
549
+ }
550
  }
551
+ return null;
552
  }
553
+ return $gender;
554
+ }
555
 
556
+ public static function get_fbid_post_meta( $wp_id, $fbid_type ) {
557
+ return get_post_meta( $wp_id, $fbid_type, true );
558
+ }
 
 
 
 
559
 
560
+ public static function is_all_caps( $value ) {
561
+ if ( $value === null || $value === '' ) {
562
+ return true;
563
+ }
564
+ if ( preg_match( '/[^\\p{Common}\\p{Latin}]/u', $value ) ) {
565
+ // Contains non-western characters
566
+ // So, it can't be all uppercase
567
+ return false;
568
  }
569
+ $latin_string = preg_replace( '/[^\\p{Latin}]/u', '', $value );
570
+ if ( $latin_string === '' ) {
571
+ // Symbols only
572
+ return true;
573
+ }
574
+ return strtoupper( $latin_string ) === $latin_string;
575
+ }
576
 
577
+ public static function decode_json( $json_string, $assoc = false ) {
578
+ // Plugin requires 5.6.0 but for some user use 5.5.9 JSON_BIGINT_AS_STRING
579
+ // will cause 502 issue when redirect.
580
+ return version_compare( phpversion(), '5.6.0' ) >= 0
581
+ ? json_decode( $json_string, $assoc, 512, JSON_BIGINT_AS_STRING )
582
+ : json_decode( $json_string, $assoc, 512 );
583
+ }
584
+
585
+ public static function set_test_fail_reason( $msg, $trace ) {
586
+ $reason_msg = get_transient( 'facebook_plugin_test_fail' );
587
+ if ( $reason_msg ) {
588
+ $msg = $reason_msg . PHP_EOL . $msg;
589
  }
590
+ set_transient( 'facebook_plugin_test_fail', $msg );
591
+ set_transient( 'facebook_plugin_test_stack_trace', $trace );
592
+ }
593
 
594
+ /**
595
+ * Helper function to check time cap.
596
+ */
597
+ public static function check_time_cap( $from, $date_cap ) {
598
+ if ( $from == null ) {
599
+ return true;
600
  }
601
+ $now = new DateTime( current_time( 'mysql' ) );
602
+ $diff_in_day = $now->diff( new DateTime( $from ) )->format( '%a' );
603
+ return is_numeric( $diff_in_day ) && (int) $diff_in_day > $date_cap;
604
  }
605
 
606
+ public static function get_cached_best_tip() {
607
+ $cached_best_tip = self::decode_json(
608
+ get_option( 'fb_info_banner_last_best_tip', '' )
609
+ );
610
+ return $cached_best_tip;
611
+ }
612
+ }
includes/fbwpml.php CHANGED
@@ -9,156 +9,150 @@
9
  * @package FacebookCommerce
10
  */
11
 
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- if ( ! class_exists( 'WC_Facebook_WPML_Injector' ) ) :
17
 
18
- class FB_WPML_Language_Status {
19
- const VISIBLE = 1;
20
- const HIDDEN = 2;
21
- const NOT_SYNCED = 0;
22
- }
23
 
24
- class WC_Facebook_WPML_Injector {
25
- public static $settings = null;
26
- public static $default_lang = null;
27
- const OPTION = 'fb_wmpl_language_visibility';
28
 
29
 
30
- /**
31
- * Constructor for WC_Facebook_WPML_Injector class.
32
- */
33
- public function __construct() {
34
 
35
- self::$settings = get_option( self::OPTION );
36
- self::$default_lang = apply_filters( 'wpml_default_language', null );
37
 
38
- if ( is_admin() ) {
39
- add_action( 'icl_menu_footer', array( $this, 'wpml_support' ) );
40
- add_action( 'icl_ajx_custom_call', array( $this, 'wpml_ajax_support' ), 10, 2 );
41
- }
42
  }
 
43
 
44
 
45
- public static function should_hide( $wp_id ) {
46
- $product_lang = apply_filters( 'wpml_post_language_details', null, $wp_id );
47
- $settings = self::$settings;
48
- if ( $product_lang && isset( $product_lang['language_code'] ) ) {
49
- $product_lang = $product_lang['language_code'];
50
- }
51
 
52
- // Option doesn't exist : Backwards Compatibility
53
- if ( ! $settings ) {
54
- return ( $product_lang && self::$default_lang !== $product_lang );
55
- }
56
- // Hide products from non-active languages.
57
- if ( ! isset( $settings[ $product_lang ] ) ) {
58
- return true;
59
- }
60
- return $settings[ $product_lang ] !== FB_WPML_Language_Status::VISIBLE;
61
  }
 
 
62
 
63
- public function wpml_ajax_support( $call, $REQUEST ) {
64
- global $sitepress;
65
- if ( isset( $REQUEST['icl_ajx_action'] ) ) {
66
- $call = $REQUEST['icl_ajx_action'];
 
 
 
 
 
 
 
67
  }
68
- if ( $call === 'icl_fb_woo' ) {
69
- $active_languages = array_keys( $sitepress->get_active_languages() );
70
- $settings = array();
71
- foreach ( $active_languages as $lang ) {
72
- $settings[ $lang ] = $REQUEST[ $lang ] === 'on' ?
73
- FB_WPML_Language_Status::VISIBLE : FB_WPML_Language_Status::HIDDEN;
74
- }
75
 
76
- update_option( 'fb_wmpl_language_visibility', $settings, false );
77
- self::$settings = $settings;
78
- }
79
  }
 
80
 
81
 
82
- /**
83
- * Prints the content for Facebook Visibility section.
84
- *
85
- * The section is shown at the bottom of the WPML > Languages settings page.
86
- */
87
- public function wpml_support() {
88
- /** @var object $sitepress */
89
- global $sitepress;
90
 
91
- // there is no nonce to check here and the value of $_GET['page] is being compared against a known and safe string
92
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
93
- if ( isset( $_GET['page'] ) && false !== strpos( esc_url_raw( wp_unslash( $_GET['page'] ) ), 'languages.php' ) ) {
94
 
95
- /** @var array $active_languages */
96
- $active_languages = $sitepress->get_active_languages();
97
- $settings = get_option( self::OPTION );
98
 
99
- // Default setting is only show default lang.
100
- if ( ! $settings ) {
101
 
102
- $settings = array_fill_keys(
103
- array_keys( $active_languages ),
104
- FB_WPML_Language_Status::HIDDEN
105
- );
106
 
107
- if ( self::$default_lang ) {
108
- $settings[ self::$default_lang ] = FB_WPML_Language_Status::VISIBLE;
109
- }
110
  }
 
111
 
112
- ?>
113
- <div id="lang-sec-fb" class="wpml-section wpml-section-languages">
114
- <div class="wpml-section-header">
115
- <h3><?php esc_html_e( 'Facebook Visibility', 'facebook-for-woocommerce' ); ?></h3>
116
- </div>
117
- <div class="wpml-section-content">
118
- <?php esc_html_e( 'WooCommerce Products with languages that are selected here will be visible to customers who see your Facebook Shop.', 'facebook-for-woocommerce' ); ?>
119
-
120
- <div class="wpml-section-content-inner">
121
- <form id="icl_fb_woo" name="icl_fb_woo" action="">
122
-
123
- <?php foreach ( $settings as $language => $set ) : ?>
124
-
125
- <p>
126
- <label>
127
- <input type="checkbox" id="icl_fb_woo_chk" name="<?php echo esc_attr( $language ); ?>" <?php checked( $set, FB_WPML_Language_Status::VISIBLE ); ?>>
128
- <?php echo isset( $active_languages[ $language ]['native_name'] ) ? esc_html( $active_languages[ $language ]['native_name'] ) : esc_html( $language ); ?>
129
- </label>
130
- </p>
131
 
132
- <?php endforeach; ?>
 
133
 
134
- <?php // TODO: restore success message removed in e7f290f when FBE 2.0 changes are available {WV 2020-04-27} ?>
135
- <p class="icl_ajx_response_fb" id="icl_ajx_response_fb" hidden="true"><?php esc_html_e( "Saved. An automated sync from Facebook will run every hour to update the catalog with any changes you've made.", 'facebook-for-woocommerce' ); ?></p>
136
 
137
- <p class="buttons-wrap">
138
- <input
139
- class="button button-primary"
140
- name="save"
141
- value="<?php esc_attr_e( 'Save', 'sitepress' ); ?>"
142
- type="submit"
143
- />
144
  </p>
145
- </form>
146
- <script type="text/javascript">
147
- addLoadEvent( function() {
148
- jQuery( '#icl_fb_woo' ).submit( iclSaveForm );
149
- jQuery( '#icl_fb_woo' ).submit( function() {
150
- jQuery( '#icl_ajx_response_fb' ).show();
151
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  } );
153
- </script>
154
- </div>
155
  </div>
156
  </div>
157
- <?php
158
- }
159
  }
160
-
161
-
162
  }
163
 
164
- endif;
 
9
  * @package FacebookCommerce
10
  */
11
 
12
+ defined( 'ABSPATH' ) || exit;
 
 
 
 
13
 
14
+ class FB_WPML_Language_Status {
15
+ const VISIBLE = 1;
16
+ const HIDDEN = 2;
17
+ const NOT_SYNCED = 0;
18
+ }
19
 
20
+ class WC_Facebook_WPML_Injector {
21
+ public static $settings = null;
22
+ public static $default_lang = null;
23
+ const OPTION = 'fb_wmpl_language_visibility';
24
 
25
 
26
+ /**
27
+ * Constructor for WC_Facebook_WPML_Injector class.
28
+ */
29
+ public function __construct() {
30
 
31
+ self::$settings = get_option( self::OPTION );
32
+ self::$default_lang = apply_filters( 'wpml_default_language', null );
33
 
34
+ if ( is_admin() ) {
35
+ add_action( 'icl_menu_footer', array( $this, 'wpml_support' ) );
36
+ add_action( 'icl_ajx_custom_call', array( $this, 'wpml_ajax_support' ), 10, 2 );
 
37
  }
38
+ }
39
 
40
 
41
+ public static function should_hide( $wp_id ) {
42
+ $product_lang = apply_filters( 'wpml_post_language_details', null, $wp_id );
43
+ $settings = self::$settings;
44
+ if ( $product_lang && isset( $product_lang['language_code'] ) ) {
45
+ $product_lang = $product_lang['language_code'];
46
+ }
47
 
48
+ // Option doesn't exist : Backwards Compatibility
49
+ if ( ! $settings ) {
50
+ return ( $product_lang && self::$default_lang !== $product_lang );
51
+ }
52
+ // Hide products from non-active languages.
53
+ if ( ! isset( $settings[ $product_lang ] ) ) {
54
+ return true;
 
 
55
  }
56
+ return $settings[ $product_lang ] !== FB_WPML_Language_Status::VISIBLE;
57
+ }
58
 
59
+ public function wpml_ajax_support( $call, $REQUEST ) {
60
+ global $sitepress;
61
+ if ( isset( $REQUEST['icl_ajx_action'] ) ) {
62
+ $call = $REQUEST['icl_ajx_action'];
63
+ }
64
+ if ( $call === 'icl_fb_woo' ) {
65
+ $active_languages = array_keys( $sitepress->get_active_languages() );
66
+ $settings = array();
67
+ foreach ( $active_languages as $lang ) {
68
+ $settings[ $lang ] = $REQUEST[ $lang ] === 'on' ?
69
+ FB_WPML_Language_Status::VISIBLE : FB_WPML_Language_Status::HIDDEN;
70
  }
 
 
 
 
 
 
 
71
 
72
+ update_option( 'fb_wmpl_language_visibility', $settings, false );
73
+ self::$settings = $settings;
 
74
  }
75
+ }
76
 
77
 
78
+ /**
79
+ * Prints the content for Facebook Visibility section.
80
+ *
81
+ * The section is shown at the bottom of the WPML > Languages settings page.
82
+ */
83
+ public function wpml_support() {
84
+ /** @var object $sitepress */
85
+ global $sitepress;
86
 
87
+ // there is no nonce to check here and the value of $_GET['page] is being compared against a known and safe string
88
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
89
+ if ( isset( $_GET['page'] ) && false !== strpos( esc_url_raw( wp_unslash( $_GET['page'] ) ), 'languages.php' ) ) {
90
 
91
+ /** @var array $active_languages */
92
+ $active_languages = $sitepress->get_active_languages();
93
+ $settings = get_option( self::OPTION );
94
 
95
+ // Default setting is only show default lang.
96
+ if ( ! $settings ) {
97
 
98
+ $settings = array_fill_keys(
99
+ array_keys( $active_languages ),
100
+ FB_WPML_Language_Status::HIDDEN
101
+ );
102
 
103
+ if ( self::$default_lang ) {
104
+ $settings[ self::$default_lang ] = FB_WPML_Language_Status::VISIBLE;
 
105
  }
106
+ }
107
 
108
+ ?>
109
+ <div id="lang-sec-fb" class="wpml-section wpml-section-languages">
110
+ <div class="wpml-section-header">
111
+ <h3><?php esc_html_e( 'Facebook Visibility', 'facebook-for-woocommerce' ); ?></h3>
112
+ </div>
113
+ <div class="wpml-section-content">
114
+ <?php esc_html_e( 'WooCommerce Products with languages that are selected here will be visible to customers who see your Facebook Shop.', 'facebook-for-woocommerce' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ <div class="wpml-section-content-inner">
117
+ <form id="icl_fb_woo" name="icl_fb_woo" action="">
118
 
119
+ <?php foreach ( $settings as $language => $set ) : ?>
 
120
 
121
+ <p>
122
+ <label>
123
+ <input type="checkbox" id="icl_fb_woo_chk" name="<?php echo esc_attr( $language ); ?>" <?php checked( $set, FB_WPML_Language_Status::VISIBLE ); ?>>
124
+ <?php echo isset( $active_languages[ $language ]['native_name'] ) ? esc_html( $active_languages[ $language ]['native_name'] ) : esc_html( $language ); ?>
125
+ </label>
 
 
126
  </p>
127
+
128
+ <?php endforeach; ?>
129
+
130
+ <?php // TODO: restore success message removed in e7f290f when FBE 2.0 changes are available {WV 2020-04-27} ?>
131
+ <p class="icl_ajx_response_fb" id="icl_ajx_response_fb" hidden="true"><?php esc_html_e( "Saved. An automated sync from Facebook will run every hour to update the catalog with any changes you've made.", 'facebook-for-woocommerce' ); ?></p>
132
+
133
+ <p class="buttons-wrap">
134
+ <input
135
+ class="button button-primary"
136
+ name="save"
137
+ value="<?php esc_attr_e( 'Save', 'sitepress' ); ?>"
138
+ type="submit"
139
+ />
140
+ </p>
141
+ </form>
142
+ <script type="text/javascript">
143
+ addLoadEvent( function() {
144
+ jQuery( '#icl_fb_woo' ).submit( iclSaveForm );
145
+ jQuery( '#icl_fb_woo' ).submit( function() {
146
+ jQuery( '#icl_ajx_response_fb' ).show();
147
  } );
148
+ } );
149
+ </script>
150
  </div>
151
  </div>
152
+ </div>
153
+ <?php
154
  }
 
 
155
  }
156
 
157
+
158
+ }
includes/test/facebook-integration-test.php DELETED
@@ -1,548 +0,0 @@
1
- <?php
2
- // phpcs:ignoreFile
3
- /**
4
- * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
5
- *
6
- * This source code is licensed under the license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @package FacebookCommerce
10
- *
11
- * usage:
12
- * 1. set WP_DEBUG = true and WP_DEBUG_DISPLAY = false
13
- * 2. append "&fb_test_product_sync=true" to the url when you are on facebook-for-woocommerce setting pages
14
- * 3. refresh the page to launch test
15
- * https://codex.wordpress.org/WP_DEBUG
16
- */
17
-
18
- if ( ! defined( 'ABSPATH' ) ) {
19
- exit;
20
- }
21
-
22
- require_once dirname( __DIR__ ) . '/fbutils.php';
23
- require_once 'fbproductfeed-test.php';
24
-
25
- if ( ! class_exists( 'WC_Facebook_Integration_Test' ) ) :
26
-
27
- /**
28
- * This tests the upload of test objects into Facebook using the plugin's
29
- * infrastructure and checks to see if the product field have been correctly
30
- * uploaded into FB.
31
- */
32
- class WC_Facebook_Integration_Test {
33
-
34
- const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
35
- const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
36
- const MAX_SLEEP_IN_SEC = 90;
37
- const MAX_TIME = 'T23:59+00:00';
38
- const MIN_TIME = 'T00:00+00:00';
39
- /** Class Instance */
40
- private static $instance;
41
-
42
- /** @var WC_Facebookcommerce_Integration full integration object */
43
- public static $commerce = null;
44
- /** @var WC_Facebookcommerce_Graph_API */
45
- public static $fbgraph = null;
46
- public static $test_mode = false;
47
-
48
- // simple products' id and variable products' parent_id
49
- public static $wp_post_ids = array();
50
- // FB product item retailer id.
51
- public static $retailer_ids = array();
52
- // product and product_variation post id for test
53
- public $product_post_wpid = null;
54
- public static $test_pass = 1;
55
-
56
- /**
57
- * Get the class instance
58
- */
59
- public static function get_instance( $commerce ) {
60
- return null === self::$instance
61
- ? ( self::$instance = new self( $commerce ) )
62
- : self::$instance;
63
- }
64
-
65
- /**
66
- * Constructor
67
- */
68
- public function __construct( $commerce ) {
69
-
70
- self::$commerce = $commerce;
71
-
72
- }
73
-
74
- function check_product_create() {
75
- if ( count( self::$retailer_ids ) < 3 ) {
76
- WC_Facebookcommerce_Utils::log( 'Test - Failed to create 3 product items.' );
77
- WC_Facebookcommerce_Utils::set_test_fail_reason(
78
- 'Failed to create 3 product items.',
79
- ( new Exception() )->getTraceAsString()
80
- );
81
- return false;
82
- }
83
-
84
- if ( count( self::$retailer_ids ) > 3 ) {
85
- WC_Facebookcommerce_Utils::log(
86
- 'Test - Failed to skip invisible products.'
87
- );
88
- WC_Facebookcommerce_Utils::set_test_fail_reason(
89
- 'Failed to skip invisible products.',
90
- ( new Exception() )->getTraceAsString()
91
- );
92
- return false;
93
- }
94
-
95
- // Check 3 products have been created.
96
- for ( $i = 0; $i < 3; $i++ ) {
97
- $product_type = $i == 0 ? 'Simple' : 'Variable';
98
- $retailer_id = self::$retailer_ids[ $i ];
99
- $item_fbid =
100
- $this->check_fbid_api( self::FB_PRODUCT_ITEM_ID, $retailer_id );
101
- $group_fbid =
102
- $this->check_fbid_api( self::FB_PRODUCT_GROUP_ID, $retailer_id );
103
- if ( ! $item_fbid || ! $group_fbid ) {
104
- WC_Facebookcommerce_Utils::log(
105
- 'Test - ' . $product_type .
106
- ' product failed to create.'
107
- );
108
- WC_Facebookcommerce_Utils::set_test_fail_reason(
109
- $product_type .
110
- ' product failed to create.',
111
- ( new Exception() )->getTraceAsString()
112
- );
113
- return false;
114
- }
115
- }
116
-
117
- // Check product detailed as expected.
118
- $data = array(
119
- 'name' => 'a simple product for test',
120
- 'price' => '20.00',
121
- 'description' => 'This is to test a simple product.',
122
- 'sale_price' => '10.00',
123
- 'sale_price_dates_from' =>
124
- date_i18n( 'Y-m-d', strtotime( 'now' ) ) . self::MIN_TIME,
125
- 'sale_price_dates_to' =>
126
- date_i18n( 'Y-m-d', strtotime( '+10 day' ) ) . self::MAX_TIME,
127
- 'visibility' => 'published',
128
- );
129
- $simple_product_result =
130
- $this->check_product_info( self::$retailer_ids[0], false, $data );
131
- if ( ! $simple_product_result ) {
132
- WC_Facebookcommerce_Utils::log(
133
- 'Test - Simple product failed to match ' .
134
- 'product details.'
135
- );
136
- WC_Facebookcommerce_Utils::set_test_fail_reason(
137
- 'Simple product failed to'
138
- . ' match product details.',
139
- ( new Exception() )->getTraceAsString()
140
- );
141
- return false;
142
- }
143
-
144
- $data = array(
145
- 'name' => 'a variable product for test',
146
- 'price' => '30.00',
147
- 'description' => 'This is to test a variable product. - Red',
148
- 'additional_variant_attributes' => array( 'value' => 'Red' ),
149
- 'visibility' => 'published',
150
- );
151
- $variable_product_result =
152
- $this->check_product_info( self::$retailer_ids[1], true, $data );
153
- if ( ! $variable_product_result ) {
154
- WC_Facebookcommerce_Utils::log(
155
- 'Test - Variable product failed to match product details.'
156
- );
157
- WC_Facebookcommerce_Utils::set_test_fail_reason(
158
- 'Variable product failed to match product details.',
159
- ( new Exception() )->getTraceAsString()
160
- );
161
- return false;
162
- }
163
- return true;
164
- }
165
-
166
- function check_fbid_api( $fbid_type, $fb_retailer_id ) {
167
- $product_fbid_result = self::$fbgraph->get_facebook_id(
168
- self::$commerce->get_product_catalog_id(),
169
- $fb_retailer_id,
170
- true
171
- );
172
-
173
- if ( is_wp_error( $product_fbid_result ) ) {
174
- WC_Facebookcommerce_Utils::log(
175
- 'Test - ' . $product_fbid_result->get_error_message()
176
- );
177
- WC_Facebookcommerce_Utils::set_test_fail_reason(
178
- 'There was an issue connecting to the Facebook API: ' .
179
- $product_fbid_result->get_error_message(),
180
- ( new Exception() )->getTraceAsString()
181
- );
182
- return false;
183
- }
184
-
185
- if ( $product_fbid_result && isset( $product_fbid_result['body'] ) ) {
186
- $body = WC_Facebookcommerce_Utils::decode_json(
187
- $product_fbid_result['body'],
188
- true
189
- );
190
- if ( $body && isset( $body['id'] ) ) {
191
- if ( $fbid_type == self::FB_PRODUCT_GROUP_ID ) {
192
- $fb_id =
193
- isset( $body['product_group'] )
194
- ? $body['product_group']['id']
195
- : false;
196
- } else {
197
- $fb_id = $body['id'];
198
- }
199
- return $fb_id;
200
- }
201
- }
202
-
203
- return false;
204
- }
205
-
206
- function check_product_info( $retailer_id, $has_variant, $data ) {
207
- $prod_info_result = self::$fbgraph->check_product_info(
208
- self::$commerce->get_product_catalog_id(),
209
- $retailer_id,
210
- $has_variant
211
- );
212
- if ( is_wp_error( $prod_info_result ) ) {
213
- WC_Facebookcommerce_Utils::log(
214
- 'Test - ' . $prod_info_result->get_error_message()
215
- );
216
- WC_Facebookcommerce_Utils::set_test_fail_reason(
217
- 'There was an issue connecting to the Facebook API: ' .
218
- $prod_info_result->get_error_message(),
219
- ( new Exception() )->getTraceAsString()
220
- );
221
- return false;
222
- }
223
-
224
- $match = true;
225
- if ( $prod_info_result && isset( $prod_info_result['body'] ) ) {
226
- $body = WC_Facebookcommerce_Utils::decode_json(
227
- $prod_info_result['body'],
228
- true
229
- );
230
- if ( ! $body ) {
231
- return false;
232
- }
233
- if ( $body['name'] != $data['name'] ) {
234
- WC_Facebookcommerce_Utils::log(
235
- 'Test - ' . $retailer_id . " doesn\'t match name."
236
- );
237
- $match = false;
238
- }
239
-
240
- if ( $body['description'] != $data['description'] ) {
241
- WC_Facebookcommerce_Utils::log(
242
- 'Test - ' . $retailer_id . " doesn\'t match description."
243
- );
244
- $match = false;
245
- }
246
- // Woo doesn't have API to return currency symbol.
247
- // FB graph API only support to response with a currency symbol price.
248
- // No php built-in function to support cast html number to symbol.
249
- // Compare numeric price only.
250
- $price = floatval( preg_replace( '/[^\d\.]+/', '', $body['price'] ) );
251
- if ( $price != $data['price'] ) {
252
- WC_Facebookcommerce_Utils::log(
253
- 'Test - ' . $retailer_id . " doesn\'t match price."
254
- );
255
- $match = false;
256
- }
257
- // Check sale price and dates.
258
- if ( isset( $data['sale_price'] ) ) {
259
- $sale_price = floatval(
260
- preg_replace( '/[^\d\.]+/', '', $body['sale_price'] )
261
- );
262
- if ( $sale_price != $data['sale_price'] ) {
263
- WC_Facebookcommerce_Utils::log(
264
- 'Test - ' . $retailer_id . " doesn\'t match sale price."
265
- );
266
- $match = false;
267
- }
268
- if ( $body['sale_price_start_date'] != $data['sale_price_dates_from'] ) {
269
- WC_Facebookcommerce_Utils::log(
270
- 'Test - ' . $retailer_id . " doesn\'t match sale price start date"
271
- );
272
- $match = false;
273
- }
274
- if ( $body['sale_price_end_date'] != $data['sale_price_dates_to'] ) {
275
- WC_Facebookcommerce_Utils::log(
276
- 'Test - ' . $retailer_id . " doesn\'t match sale price end date."
277
- );
278
- $match = false;
279
- }
280
- }
281
-
282
- if ( $body['visibility'] != $data['visibility'] ) {
283
- WC_Facebookcommerce_Utils::log(
284
- 'Test - ' . $retailer_id . " doesn\'t match visibility."
285
- );
286
- $match = false;
287
- }
288
-
289
- if ( $has_variant &&
290
- ( ! isset( $body['additional_variant_attributes'] ) ||
291
- $body['additional_variant_attributes'][0]['value'] !=
292
- $data['additional_variant_attributes']['value'] ) ) {
293
-
294
- WC_Facebookcommerce_Utils::log(
295
- 'Test - ' . $retailer_id . " doesn\'t match variation."
296
- );
297
- $match = false;
298
- }
299
- }
300
- return $match;
301
- }
302
-
303
- // Don't early return to prevent haunting product id.
304
- function clean_up() {
305
- $failure = false;
306
- foreach ( self::$wp_post_ids as $post_id ) {
307
- $delete_post_result = wp_delete_post( $post_id );
308
- // return false or null if failed.
309
- if ( ! $delete_post_result ) {
310
- WC_Facebookcommerce_Utils::log( 'Test - Fail to delete post ' . $post_id );
311
- WC_Facebookcommerce_Utils::set_test_fail_reason(
312
- 'Fail to delete post ' . $post_id,
313
- ( new Exception() )->getTraceAsString()
314
- );
315
- $failure = true;
316
- }
317
- }
318
- self::$wp_post_ids = array();
319
-
320
- $this->sleep_til_upload_complete( 60 );
321
- foreach ( self::$retailer_ids as $retailer_id ) {
322
- $item_fbid =
323
- $this->check_fbid_api( self::FB_PRODUCT_ITEM_ID, $retailer_id );
324
- $group_fbid =
325
- $this->check_fbid_api( self::FB_PRODUCT_GROUP_ID, $retailer_id );
326
- if ( $item_fbid || $group_fbid ) {
327
- WC_Facebookcommerce_Utils::log(
328
- 'Test - Failed to delete product ' .
329
- $retailer_id . ' via plugin deletion hook.'
330
- );
331
- WC_Facebookcommerce_Utils::set_test_fail_reason(
332
- 'Failed to delete product ' . $retailer_id .
333
- ' via plugin deletion hook.',
334
- ( new Exception() )->getTraceAsString()
335
- );
336
- $failure = true;
337
- }
338
- }
339
- self::$retailer_ids = array();
340
-
341
- return ! $failure;
342
- }
343
-
344
- function create_data() {
345
- $prod_and_variant_wpid = array();
346
- // Gets term object from Accessories in the database.
347
- $term = get_term_by( 'name', 'Accessories', 'product_cat' );
348
- // Accessories should be a default category.
349
- // If not exist, set categories term first.
350
- if ( ! $term ) {
351
- $term = wp_insert_term(
352
- 'Accessories', // the term
353
- 'product_cat', // the taxonomy
354
- array(
355
- 'slug' => 'accessories',
356
- )
357
- );
358
- }
359
- $data = array(
360
- 'post_content' => 'This is to test a simple product.',
361
- 'post_title' => 'a simple product for test',
362
- 'post_status' => 'publish',
363
- 'post_type' => 'product',
364
- 'term' => $term,
365
- 'price' => 20,
366
- 'sale_price' => 10,
367
- 'sale_price_dates_from' => strtotime( 'now' ),
368
- 'sale_price_dates_to' => strtotime( '+10 day' ),
369
- );
370
- $simple_product_result =
371
- $this->create_test_simple_product( $data, $prod_and_variant_wpid );
372
-
373
- if ( ! $simple_product_result ) {
374
- return false;
375
- }
376
-
377
- // Test an invisible product - invisible products won't be synced by feed.
378
- $data['visibility'] = false;
379
- $simple_product_result =
380
- $this->create_test_simple_product( $data, $prod_and_variant_wpid );
381
-
382
- if ( ! $simple_product_result ) {
383
- return false;
384
- }
385
-
386
- $data['post_content'] = 'This is to test a variable product.';
387
- $data['post_title'] = 'a variable product for test';
388
- $data['price'] = 30;
389
-
390
- // Test variable products.
391
- $variable_product_result =
392
- $this->create_test_variable_product( $data, $prod_and_variant_wpid );
393
- if ( ! $variable_product_result ) {
394
- return false;
395
- }
396
- return $prod_and_variant_wpid;
397
- }
398
-
399
- function create_test_simple_product( $data, &$prod_and_variant_wpid ) {
400
- $post_id = $this->fb_insert_post( $data, 'Simple' );
401
- if ( ! $post_id ) {
402
- return false;
403
- }
404
- array_push( $prod_and_variant_wpid, $post_id );
405
- update_post_meta( $post_id, '_regular_price', $data['price'] );
406
- update_post_meta( $post_id, '_sale_price', $data['sale_price'] );
407
- update_post_meta( $post_id, '_sale_price_dates_from', $data['sale_price_dates_from'] );
408
- update_post_meta( $post_id, '_sale_price_dates_to', $data['sale_price_dates_to'] );
409
-
410
- wp_set_object_terms( $post_id, 'simple', 'product_type' );
411
- // Invisible products won't be synced by feed.
412
- if ( isset( $data['visibility'] ) ) {
413
- $terms = array( 'exclude-from-catalog', 'exclude-from-search' );
414
- wp_set_object_terms( $post_id, $terms, 'product_visibility' );
415
- } else {
416
- array_push( self::$wp_post_ids, $post_id );
417
- array_push( self::$retailer_ids, 'wc_post_id_' . $post_id );
418
- }
419
-
420
- $product = wc_get_product( $post_id );
421
- $product->set_stock_status( 'instock' );
422
- wp_set_object_terms( $post_id, $data['term']->term_id, 'product_cat' );
423
- return true;
424
- }
425
-
426
- function create_test_variable_product( $data, &$prod_and_variant_wpid ) {
427
- $post_id = $this->fb_insert_post( $data, 'Variable' );
428
- if ( ! $post_id ) {
429
- return false;
430
- }
431
-
432
- wp_set_object_terms( $post_id, 'variable', 'product_type' );
433
- array_push( $prod_and_variant_wpid, $post_id );
434
- array_push( self::$wp_post_ids, $post_id );
435
- // Gets term object from Accessories in the database.
436
- $term = get_term_by( 'name', 'Accessories', 'product_cat' );
437
- wp_set_object_terms( $post_id, $term->term_id, 'product_cat' );
438
-
439
- // Set up attributes.
440
- $avail_attribute_values = array(
441
- 'Red',
442
- 'Blue',
443
- );
444
- wp_set_object_terms( $post_id, $avail_attribute_values, 'pa_color' );
445
- $thedata = array(
446
- 'pa_color' => array(
447
- 'name' => 'pa_color',
448
- 'value' => '',
449
- 'is_visible' => '1',
450
- 'is_variation' => '1',
451
- 'is_taxonomy' => '1',
452
- ),
453
- );
454
- update_post_meta( $post_id, '_product_attributes', $thedata );
455
-
456
- // Insert variations.
457
- $variation_data = array(
458
- 'post_content' => 'This is to test a variable product. - Red',
459
- 'post_status' => 'publish',
460
- 'post_type' => 'product_variation',
461
- 'post_parent' => $post_id,
462
- 'price' => 30,
463
- );
464
- $variation_red = $this->fb_insert_post( $variation_data, 'Variation' );
465
- if ( ! $variation_red ) {
466
- return;
467
- }
468
-
469
- $this->fb_update_variation_meta(
470
- $prod_and_variant_wpid,
471
- $variation_red,
472
- 'Red',
473
- $variation_data
474
- );
475
-
476
- $variation_data['post_content'] = 'a variable product for test - Blue';
477
- $variation_blue = $this->fb_insert_post( $variation_data, 'Variatoin' );
478
- if ( ! $variation_blue ) {
479
- return false;
480
- }
481
- $this->fb_update_variation_meta(
482
- $prod_and_variant_wpid,
483
- $variation_blue,
484
- 'Blue',
485
- $variation_data
486
- );
487
- $product = wc_get_product( $variation_blue );
488
- $product->set_stock_status( 'instock' );
489
- wp_set_object_terms( $variation_blue, 'variation', 'product_type' );
490
- return true;
491
- }
492
-
493
- function fb_update_variation_meta(
494
- &$prod_and_variant_wpid,
495
- $variation_id,
496
- $value,
497
- $data ) {
498
- array_push( $prod_and_variant_wpid, $variation_id );
499
- array_push( self::$retailer_ids, 'wc_post_id_' . $variation_id );
500
-
501
- $attribute_term = get_term_by( 'name', $value, 'pa_color' );
502
-
503
- update_post_meta( $variation_id, 'attribute_pa_color', $attribute_term->slug );
504
- update_post_meta( $variation_id, '_price', $data['price'] );
505
- update_post_meta( $variation_id, '_regular_price', $data['price'] );
506
- wp_set_object_terms( $variation_id, 'variation', 'product_type' );
507
- $product = wc_get_product( $variation_id );
508
- $product->set_stock_status( 'instock' );
509
- }
510
-
511
- function fb_insert_post( $data, $p_type ) {
512
- $postarr = array_intersect_key(
513
- $data,
514
- array_flip(
515
- array(
516
- 'post_content',
517
- 'post_title',
518
- 'post_status',
519
- 'post_type',
520
- 'post_parent',
521
- )
522
- )
523
- );
524
- $post_id = wp_insert_post( $postarr );
525
- if ( is_wp_error( $post_id ) ) {
526
- WC_Facebookcommerce_Utils::log(
527
- 'Test - ' . $p_type .
528
- ' product wp_insert_post' . 'failed: ' . json_encode( $post_id )
529
- );
530
- return false;
531
- } else {
532
- return $post_id;
533
- }
534
- }
535
-
536
- /**
537
- * IMPORTANT! Wait for Ents creation and prevent race condition.
538
- **/
539
- function sleep_til_upload_complete( $sec ) {
540
- sleep( $sec );
541
- }
542
-
543
- function set_product_wpid( $product_post_wpid ) {
544
- WC_Facebook_Product_Feed_Test_Mock::$product_post_wpid = $product_post_wpid;
545
- }
546
- }
547
-
548
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: 6.1
6
- Stable tag: 2.6.30
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
@@ -39,54 +39,63 @@ When opening a bug on GitHub, please give us as many details as possible.
39
 
40
  == Changelog ==
41
 
42
- = 2.6.30 - 2022-11-09 =
43
- * Fix - Add backward compatibility for WC 6.1, 6.2, and 6.3 versions.
44
- * Fix - Sync product set when the term name changes.
45
-
46
- = 2.6.29 - 2022-11-08 =
47
- * Add - Facebook Product Set under the Marketing menu.
48
- * Add - HPOS Compatibility.
49
- * Add - Inbox note about Facebook menu moved under the Marketing menu.
50
- * Add - Set up Facebook task to the WooCommerce admin tasks.
51
- * Dev - Replaced methods from classes in the `Internal` namespace.
52
- * Fix - Ensure the enhanced product enhance catalog attributes value is unslashed before saving in the post_meta table.
53
- * Fix - Hosted Woo Updates.
54
- * Fix - Release/2.6.28.
55
- * Fix - duplicate InitiateCheckout when using checkout block.
56
- * Tweak - WC 7.1 compatibility.
57
- * Tweak - WP 6.1 compatibility.
58
- * Update - FB Product Set name changed to Facebook Product Set.
59
- * Update - On successful FBE install users will be redirected to Advertise tab of the plugin.
60
-
61
- = 2.6.28 - 2022-10-25 =
62
- * Fix - Ensure bundles are not treated as virtual products on product_sync.
63
- * Fix - Ensure google-product-category-fields-loads.js loads only on the product category screens.
64
- * Fix - Server side sending of pixel events blocks generating pages .
65
-
66
- = 2.6.27 - 2022-10-14 =
67
- * Fix - Revert "Switch to Jetpack autoloader. (#1996 PR refresh)".
68
-
69
- = 2.6.26 - 2022-10-13 =
70
- * Add - wc_facebook_should_sync_product filter.
71
- * Dev - Rename JobRegistry to JobManager.
72
- * Dev - Replace composer autoloader with Jetpack autoloader.
73
- * Fix - Fix content_name and content_category attributes set on ViewCategory pixel events.
74
- * Tweak - WC 7.0 compatibility.
75
-
76
- = 2.6.25 - 2022-10-04 =
77
- * Add - New filter (wc_facebook_product_group_default_variation) to allow customizing a product group's default variation.
78
- * Update - Remove Skyverge's sake as a dependency from the extension build process.
79
-
80
- = 2.6.24 - 2022-09-27 =
81
- * Fix - Adds helpful admin notices for correct user roles.
82
- * Fix - Track purchase event flag in session variable instead post meta table.
83
-
84
- = 2.6.23 - 2022-09-13 =
85
- * Add - Show warning when creating product set with excluded categories.
86
- * Fix - Messenger settings are no longer overridden after business config refresh.
87
- * Fix - PHP notice thrown by get_page_id() in facebook-for-woocommerce/includes/API/FBE/Installation/Read/Response.php.
88
- * Fix - When disabling Enable Messenger on the Messenger setting page, the setting does not persist after selecting Save Changes.
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  = 2.6.22 - 2022-09-06 =
91
  * Fix - Adding an excluded category doesn't remove that category synced products.
92
  * Fix - Ensure content_name and content_ids addToCart pixel event properties are correct for variable products when redirect to cart is enabled in WooCommerce.
2
  Contributors: facebook, automattic, woothemes
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
+ Tested up to: 6.1
6
+ Stable tag: 3.0.0
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
39
 
40
  == Changelog ==
41
 
42
+ = 3.0.0 - 2022-11-17 =
43
+ * Dev - Adding API Unit Tests.
44
+ * Dev - Adding unit test workflow.
45
+ * Dev - Adjusting php code styling.
46
+ * Dev - Refactoring multiple Facebook APIs into a single one.
47
+ * Dev - Removing SkyVerge dependency.
48
+ * Dev - Removing deprecations.
49
+ * Tweak - WC 5.4 compatibility.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ = 2.6.30 - 2022-11-09 =
52
+ * Fix - Add backward compatibility for WC 6.1, 6.2, and 6.3 versions.
53
+ * Fix - Sync product set when the term name changes.
54
+
55
+ = 2.6.29 - 2022-11-08 =
56
+ * Add - Facebook Product Set under the Marketing menu.
57
+ * Add - HPOS Compatibility.
58
+ * Add - Inbox note about Facebook menu moved under the Marketing menu.
59
+ * Add - Set up Facebook task to the WooCommerce admin tasks.
60
+ * Dev - Replaced methods from classes in the `Internal` namespace.
61
+ * Fix - Ensure the enhanced product enhance catalog attributes value is unslashed before saving in the post_meta table.
62
+ * Fix - Hosted Woo Updates.
63
+ * Fix - Release/2.6.28.
64
+ * Fix - duplicate InitiateCheckout when using checkout block.
65
+ * Tweak - WC 7.1 compatibility.
66
+ * Tweak - WP 6.1 compatibility.
67
+ * Update - FB Product Set name changed to Facebook Product Set.
68
+ * Update - On successful FBE install users will be redirected to Advertise tab of the plugin.
69
+
70
+ = 2.6.28 - 2022-10-25 =
71
+ * Fix - Ensure bundles are not treated as virtual products on product_sync.
72
+ * Fix - Ensure google-product-category-fields-loads.js loads only on the product category screens.
73
+ * Fix - Server side sending of pixel events blocks generating pages .
74
+
75
+ = 2.6.27 - 2022-10-14 =
76
+ * Fix - Revert "Switch to Jetpack autoloader. (#1996 PR refresh)".
77
+
78
+ = 2.6.26 - 2022-10-13 =
79
+ * Add - wc_facebook_should_sync_product filter.
80
+ * Dev - Rename JobRegistry to JobManager.
81
+ * Dev - Replace composer autoloader with Jetpack autoloader.
82
+ * Fix - Fix content_name and content_category attributes set on ViewCategory pixel events.
83
+ * Tweak - WC 7.0 compatibility.
84
+
85
+ = 2.6.25 - 2022-10-04 =
86
+ * Add - New filter (wc_facebook_product_group_default_variation) to allow customizing a product group's default variation.
87
+ * Update - Remove Skyverge's sake as a dependency from the extension build process.
88
+
89
+ = 2.6.24 - 2022-09-27 =
90
+ * Fix - Adds helpful admin notices for correct user roles.
91
+ * Fix - Track purchase event flag in session variable instead post meta table.
92
+
93
+ = 2.6.23 - 2022-09-13 =
94
+ * Add - Show warning when creating product set with excluded categories.
95
+ * Fix - Messenger settings are no longer overridden after business config refresh.
96
+ * Fix - PHP notice thrown by get_page_id() in facebook-for-woocommerce/includes/API/FBE/Installation/Read/Response.php.
97
+ * Fix - When disabling Enable Messenger on the Messenger setting page, the setting does not persist after selecting Save Changes.
98
+
99
  = 2.6.22 - 2022-09-06 =
100
  * Fix - Adding an excluded category doesn't remove that category synced products.
101
  * Fix - Ensure content_name and content_ids addToCart pixel event properties are correct for variable products when redirect to cart is enabled in WooCommerce.
vendor/autoload.php CHANGED
@@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
9
 
10
  require_once __DIR__ . '/composer/autoload_real.php';
11
 
12
- return ComposerAutoloaderInitc7a38d3b9056d61c05392a953f2bba96::getLoader();
9
 
10
  require_once __DIR__ . '/composer/autoload_real.php';
11
 
12
+ return ComposerAutoloaderInit63d6606890a3bdef14921235e9cba133::getLoader();
vendor/composer/InstalledVersions.php CHANGED
@@ -28,7 +28,7 @@ class InstalledVersions
28
  {
29
  /**
30
  * @var mixed[]|null
31
- * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
32
  */
33
  private static $installed;
34
 
@@ -39,7 +39,7 @@ class InstalledVersions
39
 
40
  /**
41
  * @var array[]
42
- * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
43
  */
44
  private static $installedByVendor = array();
45
 
@@ -243,7 +243,7 @@ class InstalledVersions
243
 
244
  /**
245
  * @return array
246
- * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
247
  */
248
  public static function getRootPackage()
249
  {
@@ -257,7 +257,7 @@ class InstalledVersions
257
  *
258
  * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
259
  * @return array[]
260
- * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
261
  */
262
  public static function getRawData()
263
  {
@@ -280,7 +280,7 @@ class InstalledVersions
280
  * Returns the raw data of all installed.php which are currently loaded for custom implementations
281
  *
282
  * @return array[]
283
- * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
284
  */
285
  public static function getAllRawData()
286
  {
@@ -303,7 +303,7 @@ class InstalledVersions
303
  * @param array[] $data A vendor/composer/installed.php data set
304
  * @return void
305
  *
306
- * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
307
  */
308
  public static function reload($data)
309
  {
@@ -313,7 +313,7 @@ class InstalledVersions
313
 
314
  /**
315
  * @return array[]
316
- * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
317
  */
318
  private static function getInstalled()
319
  {
28
  {
29
  /**
30
  * @var mixed[]|null
31
+ * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
32
  */
33
  private static $installed;
34
 
39
 
40
  /**
41
  * @var array[]
42
+ * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
43
  */
44
  private static $installedByVendor = array();
45
 
243
 
244
  /**
245
  * @return array
246
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
247
  */
248
  public static function getRootPackage()
249
  {
257
  *
258
  * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
259
  * @return array[]
260
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
261
  */
262
  public static function getRawData()
263
  {
280
  * Returns the raw data of all installed.php which are currently loaded for custom implementations
281
  *
282
  * @return array[]
283
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
284
  */
285
  public static function getAllRawData()
286
  {
303
  * @param array[] $data A vendor/composer/installed.php data set
304
  * @return void
305
  *
306
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
307
  */
308
  public static function reload($data)
309
  {
313
 
314
  /**
315
  * @return array[]
316
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
317
  */
318
  private static function getInstalled()
319
  {
vendor/composer/autoload_classmap.php CHANGED
@@ -117,96 +117,152 @@ return array(
117
  'Composer\\Installers\\YawikInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
118
  'Composer\\Installers\\ZendInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
119
  'Composer\\Installers\\ZikulaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
120
- 'SkyVerge\\WooCommerce\\Facebook\\AJAX' => $baseDir . '/includes/AJAX.php',
121
- 'SkyVerge\\WooCommerce\\Facebook\\API' => $baseDir . '/includes/API.php',
122
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Request' => $baseDir . '/includes/API/Catalog/Product_Group/Products/Read/Request.php',
123
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Response' => $baseDir . '/includes/API/Catalog/Product_Group/Products/Read/Response.php',
124
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Find\\Request' => $baseDir . '/includes/API/Catalog/Product_Item/Find/Request.php',
125
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Response' => $baseDir . '/includes/API/Catalog/Product_Item/Response.php',
126
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Request' => $baseDir . '/includes/API/Catalog/Request.php',
127
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Response' => $baseDir . '/includes/API/Catalog/Response.php',
128
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Request' => $baseDir . '/includes/API/Catalog/Send_Item_Updates/Request.php',
129
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Response' => $baseDir . '/includes/API/Catalog/Send_Item_Updates/Response.php',
130
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Exceptions\\Request_Limit_Reached' => $baseDir . '/includes/API/Exceptions/Request_Limit_Reached.php',
131
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Messenger' => $baseDir . '/includes/API/FBE/Configuration/Messenger.php',
132
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Read\\Response' => $baseDir . '/includes/API/FBE/Configuration/Read/Response.php',
133
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Request' => $baseDir . '/includes/API/FBE/Configuration/Request.php',
134
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Request' => $baseDir . '/includes/API/FBE/Configuration/Update/Request.php',
135
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Request' => $baseDir . '/includes/API/FBE/Installation/Read/Request.php',
136
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Response' => $baseDir . '/includes/API/FBE/Installation/Read/Response.php',
137
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Request' => $baseDir . '/includes/API/FBE/Installation/Request.php',
138
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Abstract_Request' => $baseDir . '/includes/API/Orders/Abstract_Request.php',
139
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Acknowledge\\Request' => $baseDir . '/includes/API/Orders/Acknowledge/Request.php',
140
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Cancel\\Request' => $baseDir . '/includes/API/Orders/Cancel/Request.php',
141
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Fulfillment\\Request' => $baseDir . '/includes/API/Orders/Fulfillment/Request.php',
142
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Order' => $baseDir . '/includes/API/Orders/Order.php',
143
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Read\\Request' => $baseDir . '/includes/API/Orders/Read/Request.php',
144
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Read\\Response' => $baseDir . '/includes/API/Orders/Read/Response.php',
145
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Refund\\Request' => $baseDir . '/includes/API/Orders/Refund/Request.php',
146
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Request' => $baseDir . '/includes/API/Orders/Request.php',
147
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Response' => $baseDir . '/includes/API/Orders/Response.php',
148
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pages\\Read\\Request' => $baseDir . '/includes/API/Pages/Read/Request.php',
149
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pages\\Read\\Response' => $baseDir . '/includes/API/Pages/Read/Response.php',
150
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pixel\\Events\\Request' => $baseDir . '/includes/API/Pixel/Events/Request.php',
151
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Request' => $baseDir . '/includes/API/Request.php',
152
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Response' => $baseDir . '/includes/API/Response.php',
153
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Idempotent_Request' => $baseDir . '/includes/API/Traits/Idempotent_Request.php',
154
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Paginated_Response' => $baseDir . '/includes/API/Traits/Paginated_Response.php',
155
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_API' => $baseDir . '/includes/API/Traits/Rate_Limited_API.php',
156
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Request' => $baseDir . '/includes/API/Traits/Rate_Limited_Request.php',
157
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Response' => $baseDir . '/includes/API/Traits/Rate_Limited_Response.php',
158
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Request' => $baseDir . '/includes/API/User/Permissions/Delete/Request.php',
159
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Request' => $baseDir . '/includes/API/User/Request.php',
160
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Response' => $baseDir . '/includes/API/User/Response.php',
161
- 'SkyVerge\\WooCommerce\\Facebook\\Admin' => $baseDir . '/includes/Admin.php',
162
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Abstract_Settings_Screen' => $baseDir . '/includes/Admin/Abstract_Settings_Screen.php',
163
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Enhanced_Catalog_Attribute_Fields' => $baseDir . '/includes/Admin/Enhanced_Catalog_Attribute_Fields.php',
164
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Google_Product_Category_Field' => $baseDir . '/includes/Admin/Google_Product_Category_Field.php',
165
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Notes\\SettingsMoved' => $baseDir . '/includes/Admin/Notes/SettingsMoved.php',
166
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Categories' => $baseDir . '/includes/Admin/Product_Categories.php',
167
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Sets' => $baseDir . '/includes/Admin/Product_Sets.php',
168
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Sync_Meta_Box' => $baseDir . '/includes/Admin/Product_Sync_Meta_Box.php',
169
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Products' => $baseDir . '/includes/Admin/Products.php',
170
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings' => $baseDir . '/includes/Admin/Settings.php',
171
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Advertise' => $baseDir . '/includes/Admin/Settings_Screens/Advertise.php',
172
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Connection' => $baseDir . '/includes/Admin/Settings_Screens/Connection.php',
173
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Messenger' => $baseDir . '/includes/Admin/Settings_Screens/Messenger.php',
174
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sets' => $baseDir . '/includes/Admin/Settings_Screens/Product_Sets.php',
175
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sync' => $baseDir . '/includes/Admin/Settings_Screens/Product_Sync.php',
176
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Tasks\\Setup' => $baseDir . '/includes/Admin/Tasks/Setup.php',
177
- 'SkyVerge\\WooCommerce\\Facebook\\Commerce' => $baseDir . '/includes/Commerce.php',
178
- 'SkyVerge\\WooCommerce\\Facebook\\Commerce\\Orders' => $baseDir . '/includes/Commerce/Orders.php',
179
- 'SkyVerge\\WooCommerce\\Facebook\\Debug\\ProfilingLogger' => $baseDir . '/includes/Debug/ProfilingLogger.php',
180
- 'SkyVerge\\WooCommerce\\Facebook\\Debug\\ProfilingLoggerProcess' => $baseDir . '/includes/Debug/ProfilingLoggerProcess.php',
181
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\AAMSettings' => $baseDir . '/includes/Events/AAMSettings.php',
182
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\Event' => $baseDir . '/includes/Events/Event.php',
183
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\Normalizer' => $baseDir . '/includes/Events/Normalizer.php',
184
- 'SkyVerge\\WooCommerce\\Facebook\\Feed\\FeedConfigurationDetection' => $baseDir . '/includes/Feed/FeedConfigurationDetection.php',
185
- 'SkyVerge\\WooCommerce\\Facebook\\Handlers\\Connection' => $baseDir . '/includes/Handlers/Connection.php',
186
- 'SkyVerge\\WooCommerce\\Facebook\\Handlers\\WebHook' => $baseDir . '/includes/Handlers/WebHook.php',
187
- 'SkyVerge\\WooCommerce\\Facebook\\Integrations\\Bookings' => $baseDir . '/includes/Integrations/Bookings.php',
188
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\AbstractChainedJob' => $baseDir . '/includes/Jobs/AbstractChainedJob.php',
189
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\CleanupSkyvergeFrameworkJobOptions' => $baseDir . '/includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php',
190
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\GenerateProductFeed' => $baseDir . '/includes/Jobs/GenerateProductFeed.php',
191
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\JobManager' => $baseDir . '/includes/Jobs/JobManager.php',
192
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\LoggingTrait' => $baseDir . '/includes/Jobs/LoggingTrait.php',
193
- 'SkyVerge\\WooCommerce\\Facebook\\Lifecycle' => $baseDir . '/includes/Lifecycle.php',
194
- 'SkyVerge\\WooCommerce\\Facebook\\Locale' => $baseDir . '/includes/Locale.php',
195
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSets\\Sync' => $baseDir . '/includes/ProductSets/Sync.php',
196
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductExcludedException' => $baseDir . '/includes/ProductSync/ProductExcludedException.php',
197
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductInvalidException' => $baseDir . '/includes/ProductSync/ProductInvalidException.php',
198
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductValidator' => $baseDir . '/includes/ProductSync/ProductValidator.php',
199
- 'SkyVerge\\WooCommerce\\Facebook\\Product_Categories' => $baseDir . '/includes/Product_Categories.php',
200
- 'SkyVerge\\WooCommerce\\Facebook\\Products' => $baseDir . '/includes/Products.php',
201
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\FBCategories' => $baseDir . '/includes/Products/FBCategories.php',
202
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Feed' => $baseDir . '/includes/Products/Feed.php',
203
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\GoogleProductTaxonomy' => $baseDir . '/includes/Products/GoogleProductTaxonomy.php',
204
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Stock' => $baseDir . '/includes/Products/Stock.php',
205
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Sync' => $baseDir . '/includes/Products/Sync.php',
206
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Sync\\Background' => $baseDir . '/includes/Products/Sync/Background.php',
207
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Background_Handle_Virtual_Products_Variations' => $baseDir . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php',
208
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Background_Remove_Duplicate_Visibility_Meta' => $baseDir . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php',
209
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Heartbeat' => $baseDir . '/includes/Utilities/Heartbeat.php',
210
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Shipment' => $baseDir . '/includes/Utilities/Shipment.php',
211
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Tracker' => $baseDir . '/includes/Utilities/Tracker.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  );
117
  'Composer\\Installers\\YawikInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
118
  'Composer\\Installers\\ZendInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
119
  'Composer\\Installers\\ZikulaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
120
+ 'WooCommerce\\Facebook\\AJAX' => $baseDir . '/includes/AJAX.php',
121
+ 'WooCommerce\\Facebook\\API' => $baseDir . '/includes/API.php',
122
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Request' => $baseDir . '/includes/API/Catalog/Product_Group/Products/Read/Request.php',
123
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Response' => $baseDir . '/includes/API/Catalog/Product_Group/Products/Read/Response.php',
124
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Find\\Request' => $baseDir . '/includes/API/Catalog/Product_Item/Find/Request.php',
125
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Response' => $baseDir . '/includes/API/Catalog/Product_Item/Response.php',
126
+ 'WooCommerce\\Facebook\\API\\Catalog\\Request' => $baseDir . '/includes/API/Catalog/Request.php',
127
+ 'WooCommerce\\Facebook\\API\\Catalog\\Response' => $baseDir . '/includes/API/Catalog/Response.php',
128
+ 'WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Request' => $baseDir . '/includes/API/Catalog/Send_Item_Updates/Request.php',
129
+ 'WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Response' => $baseDir . '/includes/API/Catalog/Send_Item_Updates/Response.php',
130
+ 'WooCommerce\\Facebook\\API\\Exceptions\\ConnectApiException' => $baseDir . '/includes/API/Exceptions/ConnectApiException.php',
131
+ 'WooCommerce\\Facebook\\API\\Exceptions\\Request_Limit_Reached' => $baseDir . '/includes/API/Exceptions/Request_Limit_Reached.php',
132
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Messenger' => $baseDir . '/includes/API/FBE/Configuration/Messenger.php',
133
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Read\\Response' => $baseDir . '/includes/API/FBE/Configuration/Read/Response.php',
134
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Request' => $baseDir . '/includes/API/FBE/Configuration/Request.php',
135
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Request' => $baseDir . '/includes/API/FBE/Configuration/Update/Request.php',
136
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Response' => $baseDir . '/includes/API/FBE/Configuration/Update/Response.php',
137
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Request' => $baseDir . '/includes/API/FBE/Installation/Read/Request.php',
138
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Response' => $baseDir . '/includes/API/FBE/Installation/Read/Response.php',
139
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Request' => $baseDir . '/includes/API/FBE/Installation/Request.php',
140
+ 'WooCommerce\\Facebook\\API\\Log\\Create\\Request' => $baseDir . '/includes/API/Log/Create/Request.php',
141
+ 'WooCommerce\\Facebook\\API\\Log\\Create\\Response' => $baseDir . '/includes/API/Log/Create/Response.php',
142
+ 'WooCommerce\\Facebook\\API\\Orders\\Abstract_Request' => $baseDir . '/includes/API/Orders/Abstract_Request.php',
143
+ 'WooCommerce\\Facebook\\API\\Orders\\Acknowledge\\Request' => $baseDir . '/includes/API/Orders/Acknowledge/Request.php',
144
+ 'WooCommerce\\Facebook\\API\\Orders\\Cancel\\Request' => $baseDir . '/includes/API/Orders/Cancel/Request.php',
145
+ 'WooCommerce\\Facebook\\API\\Orders\\Fulfillment\\Request' => $baseDir . '/includes/API/Orders/Fulfillment/Request.php',
146
+ 'WooCommerce\\Facebook\\API\\Orders\\Order' => $baseDir . '/includes/API/Orders/Order.php',
147
+ 'WooCommerce\\Facebook\\API\\Orders\\Read\\Request' => $baseDir . '/includes/API/Orders/Read/Request.php',
148
+ 'WooCommerce\\Facebook\\API\\Orders\\Read\\Response' => $baseDir . '/includes/API/Orders/Read/Response.php',
149
+ 'WooCommerce\\Facebook\\API\\Orders\\Refund\\Request' => $baseDir . '/includes/API/Orders/Refund/Request.php',
150
+ 'WooCommerce\\Facebook\\API\\Orders\\Request' => $baseDir . '/includes/API/Orders/Request.php',
151
+ 'WooCommerce\\Facebook\\API\\Orders\\Response' => $baseDir . '/includes/API/Orders/Response.php',
152
+ 'WooCommerce\\Facebook\\API\\Pages\\Read\\Request' => $baseDir . '/includes/API/Pages/Read/Request.php',
153
+ 'WooCommerce\\Facebook\\API\\Pages\\Read\\Response' => $baseDir . '/includes/API/Pages/Read/Response.php',
154
+ 'WooCommerce\\Facebook\\API\\Pixel\\Events\\Request' => $baseDir . '/includes/API/Pixel/Events/Request.php',
155
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ItemsBatch\\Create\\Request' => $baseDir . '/includes/API/ProductCatalog/ItemsBatch/Create/Request.php',
156
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ItemsBatch\\Create\\Response' => $baseDir . '/includes/API/ProductCatalog/ItemsBatch/Create/Response.php',
157
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeedUploads\\Read\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductFeedUploads/Read/Request.php',
158
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeedUploads\\Read\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductFeedUploads/Read/Response.php',
159
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Create\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/Create/Request.php',
160
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Create\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/Create/Response.php',
161
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\ReadAll\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/ReadAll/Request.php',
162
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\ReadAll\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/ReadAll/Response.php',
163
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Read\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/Read/Request.php',
164
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Read\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductFeeds/Read/Response.php',
165
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Create\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Create/Request.php',
166
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Create\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Create/Response.php',
167
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Delete\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Delete/Request.php',
168
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Delete\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Delete/Response.php',
169
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Read\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Read/Request.php',
170
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Read\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Read/Response.php',
171
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Update\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Update/Request.php',
172
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Update\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductGroups/Update/Response.php',
173
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Create\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Create/Request.php',
174
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Create\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Create/Response.php',
175
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Delete\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Delete/Request.php',
176
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Delete\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Delete/Response.php',
177
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Update\\Request' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Update/Request.php',
178
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Update\\Response' => $baseDir . '/includes/API/ProductCatalog/ProductSets/Update/Response.php',
179
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Create\\Request' => $baseDir . '/includes/API/ProductCatalog/Products/Create/Request.php',
180
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Create\\Response' => $baseDir . '/includes/API/ProductCatalog/Products/Create/Response.php',
181
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Delete\\Request' => $baseDir . '/includes/API/ProductCatalog/Products/Delete/Request.php',
182
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Delete\\Response' => $baseDir . '/includes/API/ProductCatalog/Products/Delete/Response.php',
183
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Id\\Request' => $baseDir . '/includes/API/ProductCatalog/Products/Id/Request.php',
184
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Id\\Response' => $baseDir . '/includes/API/ProductCatalog/Products/Id/Response.php',
185
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Update\\Request' => $baseDir . '/includes/API/ProductCatalog/Products/Update/Request.php',
186
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Update\\Response' => $baseDir . '/includes/API/ProductCatalog/Products/Update/Response.php',
187
+ 'WooCommerce\\Facebook\\API\\Request' => $baseDir . '/includes/API/Request.php',
188
+ 'WooCommerce\\Facebook\\API\\Response' => $baseDir . '/includes/API/Response.php',
189
+ 'WooCommerce\\Facebook\\API\\Tip\\Log\\Request' => $baseDir . '/includes/API/Tip/Log/Request.php',
190
+ 'WooCommerce\\Facebook\\API\\Tip\\Log\\Response' => $baseDir . '/includes/API/Tip/Log/Response.php',
191
+ 'WooCommerce\\Facebook\\API\\Tip\\Read\\Request' => $baseDir . '/includes/API/Tip/Read/Request.php',
192
+ 'WooCommerce\\Facebook\\API\\Tip\\Read\\Response' => $baseDir . '/includes/API/Tip/Read/Response.php',
193
+ 'WooCommerce\\Facebook\\API\\Traits\\Idempotent_Request' => $baseDir . '/includes/API/Traits/Idempotent_Request.php',
194
+ 'WooCommerce\\Facebook\\API\\Traits\\Paginated_Response' => $baseDir . '/includes/API/Traits/Paginated_Response.php',
195
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_API' => $baseDir . '/includes/API/Traits/Rate_Limited_API.php',
196
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Request' => $baseDir . '/includes/API/Traits/Rate_Limited_Request.php',
197
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Response' => $baseDir . '/includes/API/Traits/Rate_Limited_Response.php',
198
+ 'WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Request' => $baseDir . '/includes/API/User/Permissions/Delete/Request.php',
199
+ 'WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Response' => $baseDir . '/includes/API/User/Permissions/Delete/Response.php',
200
+ 'WooCommerce\\Facebook\\API\\User\\Request' => $baseDir . '/includes/API/User/Request.php',
201
+ 'WooCommerce\\Facebook\\API\\User\\Response' => $baseDir . '/includes/API/User/Response.php',
202
+ 'WooCommerce\\Facebook\\Admin' => $baseDir . '/includes/Admin.php',
203
+ 'WooCommerce\\Facebook\\Admin\\Abstract_Settings_Screen' => $baseDir . '/includes/Admin/Abstract_Settings_Screen.php',
204
+ 'WooCommerce\\Facebook\\Admin\\Enhanced_Catalog_Attribute_Fields' => $baseDir . '/includes/Admin/Enhanced_Catalog_Attribute_Fields.php',
205
+ 'WooCommerce\\Facebook\\Admin\\Google_Product_Category_Field' => $baseDir . '/includes/Admin/Google_Product_Category_Field.php',
206
+ 'WooCommerce\\Facebook\\Admin\\Product_Categories' => $baseDir . '/includes/Admin/Product_Categories.php',
207
+ 'WooCommerce\\Facebook\\Admin\\Product_Sets' => $baseDir . '/includes/Admin/Product_Sets.php',
208
+ 'WooCommerce\\Facebook\\Admin\\Product_Sync_Meta_Box' => $baseDir . '/includes/Admin/Product_Sync_Meta_Box.php',
209
+ 'WooCommerce\\Facebook\\Admin\\Products' => $baseDir . '/includes/Admin/Products.php',
210
+ 'WooCommerce\\Facebook\\Admin\\Settings' => $baseDir . '/includes/Admin/Settings.php',
211
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Advertise' => $baseDir . '/includes/Admin/Settings_Screens/Advertise.php',
212
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Connection' => $baseDir . '/includes/Admin/Settings_Screens/Connection.php',
213
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Messenger' => $baseDir . '/includes/Admin/Settings_Screens/Messenger.php',
214
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sets' => $baseDir . '/includes/Admin/Settings_Screens/Product_Sets.php',
215
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sync' => $baseDir . '/includes/Admin/Settings_Screens/Product_Sync.php',
216
+ 'WooCommerce\\Facebook\\Admin\\Tasks\\Setup' => $baseDir . '/includes/Admin/Tasks/Setup.php',
217
+ 'WooCommerce\\Facebook\\Commerce' => $baseDir . '/includes/Commerce.php',
218
+ 'WooCommerce\\Facebook\\Commerce\\Orders' => $baseDir . '/includes/Commerce/Orders.php',
219
+ 'WooCommerce\\Facebook\\Debug\\ProfilingLogger' => $baseDir . '/includes/Debug/ProfilingLogger.php',
220
+ 'WooCommerce\\Facebook\\Debug\\ProfilingLoggerProcess' => $baseDir . '/includes/Debug/ProfilingLoggerProcess.php',
221
+ 'WooCommerce\\Facebook\\Events\\AAMSettings' => $baseDir . '/includes/Events/AAMSettings.php',
222
+ 'WooCommerce\\Facebook\\Events\\Event' => $baseDir . '/includes/Events/Event.php',
223
+ 'WooCommerce\\Facebook\\Events\\Normalizer' => $baseDir . '/includes/Events/Normalizer.php',
224
+ 'WooCommerce\\Facebook\\Feed\\FeedConfigurationDetection' => $baseDir . '/includes/Feed/FeedConfigurationDetection.php',
225
+ 'WooCommerce\\Facebook\\Framework\\AdminMessageHandler' => $baseDir . '/includes/Framework/AdminMessageHandler.php',
226
+ 'WooCommerce\\Facebook\\Framework\\AdminNoticeHandler' => $baseDir . '/includes/Framework/AdminNoticeHandler.php',
227
+ 'WooCommerce\\Facebook\\Framework\\Api\\Base' => $baseDir . '/includes/Framework/Api/Base.php',
228
+ 'WooCommerce\\Facebook\\Framework\\Api\\Exception' => $baseDir . '/includes/Framework/Api/Exception.php',
229
+ 'WooCommerce\\Facebook\\Framework\\Api\\JSONRequest' => $baseDir . '/includes/Framework/Api/JSONRequest.php',
230
+ 'WooCommerce\\Facebook\\Framework\\Api\\JSONResponse' => $baseDir . '/includes/Framework/Api/JSONResponse.php',
231
+ 'WooCommerce\\Facebook\\Framework\\Api\\Request' => $baseDir . '/includes/Framework/Api/Request.php',
232
+ 'WooCommerce\\Facebook\\Framework\\Api\\Response' => $baseDir . '/includes/Framework/Api/Response.php',
233
+ 'WooCommerce\\Facebook\\Framework\\Helper' => $baseDir . '/includes/Framework/Helper.php',
234
+ 'WooCommerce\\Facebook\\Framework\\Lifecycle' => $baseDir . '/includes/Framework/Lifecycle.php',
235
+ 'WooCommerce\\Facebook\\Framework\\Plugin' => $baseDir . '/includes/Framework/Plugin.php',
236
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Compatibility' => $baseDir . '/includes/Framework/Plugin/Compatibility.php',
237
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Dependencies' => $baseDir . '/includes/Framework/Plugin/Dependencies.php',
238
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Exception' => $baseDir . '/includes/Framework/Plugin/Exception.php',
239
+ 'WooCommerce\\Facebook\\Framework\\Utilities\\AsyncRequest' => $baseDir . '/includes/Framework/Utilities/AsyncRequest.php',
240
+ 'WooCommerce\\Facebook\\Framework\\Utilities\\BackgroundJobHandler' => $baseDir . '/includes/Framework/Utilities/BackgroundJobHandler.php',
241
+ 'WooCommerce\\Facebook\\Handlers\\Connection' => $baseDir . '/includes/Handlers/Connection.php',
242
+ 'WooCommerce\\Facebook\\Handlers\\WebHook' => $baseDir . '/includes/Handlers/WebHook.php',
243
+ 'WooCommerce\\Facebook\\Integrations\\Bookings' => $baseDir . '/includes/Integrations/Bookings.php',
244
+ 'WooCommerce\\Facebook\\Jobs\\AbstractChainedJob' => $baseDir . '/includes/Jobs/AbstractChainedJob.php',
245
+ 'WooCommerce\\Facebook\\Jobs\\CleanupSkyvergeFrameworkJobOptions' => $baseDir . '/includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php',
246
+ 'WooCommerce\\Facebook\\Jobs\\GenerateProductFeed' => $baseDir . '/includes/Jobs/GenerateProductFeed.php',
247
+ 'WooCommerce\\Facebook\\Jobs\\JobManager' => $baseDir . '/includes/Jobs/JobManager.php',
248
+ 'WooCommerce\\Facebook\\Jobs\\LoggingTrait' => $baseDir . '/includes/Jobs/LoggingTrait.php',
249
+ 'WooCommerce\\Facebook\\Lifecycle' => $baseDir . '/includes/Lifecycle.php',
250
+ 'WooCommerce\\Facebook\\Locale' => $baseDir . '/includes/Locale.php',
251
+ 'WooCommerce\\Facebook\\ProductSets\\Sync' => $baseDir . '/includes/ProductSets/Sync.php',
252
+ 'WooCommerce\\Facebook\\ProductSync\\ProductExcludedException' => $baseDir . '/includes/ProductSync/ProductExcludedException.php',
253
+ 'WooCommerce\\Facebook\\ProductSync\\ProductInvalidException' => $baseDir . '/includes/ProductSync/ProductInvalidException.php',
254
+ 'WooCommerce\\Facebook\\ProductSync\\ProductValidator' => $baseDir . '/includes/ProductSync/ProductValidator.php',
255
+ 'WooCommerce\\Facebook\\Product_Categories' => $baseDir . '/includes/Product_Categories.php',
256
+ 'WooCommerce\\Facebook\\Products' => $baseDir . '/includes/Products.php',
257
+ 'WooCommerce\\Facebook\\Products\\FBCategories' => $baseDir . '/includes/Products/FBCategories.php',
258
+ 'WooCommerce\\Facebook\\Products\\Feed' => $baseDir . '/includes/Products/Feed.php',
259
+ 'WooCommerce\\Facebook\\Products\\GoogleProductTaxonomy' => $baseDir . '/includes/Products/GoogleProductTaxonomy.php',
260
+ 'WooCommerce\\Facebook\\Products\\Stock' => $baseDir . '/includes/Products/Stock.php',
261
+ 'WooCommerce\\Facebook\\Products\\Sync' => $baseDir . '/includes/Products/Sync.php',
262
+ 'WooCommerce\\Facebook\\Products\\Sync\\Background' => $baseDir . '/includes/Products/Sync/Background.php',
263
+ 'WooCommerce\\Facebook\\Utilities\\Background_Handle_Virtual_Products_Variations' => $baseDir . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php',
264
+ 'WooCommerce\\Facebook\\Utilities\\Background_Remove_Duplicate_Visibility_Meta' => $baseDir . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php',
265
+ 'WooCommerce\\Facebook\\Utilities\\Heartbeat' => $baseDir . '/includes/Utilities/Heartbeat.php',
266
+ 'WooCommerce\\Facebook\\Utilities\\Shipment' => $baseDir . '/includes/Utilities/Shipment.php',
267
+ 'WooCommerce\\Facebook\\Utilities\\Tracker' => $baseDir . '/includes/Utilities/Tracker.php',
268
  );
vendor/composer/autoload_psr4.php CHANGED
@@ -6,7 +6,7 @@ $vendorDir = dirname(__DIR__);
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
- 'SkyVerge\\WooCommerce\\Facebook\\' => array($baseDir . '/includes'),
10
  'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
11
  'Automattic\\WooCommerce\\ActionSchedulerJobFramework\\' => array($vendorDir . '/woocommerce/action-scheduler-job-framework/src'),
12
  );
6
  $baseDir = dirname($vendorDir);
7
 
8
  return array(
9
+ 'WooCommerce\\Facebook\\' => array($baseDir . '/includes'),
10
  'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
11
  'Automattic\\WooCommerce\\ActionSchedulerJobFramework\\' => array($vendorDir . '/woocommerce/action-scheduler-job-framework/src'),
12
  );
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInitc7a38d3b9056d61c05392a953f2bba96
6
  {
7
  private static $loader;
8
 
@@ -24,12 +24,12 @@ class ComposerAutoloaderInitc7a38d3b9056d61c05392a953f2bba96
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
- spl_autoload_register(array('ComposerAutoloaderInitc7a38d3b9056d61c05392a953f2bba96', 'loadClassLoader'), true, true);
28
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
29
- spl_autoload_unregister(array('ComposerAutoloaderInitc7a38d3b9056d61c05392a953f2bba96', 'loadClassLoader'));
30
 
31
  require __DIR__ . '/autoload_static.php';
32
- call_user_func(\Composer\Autoload\ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96::getInitializer($loader));
33
 
34
  $loader->register(true);
35
 
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit63d6606890a3bdef14921235e9cba133
6
  {
7
  private static $loader;
8
 
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
+ spl_autoload_register(array('ComposerAutoloaderInit63d6606890a3bdef14921235e9cba133', 'loadClassLoader'), true, true);
28
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInit63d6606890a3bdef14921235e9cba133', 'loadClassLoader'));
30
 
31
  require __DIR__ . '/autoload_static.php';
32
+ call_user_func(\Composer\Autoload\ComposerStaticInit63d6606890a3bdef14921235e9cba133::getInitializer($loader));
33
 
34
  $loader->register(true);
35
 
vendor/composer/autoload_static.php CHANGED
@@ -4,12 +4,12 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
- 'S' =>
11
  array (
12
- 'SkyVerge\\WooCommerce\\Facebook\\' => 30,
13
  ),
14
  'C' =>
15
  array (
@@ -22,7 +22,7 @@ class ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96
22
  );
23
 
24
  public static $prefixDirsPsr4 = array (
25
- 'SkyVerge\\WooCommerce\\Facebook\\' =>
26
  array (
27
  0 => __DIR__ . '/../..' . '/includes',
28
  ),
@@ -148,106 +148,162 @@ class ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96
148
  'Composer\\Installers\\YawikInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
149
  'Composer\\Installers\\ZendInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
150
  'Composer\\Installers\\ZikulaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
151
- 'SkyVerge\\WooCommerce\\Facebook\\AJAX' => __DIR__ . '/../..' . '/includes/AJAX.php',
152
- 'SkyVerge\\WooCommerce\\Facebook\\API' => __DIR__ . '/../..' . '/includes/API.php',
153
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Group/Products/Read/Request.php',
154
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Group/Products/Read/Response.php',
155
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Find\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Item/Find/Request.php',
156
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Item/Response.php',
157
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Request.php',
158
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Response.php',
159
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Send_Item_Updates/Request.php',
160
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Send_Item_Updates/Response.php',
161
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Exceptions\\Request_Limit_Reached' => __DIR__ . '/../..' . '/includes/API/Exceptions/Request_Limit_Reached.php',
162
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Messenger' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Messenger.php',
163
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Read/Response.php',
164
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Request.php',
165
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Update/Request.php',
166
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Read/Request.php',
167
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Read/Response.php',
168
- 'SkyVerge\\WooCommerce\\Facebook\\API\\FBE\\Installation\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Request.php',
169
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Abstract_Request' => __DIR__ . '/../..' . '/includes/API/Orders/Abstract_Request.php',
170
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Acknowledge\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Acknowledge/Request.php',
171
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Cancel\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Cancel/Request.php',
172
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Fulfillment\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Fulfillment/Request.php',
173
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Order' => __DIR__ . '/../..' . '/includes/API/Orders/Order.php',
174
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Read/Request.php',
175
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Orders/Read/Response.php',
176
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Refund\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Refund/Request.php',
177
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Request.php',
178
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Orders\\Response' => __DIR__ . '/../..' . '/includes/API/Orders/Response.php',
179
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pages\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Pages/Read/Request.php',
180
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pages\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Pages/Read/Response.php',
181
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Pixel\\Events\\Request' => __DIR__ . '/../..' . '/includes/API/Pixel/Events/Request.php',
182
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Request' => __DIR__ . '/../..' . '/includes/API/Request.php',
183
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Response' => __DIR__ . '/../..' . '/includes/API/Response.php',
184
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Idempotent_Request' => __DIR__ . '/../..' . '/includes/API/Traits/Idempotent_Request.php',
185
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Paginated_Response' => __DIR__ . '/../..' . '/includes/API/Traits/Paginated_Response.php',
186
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_API' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_API.php',
187
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Request' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_Request.php',
188
- 'SkyVerge\\WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Response' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_Response.php',
189
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Request' => __DIR__ . '/../..' . '/includes/API/User/Permissions/Delete/Request.php',
190
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Request' => __DIR__ . '/../..' . '/includes/API/User/Request.php',
191
- 'SkyVerge\\WooCommerce\\Facebook\\API\\User\\Response' => __DIR__ . '/../..' . '/includes/API/User/Response.php',
192
- 'SkyVerge\\WooCommerce\\Facebook\\Admin' => __DIR__ . '/../..' . '/includes/Admin.php',
193
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Abstract_Settings_Screen' => __DIR__ . '/../..' . '/includes/Admin/Abstract_Settings_Screen.php',
194
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Enhanced_Catalog_Attribute_Fields' => __DIR__ . '/../..' . '/includes/Admin/Enhanced_Catalog_Attribute_Fields.php',
195
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Google_Product_Category_Field' => __DIR__ . '/../..' . '/includes/Admin/Google_Product_Category_Field.php',
196
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Notes\\SettingsMoved' => __DIR__ . '/../..' . '/includes/Admin/Notes/SettingsMoved.php',
197
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Categories' => __DIR__ . '/../..' . '/includes/Admin/Product_Categories.php',
198
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Sets' => __DIR__ . '/../..' . '/includes/Admin/Product_Sets.php',
199
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Product_Sync_Meta_Box' => __DIR__ . '/../..' . '/includes/Admin/Product_Sync_Meta_Box.php',
200
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Products' => __DIR__ . '/../..' . '/includes/Admin/Products.php',
201
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings' => __DIR__ . '/../..' . '/includes/Admin/Settings.php',
202
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Advertise' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Advertise.php',
203
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Connection' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Connection.php',
204
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Messenger' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Messenger.php',
205
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sets' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Product_Sets.php',
206
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sync' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Product_Sync.php',
207
- 'SkyVerge\\WooCommerce\\Facebook\\Admin\\Tasks\\Setup' => __DIR__ . '/../..' . '/includes/Admin/Tasks/Setup.php',
208
- 'SkyVerge\\WooCommerce\\Facebook\\Commerce' => __DIR__ . '/../..' . '/includes/Commerce.php',
209
- 'SkyVerge\\WooCommerce\\Facebook\\Commerce\\Orders' => __DIR__ . '/../..' . '/includes/Commerce/Orders.php',
210
- 'SkyVerge\\WooCommerce\\Facebook\\Debug\\ProfilingLogger' => __DIR__ . '/../..' . '/includes/Debug/ProfilingLogger.php',
211
- 'SkyVerge\\WooCommerce\\Facebook\\Debug\\ProfilingLoggerProcess' => __DIR__ . '/../..' . '/includes/Debug/ProfilingLoggerProcess.php',
212
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\AAMSettings' => __DIR__ . '/../..' . '/includes/Events/AAMSettings.php',
213
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\Event' => __DIR__ . '/../..' . '/includes/Events/Event.php',
214
- 'SkyVerge\\WooCommerce\\Facebook\\Events\\Normalizer' => __DIR__ . '/../..' . '/includes/Events/Normalizer.php',
215
- 'SkyVerge\\WooCommerce\\Facebook\\Feed\\FeedConfigurationDetection' => __DIR__ . '/../..' . '/includes/Feed/FeedConfigurationDetection.php',
216
- 'SkyVerge\\WooCommerce\\Facebook\\Handlers\\Connection' => __DIR__ . '/../..' . '/includes/Handlers/Connection.php',
217
- 'SkyVerge\\WooCommerce\\Facebook\\Handlers\\WebHook' => __DIR__ . '/../..' . '/includes/Handlers/WebHook.php',
218
- 'SkyVerge\\WooCommerce\\Facebook\\Integrations\\Bookings' => __DIR__ . '/../..' . '/includes/Integrations/Bookings.php',
219
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\AbstractChainedJob' => __DIR__ . '/../..' . '/includes/Jobs/AbstractChainedJob.php',
220
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\CleanupSkyvergeFrameworkJobOptions' => __DIR__ . '/../..' . '/includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php',
221
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\GenerateProductFeed' => __DIR__ . '/../..' . '/includes/Jobs/GenerateProductFeed.php',
222
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\JobManager' => __DIR__ . '/../..' . '/includes/Jobs/JobManager.php',
223
- 'SkyVerge\\WooCommerce\\Facebook\\Jobs\\LoggingTrait' => __DIR__ . '/../..' . '/includes/Jobs/LoggingTrait.php',
224
- 'SkyVerge\\WooCommerce\\Facebook\\Lifecycle' => __DIR__ . '/../..' . '/includes/Lifecycle.php',
225
- 'SkyVerge\\WooCommerce\\Facebook\\Locale' => __DIR__ . '/../..' . '/includes/Locale.php',
226
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSets\\Sync' => __DIR__ . '/../..' . '/includes/ProductSets/Sync.php',
227
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductExcludedException' => __DIR__ . '/../..' . '/includes/ProductSync/ProductExcludedException.php',
228
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductInvalidException' => __DIR__ . '/../..' . '/includes/ProductSync/ProductInvalidException.php',
229
- 'SkyVerge\\WooCommerce\\Facebook\\ProductSync\\ProductValidator' => __DIR__ . '/../..' . '/includes/ProductSync/ProductValidator.php',
230
- 'SkyVerge\\WooCommerce\\Facebook\\Product_Categories' => __DIR__ . '/../..' . '/includes/Product_Categories.php',
231
- 'SkyVerge\\WooCommerce\\Facebook\\Products' => __DIR__ . '/../..' . '/includes/Products.php',
232
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\FBCategories' => __DIR__ . '/../..' . '/includes/Products/FBCategories.php',
233
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Feed' => __DIR__ . '/../..' . '/includes/Products/Feed.php',
234
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\GoogleProductTaxonomy' => __DIR__ . '/../..' . '/includes/Products/GoogleProductTaxonomy.php',
235
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Stock' => __DIR__ . '/../..' . '/includes/Products/Stock.php',
236
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Sync' => __DIR__ . '/../..' . '/includes/Products/Sync.php',
237
- 'SkyVerge\\WooCommerce\\Facebook\\Products\\Sync\\Background' => __DIR__ . '/../..' . '/includes/Products/Sync/Background.php',
238
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Background_Handle_Virtual_Products_Variations' => __DIR__ . '/../..' . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php',
239
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Background_Remove_Duplicate_Visibility_Meta' => __DIR__ . '/../..' . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php',
240
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Heartbeat' => __DIR__ . '/../..' . '/includes/Utilities/Heartbeat.php',
241
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Shipment' => __DIR__ . '/../..' . '/includes/Utilities/Shipment.php',
242
- 'SkyVerge\\WooCommerce\\Facebook\\Utilities\\Tracker' => __DIR__ . '/../..' . '/includes/Utilities/Tracker.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  );
244
 
245
  public static function getInitializer(ClassLoader $loader)
246
  {
247
  return \Closure::bind(function () use ($loader) {
248
- $loader->prefixLengthsPsr4 = ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96::$prefixLengthsPsr4;
249
- $loader->prefixDirsPsr4 = ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96::$prefixDirsPsr4;
250
- $loader->classMap = ComposerStaticInitc7a38d3b9056d61c05392a953f2bba96::$classMap;
251
 
252
  }, null, ClassLoader::class);
253
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit63d6606890a3bdef14921235e9cba133
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
+ 'W' =>
11
  array (
12
+ 'WooCommerce\\Facebook\\' => 21,
13
  ),
14
  'C' =>
15
  array (
22
  );
23
 
24
  public static $prefixDirsPsr4 = array (
25
+ 'WooCommerce\\Facebook\\' =>
26
  array (
27
  0 => __DIR__ . '/../..' . '/includes',
28
  ),
148
  'Composer\\Installers\\YawikInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/YawikInstaller.php',
149
  'Composer\\Installers\\ZendInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZendInstaller.php',
150
  'Composer\\Installers\\ZikulaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ZikulaInstaller.php',
151
+ 'WooCommerce\\Facebook\\AJAX' => __DIR__ . '/../..' . '/includes/AJAX.php',
152
+ 'WooCommerce\\Facebook\\API' => __DIR__ . '/../..' . '/includes/API.php',
153
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Group/Products/Read/Request.php',
154
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Group\\Products\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Group/Products/Read/Response.php',
155
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Find\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Item/Find/Request.php',
156
+ 'WooCommerce\\Facebook\\API\\Catalog\\Product_Item\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Product_Item/Response.php',
157
+ 'WooCommerce\\Facebook\\API\\Catalog\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Request.php',
158
+ 'WooCommerce\\Facebook\\API\\Catalog\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Response.php',
159
+ 'WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Request' => __DIR__ . '/../..' . '/includes/API/Catalog/Send_Item_Updates/Request.php',
160
+ 'WooCommerce\\Facebook\\API\\Catalog\\Send_Item_Updates\\Response' => __DIR__ . '/../..' . '/includes/API/Catalog/Send_Item_Updates/Response.php',
161
+ 'WooCommerce\\Facebook\\API\\Exceptions\\ConnectApiException' => __DIR__ . '/../..' . '/includes/API/Exceptions/ConnectApiException.php',
162
+ 'WooCommerce\\Facebook\\API\\Exceptions\\Request_Limit_Reached' => __DIR__ . '/../..' . '/includes/API/Exceptions/Request_Limit_Reached.php',
163
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Messenger' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Messenger.php',
164
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Read/Response.php',
165
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Request.php',
166
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Update/Request.php',
167
+ 'WooCommerce\\Facebook\\API\\FBE\\Configuration\\Update\\Response' => __DIR__ . '/../..' . '/includes/API/FBE/Configuration/Update/Response.php',
168
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Read/Request.php',
169
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Read/Response.php',
170
+ 'WooCommerce\\Facebook\\API\\FBE\\Installation\\Request' => __DIR__ . '/../..' . '/includes/API/FBE/Installation/Request.php',
171
+ 'WooCommerce\\Facebook\\API\\Log\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/Log/Create/Request.php',
172
+ 'WooCommerce\\Facebook\\API\\Log\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/Log/Create/Response.php',
173
+ 'WooCommerce\\Facebook\\API\\Orders\\Abstract_Request' => __DIR__ . '/../..' . '/includes/API/Orders/Abstract_Request.php',
174
+ 'WooCommerce\\Facebook\\API\\Orders\\Acknowledge\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Acknowledge/Request.php',
175
+ 'WooCommerce\\Facebook\\API\\Orders\\Cancel\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Cancel/Request.php',
176
+ 'WooCommerce\\Facebook\\API\\Orders\\Fulfillment\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Fulfillment/Request.php',
177
+ 'WooCommerce\\Facebook\\API\\Orders\\Order' => __DIR__ . '/../..' . '/includes/API/Orders/Order.php',
178
+ 'WooCommerce\\Facebook\\API\\Orders\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Read/Request.php',
179
+ 'WooCommerce\\Facebook\\API\\Orders\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Orders/Read/Response.php',
180
+ 'WooCommerce\\Facebook\\API\\Orders\\Refund\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Refund/Request.php',
181
+ 'WooCommerce\\Facebook\\API\\Orders\\Request' => __DIR__ . '/../..' . '/includes/API/Orders/Request.php',
182
+ 'WooCommerce\\Facebook\\API\\Orders\\Response' => __DIR__ . '/../..' . '/includes/API/Orders/Response.php',
183
+ 'WooCommerce\\Facebook\\API\\Pages\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Pages/Read/Request.php',
184
+ 'WooCommerce\\Facebook\\API\\Pages\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Pages/Read/Response.php',
185
+ 'WooCommerce\\Facebook\\API\\Pixel\\Events\\Request' => __DIR__ . '/../..' . '/includes/API/Pixel/Events/Request.php',
186
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ItemsBatch\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ItemsBatch/Create/Request.php',
187
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ItemsBatch\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ItemsBatch/Create/Response.php',
188
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeedUploads\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeedUploads/Read/Request.php',
189
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeedUploads\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeedUploads/Read/Response.php',
190
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/Create/Request.php',
191
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/Create/Response.php',
192
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\ReadAll\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/ReadAll/Request.php',
193
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\ReadAll\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/ReadAll/Response.php',
194
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/Read/Request.php',
195
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductFeeds\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductFeeds/Read/Response.php',
196
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Create/Request.php',
197
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Create/Response.php',
198
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Delete\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Delete/Request.php',
199
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Delete\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Delete/Response.php',
200
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Read/Request.php',
201
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Read/Response.php',
202
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Update\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Update/Request.php',
203
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductGroups\\Update\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductGroups/Update/Response.php',
204
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Create/Request.php',
205
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Create/Response.php',
206
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Delete\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Delete/Request.php',
207
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Delete\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Delete/Response.php',
208
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Update\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Update/Request.php',
209
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\ProductSets\\Update\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/ProductSets/Update/Response.php',
210
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Create\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Create/Request.php',
211
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Create\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Create/Response.php',
212
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Delete\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Delete/Request.php',
213
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Delete\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Delete/Response.php',
214
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Id\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Id/Request.php',
215
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Id\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Id/Response.php',
216
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Update\\Request' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Update/Request.php',
217
+ 'WooCommerce\\Facebook\\API\\ProductCatalog\\Products\\Update\\Response' => __DIR__ . '/../..' . '/includes/API/ProductCatalog/Products/Update/Response.php',
218
+ 'WooCommerce\\Facebook\\API\\Request' => __DIR__ . '/../..' . '/includes/API/Request.php',
219
+ 'WooCommerce\\Facebook\\API\\Response' => __DIR__ . '/../..' . '/includes/API/Response.php',
220
+ 'WooCommerce\\Facebook\\API\\Tip\\Log\\Request' => __DIR__ . '/../..' . '/includes/API/Tip/Log/Request.php',
221
+ 'WooCommerce\\Facebook\\API\\Tip\\Log\\Response' => __DIR__ . '/../..' . '/includes/API/Tip/Log/Response.php',
222
+ 'WooCommerce\\Facebook\\API\\Tip\\Read\\Request' => __DIR__ . '/../..' . '/includes/API/Tip/Read/Request.php',
223
+ 'WooCommerce\\Facebook\\API\\Tip\\Read\\Response' => __DIR__ . '/../..' . '/includes/API/Tip/Read/Response.php',
224
+ 'WooCommerce\\Facebook\\API\\Traits\\Idempotent_Request' => __DIR__ . '/../..' . '/includes/API/Traits/Idempotent_Request.php',
225
+ 'WooCommerce\\Facebook\\API\\Traits\\Paginated_Response' => __DIR__ . '/../..' . '/includes/API/Traits/Paginated_Response.php',
226
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_API' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_API.php',
227
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Request' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_Request.php',
228
+ 'WooCommerce\\Facebook\\API\\Traits\\Rate_Limited_Response' => __DIR__ . '/../..' . '/includes/API/Traits/Rate_Limited_Response.php',
229
+ 'WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Request' => __DIR__ . '/../..' . '/includes/API/User/Permissions/Delete/Request.php',
230
+ 'WooCommerce\\Facebook\\API\\User\\Permissions\\Delete\\Response' => __DIR__ . '/../..' . '/includes/API/User/Permissions/Delete/Response.php',
231
+ 'WooCommerce\\Facebook\\API\\User\\Request' => __DIR__ . '/../..' . '/includes/API/User/Request.php',
232
+ 'WooCommerce\\Facebook\\API\\User\\Response' => __DIR__ . '/../..' . '/includes/API/User/Response.php',
233
+ 'WooCommerce\\Facebook\\Admin' => __DIR__ . '/../..' . '/includes/Admin.php',
234
+ 'WooCommerce\\Facebook\\Admin\\Abstract_Settings_Screen' => __DIR__ . '/../..' . '/includes/Admin/Abstract_Settings_Screen.php',
235
+ 'WooCommerce\\Facebook\\Admin\\Enhanced_Catalog_Attribute_Fields' => __DIR__ . '/../..' . '/includes/Admin/Enhanced_Catalog_Attribute_Fields.php',
236
+ 'WooCommerce\\Facebook\\Admin\\Google_Product_Category_Field' => __DIR__ . '/../..' . '/includes/Admin/Google_Product_Category_Field.php',
237
+ 'WooCommerce\\Facebook\\Admin\\Product_Categories' => __DIR__ . '/../..' . '/includes/Admin/Product_Categories.php',
238
+ 'WooCommerce\\Facebook\\Admin\\Product_Sets' => __DIR__ . '/../..' . '/includes/Admin/Product_Sets.php',
239
+ 'WooCommerce\\Facebook\\Admin\\Product_Sync_Meta_Box' => __DIR__ . '/../..' . '/includes/Admin/Product_Sync_Meta_Box.php',
240
+ 'WooCommerce\\Facebook\\Admin\\Products' => __DIR__ . '/../..' . '/includes/Admin/Products.php',
241
+ 'WooCommerce\\Facebook\\Admin\\Settings' => __DIR__ . '/../..' . '/includes/Admin/Settings.php',
242
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Advertise' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Advertise.php',
243
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Connection' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Connection.php',
244
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Messenger' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Messenger.php',
245
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sets' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Product_Sets.php',
246
+ 'WooCommerce\\Facebook\\Admin\\Settings_Screens\\Product_Sync' => __DIR__ . '/../..' . '/includes/Admin/Settings_Screens/Product_Sync.php',
247
+ 'WooCommerce\\Facebook\\Admin\\Tasks\\Setup' => __DIR__ . '/../..' . '/includes/Admin/Tasks/Setup.php',
248
+ 'WooCommerce\\Facebook\\Commerce' => __DIR__ . '/../..' . '/includes/Commerce.php',
249
+ 'WooCommerce\\Facebook\\Commerce\\Orders' => __DIR__ . '/../..' . '/includes/Commerce/Orders.php',
250
+ 'WooCommerce\\Facebook\\Debug\\ProfilingLogger' => __DIR__ . '/../..' . '/includes/Debug/ProfilingLogger.php',
251
+ 'WooCommerce\\Facebook\\Debug\\ProfilingLoggerProcess' => __DIR__ . '/../..' . '/includes/Debug/ProfilingLoggerProcess.php',
252
+ 'WooCommerce\\Facebook\\Events\\AAMSettings' => __DIR__ . '/../..' . '/includes/Events/AAMSettings.php',
253
+ 'WooCommerce\\Facebook\\Events\\Event' => __DIR__ . '/../..' . '/includes/Events/Event.php',
254
+ 'WooCommerce\\Facebook\\Events\\Normalizer' => __DIR__ . '/../..' . '/includes/Events/Normalizer.php',
255
+ 'WooCommerce\\Facebook\\Feed\\FeedConfigurationDetection' => __DIR__ . '/../..' . '/includes/Feed/FeedConfigurationDetection.php',
256
+ 'WooCommerce\\Facebook\\Framework\\AdminMessageHandler' => __DIR__ . '/../..' . '/includes/Framework/AdminMessageHandler.php',
257
+ 'WooCommerce\\Facebook\\Framework\\AdminNoticeHandler' => __DIR__ . '/../..' . '/includes/Framework/AdminNoticeHandler.php',
258
+ 'WooCommerce\\Facebook\\Framework\\Api\\Base' => __DIR__ . '/../..' . '/includes/Framework/Api/Base.php',
259
+ 'WooCommerce\\Facebook\\Framework\\Api\\Exception' => __DIR__ . '/../..' . '/includes/Framework/Api/Exception.php',
260
+ 'WooCommerce\\Facebook\\Framework\\Api\\JSONRequest' => __DIR__ . '/../..' . '/includes/Framework/Api/JSONRequest.php',
261
+ 'WooCommerce\\Facebook\\Framework\\Api\\JSONResponse' => __DIR__ . '/../..' . '/includes/Framework/Api/JSONResponse.php',
262
+ 'WooCommerce\\Facebook\\Framework\\Api\\Request' => __DIR__ . '/../..' . '/includes/Framework/Api/Request.php',
263
+ 'WooCommerce\\Facebook\\Framework\\Api\\Response' => __DIR__ . '/../..' . '/includes/Framework/Api/Response.php',
264
+ 'WooCommerce\\Facebook\\Framework\\Helper' => __DIR__ . '/../..' . '/includes/Framework/Helper.php',
265
+ 'WooCommerce\\Facebook\\Framework\\Lifecycle' => __DIR__ . '/../..' . '/includes/Framework/Lifecycle.php',
266
+ 'WooCommerce\\Facebook\\Framework\\Plugin' => __DIR__ . '/../..' . '/includes/Framework/Plugin.php',
267
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Compatibility' => __DIR__ . '/../..' . '/includes/Framework/Plugin/Compatibility.php',
268
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Dependencies' => __DIR__ . '/../..' . '/includes/Framework/Plugin/Dependencies.php',
269
+ 'WooCommerce\\Facebook\\Framework\\Plugin\\Exception' => __DIR__ . '/../..' . '/includes/Framework/Plugin/Exception.php',
270
+ 'WooCommerce\\Facebook\\Framework\\Utilities\\AsyncRequest' => __DIR__ . '/../..' . '/includes/Framework/Utilities/AsyncRequest.php',
271
+ 'WooCommerce\\Facebook\\Framework\\Utilities\\BackgroundJobHandler' => __DIR__ . '/../..' . '/includes/Framework/Utilities/BackgroundJobHandler.php',
272
+ 'WooCommerce\\Facebook\\Handlers\\Connection' => __DIR__ . '/../..' . '/includes/Handlers/Connection.php',
273
+ 'WooCommerce\\Facebook\\Handlers\\WebHook' => __DIR__ . '/../..' . '/includes/Handlers/WebHook.php',
274
+ 'WooCommerce\\Facebook\\Integrations\\Bookings' => __DIR__ . '/../..' . '/includes/Integrations/Bookings.php',
275
+ 'WooCommerce\\Facebook\\Jobs\\AbstractChainedJob' => __DIR__ . '/../..' . '/includes/Jobs/AbstractChainedJob.php',
276
+ 'WooCommerce\\Facebook\\Jobs\\CleanupSkyvergeFrameworkJobOptions' => __DIR__ . '/../..' . '/includes/Jobs/CleanupSkyvergeFrameworkJobOptions.php',
277
+ 'WooCommerce\\Facebook\\Jobs\\GenerateProductFeed' => __DIR__ . '/../..' . '/includes/Jobs/GenerateProductFeed.php',
278
+ 'WooCommerce\\Facebook\\Jobs\\JobManager' => __DIR__ . '/../..' . '/includes/Jobs/JobManager.php',
279
+ 'WooCommerce\\Facebook\\Jobs\\LoggingTrait' => __DIR__ . '/../..' . '/includes/Jobs/LoggingTrait.php',
280
+ 'WooCommerce\\Facebook\\Lifecycle' => __DIR__ . '/../..' . '/includes/Lifecycle.php',
281
+ 'WooCommerce\\Facebook\\Locale' => __DIR__ . '/../..' . '/includes/Locale.php',
282
+ 'WooCommerce\\Facebook\\ProductSets\\Sync' => __DIR__ . '/../..' . '/includes/ProductSets/Sync.php',
283
+ 'WooCommerce\\Facebook\\ProductSync\\ProductExcludedException' => __DIR__ . '/../..' . '/includes/ProductSync/ProductExcludedException.php',
284
+ 'WooCommerce\\Facebook\\ProductSync\\ProductInvalidException' => __DIR__ . '/../..' . '/includes/ProductSync/ProductInvalidException.php',
285
+ 'WooCommerce\\Facebook\\ProductSync\\ProductValidator' => __DIR__ . '/../..' . '/includes/ProductSync/ProductValidator.php',
286
+ 'WooCommerce\\Facebook\\Product_Categories' => __DIR__ . '/../..' . '/includes/Product_Categories.php',
287
+ 'WooCommerce\\Facebook\\Products' => __DIR__ . '/../..' . '/includes/Products.php',
288
+ 'WooCommerce\\Facebook\\Products\\FBCategories' => __DIR__ . '/../..' . '/includes/Products/FBCategories.php',
289
+ 'WooCommerce\\Facebook\\Products\\Feed' => __DIR__ . '/../..' . '/includes/Products/Feed.php',
290
+ 'WooCommerce\\Facebook\\Products\\GoogleProductTaxonomy' => __DIR__ . '/../..' . '/includes/Products/GoogleProductTaxonomy.php',
291
+ 'WooCommerce\\Facebook\\Products\\Stock' => __DIR__ . '/../..' . '/includes/Products/Stock.php',
292
+ 'WooCommerce\\Facebook\\Products\\Sync' => __DIR__ . '/../..' . '/includes/Products/Sync.php',
293
+ 'WooCommerce\\Facebook\\Products\\Sync\\Background' => __DIR__ . '/../..' . '/includes/Products/Sync/Background.php',
294
+ 'WooCommerce\\Facebook\\Utilities\\Background_Handle_Virtual_Products_Variations' => __DIR__ . '/../..' . '/includes/Utilities/Background_Handle_Virtual_Products_Variations.php',
295
+ 'WooCommerce\\Facebook\\Utilities\\Background_Remove_Duplicate_Visibility_Meta' => __DIR__ . '/../..' . '/includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php',
296
+ 'WooCommerce\\Facebook\\Utilities\\Heartbeat' => __DIR__ . '/../..' . '/includes/Utilities/Heartbeat.php',
297
+ 'WooCommerce\\Facebook\\Utilities\\Shipment' => __DIR__ . '/../..' . '/includes/Utilities/Shipment.php',
298
+ 'WooCommerce\\Facebook\\Utilities\\Tracker' => __DIR__ . '/../..' . '/includes/Utilities/Tracker.php',
299
  );
300
 
301
  public static function getInitializer(ClassLoader $loader)
302
  {
303
  return \Closure::bind(function () use ($loader) {
304
+ $loader->prefixLengthsPsr4 = ComposerStaticInit63d6606890a3bdef14921235e9cba133::$prefixLengthsPsr4;
305
+ $loader->prefixDirsPsr4 = ComposerStaticInit63d6606890a3bdef14921235e9cba133::$prefixDirsPsr4;
306
+ $loader->classMap = ComposerStaticInit63d6606890a3bdef14921235e9cba133::$classMap;
307
 
308
  }, null, ClassLoader::class);
309
  }
vendor/composer/installed.json CHANGED
@@ -154,34 +154,6 @@
154
  ],
155
  "install-path": "./installers"
156
  },
157
- {
158
- "name": "skyverge/wc-plugin-framework",
159
- "version": "5.10.0",
160
- "version_normalized": "5.10.0.0",
161
- "source": {
162
- "type": "git",
163
- "url": "https://github.com/skyverge/wc-plugin-framework.git",
164
- "reference": "e230d7c40286854e49c0cafeec3398cbf2427a64"
165
- },
166
- "dist": {
167
- "type": "zip",
168
- "url": "https://api.github.com/repos/skyverge/wc-plugin-framework/zipball/e230d7c40286854e49c0cafeec3398cbf2427a64",
169
- "reference": "e230d7c40286854e49c0cafeec3398cbf2427a64",
170
- "shasum": ""
171
- },
172
- "require-dev": {
173
- "lucatume/wp-browser": "^2.1"
174
- },
175
- "time": "2020-11-06T21:25:49+00:00",
176
- "type": "library",
177
- "installation-source": "dist",
178
- "description": "The official SkyVerge WooCommerce plugin framework",
179
- "support": {
180
- "source": "https://github.com/skyverge/wc-plugin-framework/tree/5.10.0",
181
- "issues": "https://github.com/skyverge/wc-plugin-framework/issues"
182
- },
183
- "install-path": "../skyverge/wc-plugin-framework"
184
- },
185
  {
186
  "name": "woocommerce/action-scheduler-job-framework",
187
  "version": "2.0.0",
154
  ],
155
  "install-path": "./installers"
156
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  {
158
  "name": "woocommerce/action-scheduler-job-framework",
159
  "version": "2.0.0",
vendor/composer/installed.php CHANGED
@@ -1,31 +1,31 @@
1
  <?php return array(
2
  'root' => array(
3
- 'name' => 'facebookincubator/facebook-for-woocommerce',
4
- 'pretty_version' => 'dev-release/2.6.30',
5
- 'version' => 'dev-release/2.6.30',
6
- 'reference' => '26ee0f8590597b20893f64d839a65f11ebc64b45',
7
  'type' => 'wordpress-plugin',
8
  'install_path' => __DIR__ . '/../../',
9
  'aliases' => array(),
 
 
10
  'dev' => false,
11
  ),
12
  'versions' => array(
13
  'composer/installers' => array(
14
  'pretty_version' => 'v1.12.0',
15
  'version' => '1.12.0.0',
16
- 'reference' => 'd20a64ed3c94748397ff5973488761b22f6d3f19',
17
  'type' => 'composer-plugin',
18
  'install_path' => __DIR__ . '/./installers',
19
  'aliases' => array(),
 
20
  'dev_requirement' => false,
21
  ),
22
  'facebookincubator/facebook-for-woocommerce' => array(
23
- 'pretty_version' => 'dev-release/2.6.30',
24
- 'version' => 'dev-release/2.6.30',
25
- 'reference' => '26ee0f8590597b20893f64d839a65f11ebc64b45',
26
  'type' => 'wordpress-plugin',
27
  'install_path' => __DIR__ . '/../../',
28
  'aliases' => array(),
 
29
  'dev_requirement' => false,
30
  ),
31
  'roundcube/plugin-installer' => array(
@@ -40,22 +40,13 @@
40
  0 => '*',
41
  ),
42
  ),
43
- 'skyverge/wc-plugin-framework' => array(
44
- 'pretty_version' => '5.10.0',
45
- 'version' => '5.10.0.0',
46
- 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
47
- 'type' => 'library',
48
- 'install_path' => __DIR__ . '/../skyverge/wc-plugin-framework',
49
- 'aliases' => array(),
50
- 'dev_requirement' => false,
51
- ),
52
  'woocommerce/action-scheduler-job-framework' => array(
53
  'pretty_version' => '2.0.0',
54
  'version' => '2.0.0.0',
55
- 'reference' => 'b0b21b9cc87e476ba7f8817050b39274ea7d6732',
56
  'type' => 'library',
57
  'install_path' => __DIR__ . '/../woocommerce/action-scheduler-job-framework',
58
  'aliases' => array(),
 
59
  'dev_requirement' => false,
60
  ),
61
  ),
1
  <?php return array(
2
  'root' => array(
3
+ 'pretty_version' => 'dev-release/3.0.0',
4
+ 'version' => 'dev-release/3.0.0',
 
 
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => 'e2a818b69d94e4f9a7078cacaebc2ce404e06a9c',
9
+ 'name' => 'facebookincubator/facebook-for-woocommerce',
10
  'dev' => false,
11
  ),
12
  'versions' => array(
13
  'composer/installers' => array(
14
  'pretty_version' => 'v1.12.0',
15
  'version' => '1.12.0.0',
 
16
  'type' => 'composer-plugin',
17
  'install_path' => __DIR__ . '/./installers',
18
  'aliases' => array(),
19
+ 'reference' => 'd20a64ed3c94748397ff5973488761b22f6d3f19',
20
  'dev_requirement' => false,
21
  ),
22
  'facebookincubator/facebook-for-woocommerce' => array(
23
+ 'pretty_version' => 'dev-release/3.0.0',
24
+ 'version' => 'dev-release/3.0.0',
 
25
  'type' => 'wordpress-plugin',
26
  'install_path' => __DIR__ . '/../../',
27
  'aliases' => array(),
28
+ 'reference' => 'e2a818b69d94e4f9a7078cacaebc2ce404e06a9c',
29
  'dev_requirement' => false,
30
  ),
31
  'roundcube/plugin-installer' => array(
40
  0 => '*',
41
  ),
42
  ),
 
 
 
 
 
 
 
 
 
43
  'woocommerce/action-scheduler-job-framework' => array(
44
  'pretty_version' => '2.0.0',
45
  'version' => '2.0.0.0',
 
46
  'type' => 'library',
47
  'install_path' => __DIR__ . '/../woocommerce/action-scheduler-job-framework',
48
  'aliases' => array(),
49
+ 'reference' => 'b0b21b9cc87e476ba7f8817050b39274ea7d6732',
50
  'dev_requirement' => false,
51
  ),
52
  ),
vendor/composer/platform_check.php CHANGED
@@ -4,8 +4,8 @@
4
 
5
  $issues = array();
6
 
7
- if (!(PHP_VERSION_ID >= 70000)) {
8
- $issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
9
  }
10
 
11
  if ($issues) {
4
 
5
  $issues = array();
6
 
7
+ if (!(PHP_VERSION_ID >= 70400)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
9
  }
10
 
11
  if ($issues) {
vendor/skyverge/wc-plugin-framework/.dist.env DELETED
@@ -1,17 +0,0 @@
1
- WP_ROOT_FOLDER="/path/to/wp/root"
2
- WP_ADMIN_PATH="/wp-admin"
3
- ACCEPTANCE_DB_NAME="your_acceptance_db_name"
4
- ACCEPTANCE_DB_HOST="localhost"
5
- ACCEPTANCE_DB_USER="root"
6
- ACCEPTANCE_DB_PASSWORD="root"
7
- ACCEPTANCE_TABLE_PREFIX="wp_"
8
- INTEGRATION_DB_NAME="your_integration_db_name"
9
- INTEGRATION_DB_HOST="localhost"
10
- INTEGRATION_DB_USER="root"
11
- INTEGRATION_DB_PASSWORD="root"
12
- INTEGRATION_TABLE_PREFIX="wp_"
13
- WP_URL="https://your-test-url.test"
14
- WP_DOMAIN="your-test-url.test"
15
- ADMIN_EMAIL="admin@your-test-url.test"
16
- ADMIN_USERNAME="admin"
17
- ADMIN_PASSWORD="admin"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/.editorconfig DELETED
@@ -1,21 +0,0 @@
1
- # This file is for unifying the coding style for different editors and IDEs
2
- # editorconfig.org
3
-
4
- # WordPress Coding Standards
5
- # http://make.wordpress.org/core/handbook/coding-standards/
6
-
7
- root = true
8
-
9
- [*]
10
- charset = utf-8
11
- end_of_line = lf
12
- insert_final_newline = true
13
- trim_trailing_whitespace = true
14
- indent_style = tab
15
-
16
- [{.jshintrc,*.json,*.yml}]
17
- indent_style = space
18
- indent_size = 2
19
-
20
- [{*.txt}]
21
- end_of_line = crlf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/.gitignore DELETED
@@ -1,26 +0,0 @@
1
- # IDEs
2
- /.settings
3
- /.project
4
- /.idea
5
-
6
- # NPM packages used by Grunt.js
7
- /node_modules
8
-
9
- # NPM debug log
10
- npm-debug.log
11
-
12
- # Composer
13
- /vendor
14
-
15
- # PHPUnit
16
- /tmp
17
-
18
- # Misc
19
- *.zip
20
- .cache
21
- .sass-cache*
22
-
23
- # Tests
24
- codeception.yml
25
- .env
26
- /tests/_data/dump.sql
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/.travis.yml DELETED
@@ -1,102 +0,0 @@
1
- sudo: false
2
-
3
- language: php
4
-
5
- notifications:
6
- email: false
7
-
8
- php:
9
- - '7.3'
10
-
11
- matrix:
12
- fast_finish: true
13
-
14
- services:
15
- - mysql
16
-
17
- cache:
18
- apt: false
19
-
20
- env:
21
- global:
22
- # the WordPress configuration must match the env variables in codeception.travis.yml
23
- - WP_ROOT_FOLDER="/tmp/wordpress"
24
- - WP_ADMIN_PATH="/wp-admin"
25
- - WP_URL="http://127.0.0.1:8888"
26
- - WP_DOMAIN="127.0.0.1:8888"
27
- - ACCEPTANCE_TABLE_PREFIX="wp_"
28
- - ACCEPTANCE_DB_NAME="memberships_acceptance"
29
- - ACCEPTANCE_DB_HOST="127.0.0.1"
30
- - ACCEPTANCE_DB_USER="root"
31
- - ACCEPTANCE_DB_PASSWORD="" # intentionally blank
32
- - INTEGRATION_DB_NAME="memberships_integration"
33
- - INTEGRATION_DB_HOST="127.0.0.1"
34
- - INTEGRATION_DB_USER="root"
35
- - INTEGRATION_DB_PASSWORD="" # intentionally blank
36
- - INTEGRATION_TABLE_PREFIX="wp_"
37
- - ADMIN_EMAIL="admin@localhost.test"
38
- - ADMIN_USERNAME="admin"
39
- - ADMIN_PASSWORD="password"
40
- matrix:
41
- - WP_VERSION=latest
42
-
43
- before_install:
44
- # create the databases that will be used in the tests
45
- - mysql -e "create database IF NOT EXISTS $ACCEPTANCE_DB_NAME;" -uroot
46
- - mysql -e "create database IF NOT EXISTS $INTEGRATION_DB_NAME;" -uroot
47
- # set up folders
48
- - mkdir -p $WP_ROOT_FOLDER
49
- - mkdir tools
50
- # install wp-cli in the `tools` folder
51
- - wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -P $(pwd)/tools/
52
- - chmod +x tools/wp-cli.phar && mv tools/wp-cli.phar tools/wp
53
- # append the `tools` folder to the PATH
54
- - export PATH=$PATH:$(pwd)/tools
55
- # prepend the `vendor/bin` folder the PATH
56
- - export PATH=vendor/bin:$PATH
57
-
58
- install:
59
- - composer install --prefer-dist
60
- - cd $WP_ROOT_FOLDER
61
-
62
- # install and configure WordPress
63
- - wp core download --version=$WP_VERSION
64
- - wp config create --dbname="$ACCEPTANCE_DB_NAME" --dbuser="$ACCEPTANCE_DB_USER" --dbpass="$ACCEPTANCE_DB_PASSWORD" --dbhost="$ACCEPTANCE_DB_HOST" --dbprefix="$ACCEPTANCE_TABLE_PREFIX"
65
- - wp core install --url="$WP_URL" --title="Test" --admin_user="$ADMIN_USERNAME" --admin_password="$ADMIN_PASSWORD" --admin_email="$ADMIN_EMAIL" --skip-email
66
- - wp rewrite structure '/%postname%/' --hard
67
- - wp core update-db
68
-
69
- # copy the plugins to the plugins directory
70
- - cp -r $TRAVIS_BUILD_DIR/tests/_support/plugins/test-plugin $WP_ROOT_FOLDER/wp-content/plugins/
71
- - cp -r $TRAVIS_BUILD_DIR/tests/_support/plugins/gateway-test-plugin $WP_ROOT_FOLDER/wp-content/plugins/
72
- - cp -r $TRAVIS_BUILD_DIR/woocommerce $WP_ROOT_FOLDER/wp-content/plugins/test-plugin/vendor/skyverge/wc-plugin-framework
73
- - cp -r $TRAVIS_BUILD_DIR/woocommerce $WP_ROOT_FOLDER/wp-content/plugins/gateway-test-plugin/vendor/skyverge/wc-plugin-framework
74
-
75
- # install & activate WooCommerce & the plugin
76
- - wp plugin install woocommerce --activate
77
- - wp plugin activate test-plugin
78
- - wp plugin activate gateway-test-plugin
79
-
80
- # verify the active plugins
81
- - wp plugin list --status=active
82
-
83
- # generate the db export
84
- - wp db export $TRAVIS_BUILD_DIR/tests/_data/dump.sql
85
-
86
- # make sure directory is writeable
87
- - cd $TRAVIS_BUILD_DIR
88
- - chmod -R 777 $WP_ROOT_FOLDER
89
-
90
- before_script:
91
- - php -S "$WP_DOMAIN" -t "$WP_ROOT_FOLDER" >/dev/null 2>&1 &
92
- - phantomjs --webdriver=4444 >/dev/null 2>&1 &
93
-
94
- script:
95
- - codecept run unit
96
- - codecept run integration
97
- - codecept run acceptance
98
-
99
- # change this when merged
100
- branches:
101
- only:
102
- - master
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/.tx/config DELETED
@@ -1,9 +0,0 @@
1
- [main]
2
- host = https://www.transifex.com
3
-
4
- [wc-plugin-framework.wc-plugin-framework]
5
- file_filter = woocommerce/i18n/languages/woocommerce-plugin-framework-<lang>.po
6
- source_file = woocommerce/i18n/languages/woocommerce-plugin-framework.pot
7
- source_lang = en
8
- type = PO
9
-
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/Gruntfile.js DELETED
@@ -1,82 +0,0 @@
1
- /* jshint node:true */
2
- module.exports = function( grunt ) {
3
- 'use strict';
4
-
5
- // load all grunt tasks matching the `grunt-*` pattern
6
- require( 'load-grunt-tasks' )( grunt );
7
-
8
- // Show elapsed time
9
- require( 'time-grunt' )( grunt );
10
-
11
- var _ = require( 'underscore' );
12
- var path = require( 'path' );
13
-
14
- var gruntConfig = {};
15
-
16
- // options
17
- gruntConfig.options = {};
18
-
19
- gruntConfig.pkg = grunt.file.readJSON( 'package.json' ),
20
-
21
- // Set folder templates
22
- gruntConfig.dirs = {
23
- lang: 'woocommerce/i18n/languages',
24
- general: {
25
- css: 'woocommerce/assets/css',
26
- js: 'woocommerce/assets/js'
27
- },
28
- gateway : {
29
- css: 'woocommerce/payment-gateway/assets/css',
30
- js: 'woocommerce/payment-gateway/assets/js'
31
- }
32
- };
33
-
34
- function loadConfig( filepath ) {
35
- var object = {};
36
- var key;
37
-
38
- filepath = path.normalize( path.resolve( process.cwd(), filepath ) + '/' )
39
-
40
- var files = grunt.file.glob.sync( '*', { cwd: filepath } );
41
-
42
- files.forEach( function( option ) {
43
- key = option.replace(/\.js$/,'');
44
- object = _.extend( object, require( filepath + option )( grunt ) );
45
- });
46
-
47
- return object;
48
- };
49
-
50
- // load task configs
51
- gruntConfig = _.extend( gruntConfig, loadConfig( './grunt/configs/' ) );
52
-
53
- // Init Grunt
54
- grunt.initConfig( gruntConfig );
55
-
56
- // Load custom tasks
57
- grunt.loadTasks( 'grunt/tasks/' );
58
-
59
- // Register update_translations task
60
- grunt.registerTask( 'update_translations', [
61
- 'makepot',
62
- 'shell:tx_push',
63
- 'shell:tx_pull',
64
- 'potomo'
65
- ] );
66
-
67
- // Register build task
68
- grunt.registerTask( 'build', [
69
- 'coffee',
70
- 'sass',
71
- 'update_translations',
72
- ] );
73
-
74
- // Register default task
75
- grunt.registerTask( 'default', [
76
- 'coffee',
77
- 'sass',
78
- 'makepot',
79
- 'shell:tx_push',
80
- ] );
81
-
82
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/codeception.dist.yml DELETED
@@ -1,22 +0,0 @@
1
- paths:
2
- tests: tests
3
- output: tests/_output
4
- data: tests/_data
5
- support: tests/_support
6
- envs: tests/_envs
7
- actor_suffix: Tester
8
- extensions:
9
- enabled:
10
- - Codeception\Extension\RunFailed
11
- commands:
12
- - Codeception\Command\GenerateWPUnit
13
- - Codeception\Command\GenerateWPRestApi
14
- - Codeception\Command\GenerateWPRestController
15
- - Codeception\Command\GenerateWPRestPostTypeController
16
- - Codeception\Command\GenerateWPAjax
17
- - Codeception\Command\GenerateWPCanonical
18
- - Codeception\Command\GenerateWPXMLRPC
19
- #- Codeception\Command\DbSnapshot
20
- #- tad\Codeception\Command\SearchReplace
21
- params:
22
- - env
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/codeception.local.yml DELETED
@@ -1,12 +0,0 @@
1
- extensions:
2
- enabled:
3
- - tad\WPBrowser\Extension\Copier
4
- config:
5
- tad\WPBrowser\Extension\Copier:
6
- files:
7
- woocommerce: 'tests/_support/plugins/test-plugin/vendor/skyverge/wc-plugin-framework/woocommerce'
8
- tests/_support/plugins/test-plugin: '%WP_ROOT_FOLDER%/wp-content/plugins/test-plugin'
9
- ./woocommerce: 'tests/_support/plugins/gateway-test-plugin/vendor/skyverge/wc-plugin-framework/woocommerce'
10
- tests/_support/plugins/gateway-test-plugin: '%WP_ROOT_FOLDER%/wp-content/plugins/gateway-test-plugin'
11
- params:
12
- - .env
 
 
 
 
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/composer.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "name": "skyverge/wc-plugin-framework",
3
- "description": "The official SkyVerge WooCommerce plugin framework",
4
- "version": "5.10.0",
5
- "require-dev": {
6
- "lucatume/wp-browser": "^2.1"
7
- }
8
- }
 
 
 
 
 
 
 
 
vendor/skyverge/wc-plugin-framework/composer.lock DELETED
@@ -1,7236 +0,0 @@
1
- {
2
- "_readme": [
3
- "This file locks the dependencies of your project to a known state",
4
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
- "This file is @generated automatically"
6
- ],
7
- "content-hash": "b8c6edf9a53f9b7fbb494a16b6a7272f",
8
- "packages": [],
9
- "packages-dev": [
10
- {
11
- "name": "antecedent/patchwork",
12
- "version": "2.1.11",
13
- "source": {
14
- "type": "git",
15
- "url": "https://github.com/antecedent/patchwork.git",
16
- "reference": "ff7aae02f1c5492716fe13d59de4cfc389b8c4b0"
17
- },
18
- "dist": {
19
- "type": "zip",
20
- "url": "https://api.github.com/repos/antecedent/patchwork/zipball/ff7aae02f1c5492716fe13d59de4cfc389b8c4b0",
21
- "reference": "ff7aae02f1c5492716fe13d59de4cfc389b8c4b0",
22
- "shasum": ""
23
- },
24
- "require": {
25
- "php": ">=5.4.0"
26
- },
27
- "require-dev": {
28
- "phpunit/phpunit": ">=4"
29
- },
30
- "type": "library",
31
- "notification-url": "https://packagist.org/downloads/",
32
- "license": [
33
- "MIT"
34
- ],
35
- "authors": [
36
- {
37
- "name": "Ignas Rudaitis",
38
- "email": "ignas.rudaitis@gmail.com"
39
- }
40
- ],
41
- "description": "Method redefinition (monkey-patching) functionality for PHP.",
42
- "homepage": "http://patchwork2.org/",
43
- "keywords": [
44
- "aop",
45
- "aspect",
46
- "interception",
47
- "monkeypatching",
48
- "redefinition",
49
- "runkit",
50
- "testing"
51
- ],
52
- "time": "2019-10-26T07:10:56+00:00"
53
- },
54
- {
55
- "name": "behat/gherkin",
56
- "version": "v4.6.0",
57
- "source": {
58
- "type": "git",
59
- "url": "https://github.com/Behat/Gherkin.git",
60
- "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07"
61
- },
62
- "dist": {
63
- "type": "zip",
64
- "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07",
65
- "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07",
66
- "shasum": ""
67
- },
68
- "require": {
69
- "php": ">=5.3.1"
70
- },
71
- "require-dev": {
72
- "phpunit/phpunit": "~4.5|~5",
73
- "symfony/phpunit-bridge": "~2.7|~3|~4",
74
- "symfony/yaml": "~2.3|~3|~4"
75
- },
76
- "suggest": {
77
- "symfony/yaml": "If you want to parse features, represented in YAML files"
78
- },
79
- "type": "library",
80
- "extra": {
81
- "branch-alias": {
82
- "dev-master": "4.4-dev"
83
- }
84
- },
85
- "autoload": {
86
- "psr-0": {
87
- "Behat\\Gherkin": "src/"
88
- }
89
- },
90
- "notification-url": "https://packagist.org/downloads/",
91
- "license": [
92
- "MIT"
93
- ],
94
- "authors": [
95
- {
96
- "name": "Konstantin Kudryashov",
97
- "email": "ever.zet@gmail.com",
98
- "homepage": "http://everzet.com"
99
- }
100
- ],
101
- "description": "Gherkin DSL parser for PHP 5.3",
102
- "homepage": "http://behat.org/",
103
- "keywords": [
104
- "BDD",
105
- "Behat",
106
- "Cucumber",
107
- "DSL",
108
- "gherkin",
109
- "parser"
110
- ],
111
- "time": "2019-01-16T14:22:17+00:00"
112
- },
113
- {
114
- "name": "codeception/codeception",
115
- "version": "3.1.2",
116
- "source": {
117
- "type": "git",
118
- "url": "https://github.com/Codeception/Codeception.git",
119
- "reference": "5ea172de7b1b2e61dcdd50d73f8368886c549fb4"
120
- },
121
- "dist": {
122
- "type": "zip",
123
- "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5ea172de7b1b2e61dcdd50d73f8368886c549fb4",
124
- "reference": "5ea172de7b1b2e61dcdd50d73f8368886c549fb4",
125
- "shasum": ""
126
- },
127
- "require": {
128
- "behat/gherkin": "^4.4.0",
129
- "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3",
130
- "codeception/stub": "^2.0 | ^3.0",
131
- "ext-curl": "*",
132
- "ext-json": "*",
133
- "ext-mbstring": "*",
134
- "facebook/webdriver": "^1.6.0",
135
- "guzzlehttp/guzzle": "^6.3.0",
136
- "guzzlehttp/psr7": "~1.4",
137
- "hoa/console": "~3.0",
138
- "php": ">=5.6.0 <8.0",
139
- "symfony/browser-kit": ">=2.7 <5.0",
140
- "symfony/console": ">=2.7 <5.0",
141
- "symfony/css-selector": ">=2.7 <5.0",
142
- "symfony/dom-crawler": ">=2.7 <5.0",
143
- "symfony/event-dispatcher": ">=2.7 <5.0",
144
- "symfony/finder": ">=2.7 <5.0",
145
- "symfony/yaml": ">=2.7 <5.0"
146
- },
147
- "require-dev": {
148
- "codeception/specify": "~0.3",
149
- "doctrine/annotations": "^1",
150
- "doctrine/data-fixtures": "^1",
151
- "doctrine/orm": "^2",
152
- "flow/jsonpath": "~0.2",
153
- "monolog/monolog": "~1.8",
154
- "pda/pheanstalk": "~3.0",
155
- "php-amqplib/php-amqplib": "~2.4",
156
- "predis/predis": "^1.0",
157
- "ramsey/uuid-doctrine": "^1.5",
158
- "squizlabs/php_codesniffer": "~2.0",
159
- "symfony/process": ">=2.7 <5.0",
160
- "vlucas/phpdotenv": "^3.0"
161
- },
162
- "suggest": {
163
- "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module",
164
- "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests",
165
- "codeception/specify": "BDD-style code blocks",
166
- "codeception/verify": "BDD-style assertions",
167
- "flow/jsonpath": "For using JSONPath in REST module",
168
- "league/factory-muffin": "For DataFactory module",
169
- "league/factory-muffin-faker": "For Faker support in DataFactory module",
170
- "phpseclib/phpseclib": "for SFTP option in FTP Module",
171
- "stecman/symfony-console-completion": "For BASH autocompletion",
172
- "symfony/phpunit-bridge": "For phpunit-bridge support"
173
- },
174
- "bin": [
175
- "codecept"
176
- ],
177
- "type": "library",
178
- "extra": {
179
- "branch-alias": []
180
- },
181
- "autoload": {
182
- "psr-4": {
183
- "Codeception\\": "src/Codeception",
184
- "Codeception\\Extension\\": "ext"
185
- }
186
- },
187
- "notification-url": "https://packagist.org/downloads/",
188
- "license": [
189
- "MIT"
190
- ],
191
- "authors": [
192
- {
193
- "name": "Michael Bodnarchuk",
194
- "email": "davert@mail.ua",
195
- "homepage": "http://codegyre.com"
196
- }
197
- ],
198
- "description": "BDD-style testing framework",
199
- "homepage": "http://codeception.com/",
200
- "keywords": [
201
- "BDD",
202
- "TDD",
203
- "acceptance testing",
204
- "functional testing",
205
- "unit testing"
206
- ],
207
- "time": "2019-10-19T13:15:55+00:00"
208
- },
209
- {
210
- "name": "codeception/phpunit-wrapper",
211
- "version": "8.1.0",
212
- "source": {
213
- "type": "git",
214
- "url": "https://github.com/Codeception/phpunit-wrapper.git",
215
- "reference": "ee0c1fa08ef6e7f86e3592eccb57139110e5913d"
216
- },
217
- "dist": {
218
- "type": "zip",
219
- "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/ee0c1fa08ef6e7f86e3592eccb57139110e5913d",
220
- "reference": "ee0c1fa08ef6e7f86e3592eccb57139110e5913d",
221
- "shasum": ""
222
- },
223
- "require": {
224
- "php": ">=7.2",
225
- "phpunit/php-code-coverage": "^7.0",
226
- "phpunit/phpunit": "^8.0",
227
- "sebastian/comparator": "^3.0",
228
- "sebastian/diff": "^3.0"
229
- },
230
- "require-dev": {
231
- "codeception/specify": "*",
232
- "vlucas/phpdotenv": "^3.0"
233
- },
234
- "type": "library",
235
- "autoload": {
236
- "psr-4": {
237
- "Codeception\\PHPUnit\\": "src\\"
238
- }
239
- },
240
- "notification-url": "https://packagist.org/downloads/",
241
- "license": [
242
- "MIT"
243
- ],
244
- "authors": [
245
- {
246
- "name": "Davert",
247
- "email": "davert.php@resend.cc"
248
- }
249
- ],
250
- "description": "PHPUnit classes used by Codeception",
251
- "time": "2019-11-24T15:30:35+00:00"
252
- },
253
- {
254
- "name": "codeception/stub",
255
- "version": "3.6.0",
256
- "source": {
257
- "type": "git",
258
- "url": "https://github.com/Codeception/Stub.git",
259
- "reference": "94874f511ab1025b1f4cb927884cdda5004ece64"
260
- },
261
- "dist": {
262
- "type": "zip",
263
- "url": "https://api.github.com/repos/Codeception/Stub/zipball/94874f511ab1025b1f4cb927884cdda5004ece64",
264
- "reference": "94874f511ab1025b1f4cb927884cdda5004ece64",
265
- "shasum": ""
266
- },
267
- "require": {
268
- "phpunit/phpunit": "^8.4"
269
- },
270
- "type": "library",
271
- "autoload": {
272
- "psr-4": {
273
- "Codeception\\": "src/"
274
- }
275
- },
276
- "notification-url": "https://packagist.org/downloads/",
277
- "license": [
278
- "MIT"
279
- ],
280
- "description": "Flexible Stub wrapper for PHPUnit's Mock Builder",
281
- "time": "2019-11-23T20:11:30+00:00"
282
- },
283
- {
284
- "name": "composer/ca-bundle",
285
- "version": "1.2.5",
286
- "source": {
287
- "type": "git",
288
- "url": "https://github.com/composer/ca-bundle.git",
289
- "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149"
290
- },
291
- "dist": {
292
- "type": "zip",
293
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/62e8fc2dc550e5d6d8c9360c7721662670f58149",
294
- "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149",
295
- "shasum": ""
296
- },
297
- "require": {
298
- "ext-openssl": "*",
299
- "ext-pcre": "*",
300
- "php": "^5.3.2 || ^7.0 || ^8.0"
301
- },
302
- "require-dev": {
303
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
304
- "psr/log": "^1.0",
305
- "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
306
- },
307
- "type": "library",
308
- "extra": {
309
- "branch-alias": {
310
- "dev-master": "1.x-dev"
311
- }
312
- },
313
- "autoload": {
314
- "psr-4": {
315
- "Composer\\CaBundle\\": "src"
316
- }
317
- },
318
- "notification-url": "https://packagist.org/downloads/",
319
- "license": [
320
- "MIT"
321
- ],
322
- "authors": [
323
- {
324
- "name": "Jordi Boggiano",
325
- "email": "j.boggiano@seld.be",
326
- "homepage": "http://seld.be"
327
- }
328
- ],
329
- "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
330
- "keywords": [
331
- "cabundle",
332
- "cacert",
333
- "certificate",
334
- "ssl",
335
- "tls"
336
- ],
337
- "time": "2019-12-11T14:44:42+00:00"
338
- },
339
- {
340
- "name": "composer/composer",
341
- "version": "1.9.1",
342
- "source": {
343
- "type": "git",
344
- "url": "https://github.com/composer/composer.git",
345
- "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f"
346
- },
347
- "dist": {
348
- "type": "zip",
349
- "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f",
350
- "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f",
351
- "shasum": ""
352
- },
353
- "require": {
354
- "composer/ca-bundle": "^1.0",
355
- "composer/semver": "^1.0",
356
- "composer/spdx-licenses": "^1.2",
357
- "composer/xdebug-handler": "^1.1",
358
- "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
359
- "php": "^5.3.2 || ^7.0",
360
- "psr/log": "^1.0",
361
- "seld/jsonlint": "^1.4",
362
- "seld/phar-utils": "^1.0",
363
- "symfony/console": "^2.7 || ^3.0 || ^4.0",
364
- "symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
365
- "symfony/finder": "^2.7 || ^3.0 || ^4.0",
366
- "symfony/process": "^2.7 || ^3.0 || ^4.0"
367
- },
368
- "conflict": {
369
- "symfony/console": "2.8.38"
370
- },
371
- "require-dev": {
372
- "phpunit/phpunit": "^4.8.35 || ^5.7",
373
- "phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
374
- },
375
- "suggest": {
376
- "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
377
- "ext-zip": "Enabling the zip extension allows you to unzip archives",
378
- "ext-zlib": "Allow gzip compression of HTTP requests"
379
- },
380
- "bin": [
381
- "bin/composer"
382
- ],
383
- "type": "library",
384
- "extra": {
385
- "branch-alias": {
386
- "dev-master": "1.9-dev"
387
- }
388
- },
389
- "autoload": {
390
- "psr-4": {
391
- "Composer\\": "src/Composer"
392
- }
393
- },
394
- "notification-url": "https://packagist.org/downloads/",
395
- "license": [
396
- "MIT"
397
- ],
398
- "authors": [
399
- {
400
- "name": "Nils Adermann",
401
- "email": "naderman@naderman.de",
402
- "homepage": "http://www.naderman.de"
403
- },
404
- {
405
- "name": "Jordi Boggiano",
406
- "email": "j.boggiano@seld.be",
407
- "homepage": "http://seld.be"
408
- }
409
- ],
410
- "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
411
- "homepage": "https://getcomposer.org/",
412
- "keywords": [
413
- "autoload",
414
- "dependency",
415
- "package"
416
- ],
417
- "time": "2019-11-01T16:20:17+00:00"
418
- },
419
- {
420
- "name": "composer/semver",
421
- "version": "1.5.0",
422
- "source": {
423
- "type": "git",
424
- "url": "https://github.com/composer/semver.git",
425
- "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e"
426
- },
427
- "dist": {
428
- "type": "zip",
429
- "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
430
- "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e",
431
- "shasum": ""
432
- },
433
- "require": {
434
- "php": "^5.3.2 || ^7.0"
435
- },
436
- "require-dev": {
437
- "phpunit/phpunit": "^4.5 || ^5.0.5",
438
- "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
439
- },
440
- "type": "library",
441
- "extra": {
442
- "branch-alias": {
443
- "dev-master": "1.x-dev"
444
- }
445
- },
446
- "autoload": {
447
- "psr-4": {
448
- "Composer\\Semver\\": "src"
449
- }
450
- },
451
- "notification-url": "https://packagist.org/downloads/",
452
- "license": [
453
- "MIT"
454
- ],
455
- "authors": [
456
- {
457
- "name": "Nils Adermann",
458
- "email": "naderman@naderman.de",
459
- "homepage": "http://www.naderman.de"
460
- },
461
- {
462
- "name": "Jordi Boggiano",
463
- "email": "j.boggiano@seld.be",
464
- "homepage": "http://seld.be"
465
- },
466
- {
467
- "name": "Rob Bast",
468
- "email": "rob.bast@gmail.com",
469
- "homepage": "http://robbast.nl"
470
- }
471
- ],
472
- "description": "Semver library that offers utilities, version constraint parsing and validation.",
473
- "keywords": [
474
- "semantic",
475
- "semver",
476
- "validation",
477
- "versioning"
478
- ],
479
- "time": "2019-03-19T17:25:45+00:00"
480
- },
481
- {
482
- "name": "composer/spdx-licenses",
483
- "version": "1.5.2",
484
- "source": {
485
- "type": "git",
486
- "url": "https://github.com/composer/spdx-licenses.git",
487
- "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5"
488
- },
489
- "dist": {
490
- "type": "zip",
491
- "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5",
492
- "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5",
493
- "shasum": ""
494
- },
495
- "require": {
496
- "php": "^5.3.2 || ^7.0 || ^8.0"
497
- },
498
- "require-dev": {
499
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7"
500
- },
501
- "type": "library",
502
- "extra": {
503
- "branch-alias": {
504
- "dev-master": "1.x-dev"
505
- }
506
- },
507
- "autoload": {
508
- "psr-4": {
509
- "Composer\\Spdx\\": "src"
510
- }
511
- },
512
- "notification-url": "https://packagist.org/downloads/",
513
- "license": [
514
- "MIT"
515
- ],
516
- "authors": [
517
- {
518
- "name": "Nils Adermann",
519
- "email": "naderman@naderman.de",
520
- "homepage": "http://www.naderman.de"
521
- },
522
- {
523
- "name": "Jordi Boggiano",
524
- "email": "j.boggiano@seld.be",
525
- "homepage": "http://seld.be"
526
- },
527
- {
528
- "name": "Rob Bast",
529
- "email": "rob.bast@gmail.com",
530
- "homepage": "http://robbast.nl"
531
- }
532
- ],
533
- "description": "SPDX licenses list and validation library.",
534
- "keywords": [
535
- "license",
536
- "spdx",
537
- "validator"
538
- ],
539
- "time": "2019-07-29T10:31:59+00:00"
540
- },
541
- {
542
- "name": "composer/xdebug-handler",
543
- "version": "1.4.0",
544
- "source": {
545
- "type": "git",
546
- "url": "https://github.com/composer/xdebug-handler.git",
547
- "reference": "cbe23383749496fe0f373345208b79568e4bc248"
548
- },
549
- "dist": {
550
- "type": "zip",
551
- "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248",
552
- "reference": "cbe23383749496fe0f373345208b79568e4bc248",
553
- "shasum": ""
554
- },
555
- "require": {
556
- "php": "^5.3.2 || ^7.0 || ^8.0",
557
- "psr/log": "^1.0"
558
- },
559
- "require-dev": {
560
- "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
561
- },
562
- "type": "library",
563
- "autoload": {
564
- "psr-4": {
565
- "Composer\\XdebugHandler\\": "src"
566
- }
567
- },
568
- "notification-url": "https://packagist.org/downloads/",
569
- "license": [
570
- "MIT"
571
- ],
572
- "authors": [
573
- {
574
- "name": "John Stevenson",
575
- "email": "john-stevenson@blueyonder.co.uk"
576
- }
577
- ],
578
- "description": "Restarts a process without Xdebug.",
579
- "keywords": [
580
- "Xdebug",
581
- "performance"
582
- ],
583
- "time": "2019-11-06T16:40:04+00:00"
584
- },
585
- {
586
- "name": "cweagans/composer-patches",
587
- "version": "1.6.7",
588
- "source": {
589
- "type": "git",
590
- "url": "https://github.com/cweagans/composer-patches.git",
591
- "reference": "2e6f72a2ad8d59cd7e2b729f218bf42adb14f590"
592
- },
593
- "dist": {
594
- "type": "zip",
595
- "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/2e6f72a2ad8d59cd7e2b729f218bf42adb14f590",
596
- "reference": "2e6f72a2ad8d59cd7e2b729f218bf42adb14f590",
597
- "shasum": ""
598
- },
599
- "require": {
600
- "composer-plugin-api": "^1.0",
601
- "php": ">=5.3.0"
602
- },
603
- "require-dev": {
604
- "composer/composer": "~1.0",
605
- "phpunit/phpunit": "~4.6"
606
- },
607
- "type": "composer-plugin",
608
- "extra": {
609
- "class": "cweagans\\Composer\\Patches"
610
- },
611
- "autoload": {
612
- "psr-4": {
613
- "cweagans\\Composer\\": "src"
614
- }
615
- },
616
- "notification-url": "https://packagist.org/downloads/",
617
- "license": [
618
- "BSD-3-Clause"
619
- ],
620
- "authors": [
621
- {
622
- "name": "Cameron Eagans",
623
- "email": "me@cweagans.net"
624
- }
625
- ],
626
- "description": "Provides a way to patch Composer packages.",
627
- "time": "2019-08-29T20:11:49+00:00"
628
- },
629
- {
630
- "name": "dg/mysql-dump",
631
- "version": "v1.5.1",
632
- "source": {
633
- "type": "git",
634
- "url": "https://github.com/dg/MySQL-dump.git",
635
- "reference": "e0e287b715b43293773a8b0edf8514f606e01780"
636
- },
637
- "dist": {
638
- "type": "zip",
639
- "url": "https://api.github.com/repos/dg/MySQL-dump/zipball/e0e287b715b43293773a8b0edf8514f606e01780",
640
- "reference": "e0e287b715b43293773a8b0edf8514f606e01780",
641
- "shasum": ""
642
- },
643
- "require": {
644
- "php": ">=5.6"
645
- },
646
- "type": "library",
647
- "autoload": {
648
- "classmap": [
649
- "src/"
650
- ]
651
- },
652
- "notification-url": "https://packagist.org/downloads/",
653
- "license": [
654
- "BSD-3-Clause"
655
- ],
656
- "authors": [
657
- {
658
- "name": "David Grudl",
659
- "homepage": "http://davidgrudl.com"
660
- }
661
- ],
662
- "description": "MySQL database dump.",
663
- "homepage": "https://github.com/dg/MySQL-dump",
664
- "keywords": [
665
- "mysql"
666
- ],
667
- "time": "2019-09-10T21:36:25+00:00"
668
- },
669
- {
670
- "name": "doctrine/inflector",
671
- "version": "1.3.1",
672
- "source": {
673
- "type": "git",
674
- "url": "https://github.com/doctrine/inflector.git",
675
- "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1"
676
- },
677
- "dist": {
678
- "type": "zip",
679
- "url": "https://api.github.com/repos/doctrine/inflector/zipball/ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
680
- "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
681
- "shasum": ""
682
- },
683
- "require": {
684
- "php": "^7.1"
685
- },
686
- "require-dev": {
687
- "phpunit/phpunit": "^6.2"
688
- },
689
- "type": "library",
690
- "extra": {
691
- "branch-alias": {
692
- "dev-master": "1.3.x-dev"
693
- }
694
- },
695
- "autoload": {
696
- "psr-4": {
697
- "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
698
- }
699
- },
700
- "notification-url": "https://packagist.org/downloads/",
701
- "license": [
702
- "MIT"
703
- ],
704
- "authors": [
705
- {
706
- "name": "Guilherme Blanco",
707
- "email": "guilhermeblanco@gmail.com"
708
- },
709
- {
710
- "name": "Roman Borschel",
711
- "email": "roman@code-factory.org"
712
- },
713
- {
714
- "name": "Benjamin Eberlei",
715
- "email": "kontakt@beberlei.de"
716
- },
717
- {
718
- "name": "Jonathan Wage",
719
- "email": "jonwage@gmail.com"
720
- },
721
- {
722
- "name": "Johannes Schmitt",
723
- "email": "schmittjoh@gmail.com"
724
- }
725
- ],
726
- "description": "Common String Manipulations with regard to casing and singular/plural rules.",
727
- "homepage": "http://www.doctrine-project.org",
728
- "keywords": [
729
- "inflection",
730
- "pluralize",
731
- "singularize",
732
- "string"
733
- ],
734
- "time": "2019-10-30T19:59:35+00:00"
735
- },
736
- {
737
- "name": "doctrine/instantiator",
738
- "version": "1.3.0",
739
- "source": {
740
- "type": "git",
741
- "url": "https://github.com/doctrine/instantiator.git",
742
- "reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
743
- },
744
- "dist": {
745
- "type": "zip",
746
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
747
- "reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
748
- "shasum": ""
749
- },
750
- "require": {
751
- "php": "^7.1"
752
- },
753
- "require-dev": {
754
- "doctrine/coding-standard": "^6.0",
755
- "ext-pdo": "*",
756
- "ext-phar": "*",
757
- "phpbench/phpbench": "^0.13",
758
- "phpstan/phpstan-phpunit": "^0.11",
759
- "phpstan/phpstan-shim": "^0.11",
760
- "phpunit/phpunit": "^7.0"
761
- },
762
- "type": "library",
763
- "extra": {
764
- "branch-alias": {
765
- "dev-master": "1.2.x-dev"
766
- }
767
- },
768
- "autoload": {
769
- "psr-4": {
770
- "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
771
- }
772
- },
773
- "notification-url": "https://packagist.org/downloads/",
774
- "license": [
775
- "MIT"
776
- ],
777
- "authors": [
778
- {
779
- "name": "Marco Pivetta",
780
- "email": "ocramius@gmail.com",
781
- "homepage": "http://ocramius.github.com/"
782
- }
783
- ],
784
- "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
785
- "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
786
- "keywords": [
787
- "constructor",
788
- "instantiate"
789
- ],
790
- "time": "2019-10-21T16:45:58+00:00"
791
- },
792
- {
793
- "name": "facebook/webdriver",
794
- "version": "1.7.1",
795
- "source": {
796
- "type": "git",
797
- "url": "https://github.com/facebook/php-webdriver.git",
798
- "reference": "e43de70f3c7166169d0f14a374505392734160e5"
799
- },
800
- "dist": {
801
- "type": "zip",
802
- "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/e43de70f3c7166169d0f14a374505392734160e5",
803
- "reference": "e43de70f3c7166169d0f14a374505392734160e5",
804
- "shasum": ""
805
- },
806
- "require": {
807
- "ext-curl": "*",
808
- "ext-json": "*",
809
- "ext-mbstring": "*",
810
- "ext-zip": "*",
811
- "php": "^5.6 || ~7.0",
812
- "symfony/process": "^2.8 || ^3.1 || ^4.0"
813
- },
814
- "require-dev": {
815
- "friendsofphp/php-cs-fixer": "^2.0",
816
- "jakub-onderka/php-parallel-lint": "^0.9.2",
817
- "php-coveralls/php-coveralls": "^2.0",
818
- "php-mock/php-mock-phpunit": "^1.1",
819
- "phpunit/phpunit": "^5.7",
820
- "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0",
821
- "squizlabs/php_codesniffer": "^2.6",
822
- "symfony/var-dumper": "^3.3 || ^4.0"
823
- },
824
- "suggest": {
825
- "ext-SimpleXML": "For Firefox profile creation"
826
- },
827
- "type": "library",
828
- "extra": {
829
- "branch-alias": {
830
- "dev-community": "1.5-dev"
831
- }
832
- },
833
- "autoload": {
834
- "psr-4": {
835
- "Facebook\\WebDriver\\": "lib/"
836
- }
837
- },
838
- "notification-url": "https://packagist.org/downloads/",
839
- "license": [
840
- "Apache-2.0"
841
- ],
842
- "description": "A PHP client for Selenium WebDriver",
843
- "homepage": "https://github.com/facebook/php-webdriver",
844
- "keywords": [
845
- "facebook",
846
- "php",
847
- "selenium",
848
- "webdriver"
849
- ],
850
- "time": "2019-06-13T08:02:18+00:00"
851
- },
852
- {
853
- "name": "gettext/gettext",
854
- "version": "v4.8.2",
855
- "source": {
856
- "type": "git",
857
- "url": "https://github.com/php-gettext/Gettext.git",
858
- "reference": "e474f872f2c8636cf53fd283ec4ce1218f3d236a"
859
- },
860
- "dist": {
861
- "type": "zip",
862
- "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/e474f872f2c8636cf53fd283ec4ce1218f3d236a",
863
- "reference": "e474f872f2c8636cf53fd283ec4ce1218f3d236a",
864
- "shasum": ""
865
- },
866
- "require": {
867
- "gettext/languages": "^2.3",
868
- "php": ">=5.4.0"
869
- },
870
- "require-dev": {
871
- "illuminate/view": "*",
872
- "phpunit/phpunit": "^4.8|^5.7|^6.5",
873
- "squizlabs/php_codesniffer": "^3.0",
874
- "symfony/yaml": "~2",
875
- "twig/extensions": "*",
876
- "twig/twig": "^1.31|^2.0"
877
- },
878
- "suggest": {
879
- "illuminate/view": "Is necessary if you want to use the Blade extractor",
880
- "symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator",
881
- "twig/extensions": "Is necessary if you want to use the Twig extractor",
882
- "twig/twig": "Is necessary if you want to use the Twig extractor"
883
- },
884
- "type": "library",
885
- "autoload": {
886
- "psr-4": {
887
- "Gettext\\": "src"
888
- }
889
- },
890
- "notification-url": "https://packagist.org/downloads/",
891
- "license": [
892
- "MIT"
893
- ],
894
- "authors": [
895
- {
896
- "name": "Oscar Otero",
897
- "email": "oom@oscarotero.com",
898
- "homepage": "http://oscarotero.com",
899
- "role": "Developer"
900
- }
901
- ],
902
- "description": "PHP gettext manager",
903
- "homepage": "https://github.com/oscarotero/Gettext",
904
- "keywords": [
905
- "JS",
906
- "gettext",
907
- "i18n",
908
- "mo",
909
- "po",
910
- "translation"
911
- ],
912
- "time": "2019-12-02T10:21:14+00:00"
913
- },
914
- {
915
- "name": "gettext/languages",
916
- "version": "2.6.0",
917
- "source": {
918
- "type": "git",
919
- "url": "https://github.com/php-gettext/Languages.git",
920
- "reference": "38ea0482f649e0802e475f0ed19fa993bcb7a618"
921
- },
922
- "dist": {
923
- "type": "zip",
924
- "url": "https://api.github.com/repos/php-gettext/Languages/zipball/38ea0482f649e0802e475f0ed19fa993bcb7a618",
925
- "reference": "38ea0482f649e0802e475f0ed19fa993bcb7a618",
926
- "shasum": ""
927
- },
928
- "require": {
929
- "php": ">=5.3"
930
- },
931
- "require-dev": {
932
- "friendsofphp/php-cs-fixer": "^2.16.0",
933
- "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4"
934
- },
935
- "bin": [
936
- "bin/export-plural-rules"
937
- ],
938
- "type": "library",
939
- "autoload": {
940
- "psr-4": {
941
- "Gettext\\Languages\\": "src/"
942
- }
943
- },
944
- "notification-url": "https://packagist.org/downloads/",
945
- "license": [
946
- "MIT"
947
- ],
948
- "authors": [
949
- {
950
- "name": "Michele Locati",
951
- "email": "mlocati@gmail.com",
952
- "role": "Developer"
953
- }
954
- ],
955
- "description": "gettext languages with plural rules",
956
- "homepage": "https://github.com/php-gettext/Languages",
957
- "keywords": [
958
- "cldr",
959
- "i18n",
960
- "internationalization",
961
- "l10n",
962
- "language",
963
- "languages",
964
- "localization",
965
- "php",
966
- "plural",
967
- "plural rules",
968
- "plurals",
969
- "translate",
970
- "translations",
971
- "unicode"
972
- ],
973
- "time": "2019-11-13T10:30:21+00:00"
974
- },
975
- {
976
- "name": "gumlet/php-image-resize",
977
- "version": "1.9.2",
978
- "source": {
979
- "type": "git",
980
- "url": "https://github.com/gumlet/php-image-resize.git",
981
- "reference": "06339a9c1b167acd58173db226f57957a6617547"
982
- },
983
- "dist": {
984
- "type": "zip",
985
- "url": "https://api.github.com/repos/gumlet/php-image-resize/zipball/06339a9c1b167acd58173db226f57957a6617547",
986
- "reference": "06339a9c1b167acd58173db226f57957a6617547",
987
- "shasum": ""
988
- },
989
- "require": {
990
- "ext-fileinfo": "*",
991
- "ext-gd": "*",
992
- "php": ">=5.5.0"
993
- },
994
- "require-dev": {
995
- "apigen/apigen": "^4.1",
996
- "ext-exif": "*",
997
- "ext-gd": "*",
998
- "php-coveralls/php-coveralls": "^2.1",
999
- "phpunit/phpunit": "^4.8"
1000
- },
1001
- "suggest": {
1002
- "ext-exif": "Auto-rotate jpeg files"
1003
- },
1004
- "type": "library",
1005
- "autoload": {
1006
- "psr-4": {
1007
- "Gumlet\\": "lib/"
1008
- }
1009
- },
1010
- "notification-url": "https://packagist.org/downloads/",
1011
- "license": [
1012
- "MIT"
1013
- ],
1014
- "authors": [
1015
- {
1016
- "name": "Aditya Patadia",
1017
- "homepage": "http://aditya.patadia.org/"
1018
- }
1019
- ],
1020
- "description": "PHP class to re-size and scale images",
1021
- "homepage": "https://github.com/gumlet/php-image-resize",
1022
- "keywords": [
1023
- "image",
1024
- "php",
1025
- "resize",
1026
- "scale"
1027
- ],
1028
- "time": "2019-01-01T13:53:00+00:00"
1029
- },
1030
- {
1031
- "name": "guzzlehttp/guzzle",
1032
- "version": "6.5.0",
1033
- "source": {
1034
- "type": "git",
1035
- "url": "https://github.com/guzzle/guzzle.git",
1036
- "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5"
1037
- },
1038
- "dist": {
1039
- "type": "zip",
1040
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5",
1041
- "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5",
1042
- "shasum": ""
1043
- },
1044
- "require": {
1045
- "ext-json": "*",
1046
- "guzzlehttp/promises": "^1.0",
1047
- "guzzlehttp/psr7": "^1.6.1",
1048
- "php": ">=5.5"
1049
- },
1050
- "require-dev": {
1051
- "ext-curl": "*",
1052
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
1053
- "psr/log": "^1.1"
1054
- },
1055
- "suggest": {
1056
- "ext-intl": "Required for Internationalized Domain Name (IDN) support",
1057
- "psr/log": "Required for using the Log middleware"
1058
- },
1059
- "type": "library",
1060
- "extra": {
1061
- "branch-alias": {
1062
- "dev-master": "6.5-dev"
1063
- }
1064
- },
1065
- "autoload": {
1066
- "psr-4": {
1067
- "GuzzleHttp\\": "src/"
1068
- },
1069
- "files": [
1070
- "src/functions_include.php"
1071
- ]
1072
- },
1073
- "notification-url": "https://packagist.org/downloads/",
1074
- "license": [
1075
- "MIT"
1076
- ],
1077
- "authors": [
1078
- {
1079
- "name": "Michael Dowling",
1080
- "email": "mtdowling@gmail.com",
1081
- "homepage": "https://github.com/mtdowling"
1082
- }
1083
- ],
1084
- "description": "Guzzle is a PHP HTTP client library",
1085
- "homepage": "http://guzzlephp.org/",
1086
- "keywords": [
1087
- "client",
1088
- "curl",
1089
- "framework",
1090
- "http",
1091
- "http client",
1092
- "rest",
1093
- "web service"
1094
- ],
1095
- "time": "2019-12-07T18:20:45+00:00"
1096
- },
1097
- {
1098
- "name": "guzzlehttp/promises",
1099
- "version": "v1.3.1",
1100
- "source": {
1101
- "type": "git",
1102
- "url": "https://github.com/guzzle/promises.git",
1103
- "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
1104
- },
1105
- "dist": {
1106
- "type": "zip",
1107
- "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
1108
- "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
1109
- "shasum": ""
1110
- },
1111
- "require": {
1112
- "php": ">=5.5.0"
1113
- },
1114
- "require-dev": {
1115
- "phpunit/phpunit": "^4.0"
1116
- },
1117
- "type": "library",
1118
- "extra": {
1119
- "branch-alias": {
1120
- "dev-master": "1.4-dev"
1121
- }
1122
- },
1123
- "autoload": {
1124
- "psr-4": {
1125
- "GuzzleHttp\\Promise\\": "src/"
1126
- },
1127
- "files": [
1128
- "src/functions_include.php"
1129
- ]
1130
- },
1131
- "notification-url": "https://packagist.org/downloads/",
1132
- "license": [
1133
- "MIT"
1134
- ],
1135
- "authors": [
1136
- {
1137
- "name": "Michael Dowling",
1138
- "email": "mtdowling@gmail.com",
1139
- "homepage": "https://github.com/mtdowling"
1140
- }
1141
- ],
1142
- "description": "Guzzle promises library",
1143
- "keywords": [
1144
- "promise"
1145
- ],
1146
- "time": "2016-12-20T10:07:11+00:00"
1147
- },
1148
- {
1149
- "name": "guzzlehttp/psr7",
1150
- "version": "1.6.1",
1151
- "source": {
1152
- "type": "git",
1153
- "url": "https://github.com/guzzle/psr7.git",
1154
- "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
1155
- },
1156
- "dist": {
1157
- "type": "zip",
1158
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
1159
- "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
1160
- "shasum": ""
1161
- },
1162
- "require": {
1163
- "php": ">=5.4.0",
1164
- "psr/http-message": "~1.0",
1165
- "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
1166
- },
1167
- "provide": {
1168
- "psr/http-message-implementation": "1.0"
1169
- },
1170
- "require-dev": {
1171
- "ext-zlib": "*",
1172
- "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
1173
- },
1174
- "suggest": {
1175
- "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
1176
- },
1177
- "type": "library",
1178
- "extra": {
1179
- "branch-alias": {
1180
- "dev-master": "1.6-dev"
1181
- }
1182
- },
1183
- "autoload": {
1184
- "psr-4": {
1185
- "GuzzleHttp\\Psr7\\": "src/"
1186
- },
1187
- "files": [
1188
- "src/functions_include.php"
1189
- ]
1190
- },
1191
- "notification-url": "https://packagist.org/downloads/",
1192
- "license": [
1193
- "MIT"
1194
- ],
1195
- "authors": [
1196
- {
1197
- "name": "Michael Dowling",
1198
- "email": "mtdowling@gmail.com",
1199
- "homepage": "https://github.com/mtdowling"
1200
- },
1201
- {
1202
- "name": "Tobias Schultze",
1203
- "homepage": "https://github.com/Tobion"
1204
- }
1205
- ],
1206
- "description": "PSR-7 message implementation that also provides common utility methods",
1207
- "keywords": [
1208
- "http",
1209
- "message",
1210
- "psr-7",
1211
- "request",
1212
- "response",
1213
- "stream",
1214
- "uri",
1215
- "url"
1216
- ],
1217
- "time": "2019-07-01T23:21:34+00:00"
1218
- },
1219
- {
1220
- "name": "hautelook/phpass",
1221
- "version": "0.3.5",
1222
- "source": {
1223
- "type": "git",
1224
- "url": "https://github.com/hautelook/phpass.git",
1225
- "reference": "b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd"
1226
- },
1227
- "dist": {
1228
- "type": "zip",
1229
- "url": "https://api.github.com/repos/hautelook/phpass/zipball/b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd",
1230
- "reference": "b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd",
1231
- "shasum": ""
1232
- },
1233
- "require": {
1234
- "php": ">=5.3.3"
1235
- },
1236
- "type": "library",
1237
- "autoload": {
1238
- "psr-0": {
1239
- "Hautelook": "src/"
1240
- }
1241
- },
1242
- "notification-url": "https://packagist.org/downloads/",
1243
- "license": [
1244
- "Public Domain"
1245
- ],
1246
- "authors": [
1247
- {
1248
- "name": "Solar Designer",
1249
- "email": "solar@openwall.com",
1250
- "homepage": "http://openwall.com/phpass/"
1251
- }
1252
- ],
1253
- "description": "Portable PHP password hashing framework",
1254
- "homepage": "http://github.com/hautelook/phpass/",
1255
- "keywords": [
1256
- "blowfish",
1257
- "crypt",
1258
- "password",
1259
- "security"
1260
- ],
1261
- "time": "2012-08-31T00:00:00+00:00"
1262
- },
1263
- {
1264
- "name": "hoa/consistency",
1265
- "version": "1.17.05.02",
1266
- "source": {
1267
- "type": "git",
1268
- "url": "https://github.com/hoaproject/Consistency.git",
1269
- "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f"
1270
- },
1271
- "dist": {
1272
- "type": "zip",
1273
- "url": "https://api.github.com/repos/hoaproject/Consistency/zipball/fd7d0adc82410507f332516faf655b6ed22e4c2f",
1274
- "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f",
1275
- "shasum": ""
1276
- },
1277
- "require": {
1278
- "hoa/exception": "~1.0",
1279
- "php": ">=5.5.0"
1280
- },
1281
- "require-dev": {
1282
- "hoa/stream": "~1.0",
1283
- "hoa/test": "~2.0"
1284
- },
1285
- "type": "library",
1286
- "extra": {
1287
- "branch-alias": {
1288
- "dev-master": "1.x-dev"
1289
- }
1290
- },
1291
- "autoload": {
1292
- "psr-4": {
1293
- "Hoa\\Consistency\\": "."
1294
- },
1295
- "files": [
1296
- "Prelude.php"
1297
- ]
1298
- },
1299
- "notification-url": "https://packagist.org/downloads/",
1300
- "license": [
1301
- "BSD-3-Clause"
1302
- ],
1303
- "authors": [
1304
- {
1305
- "name": "Ivan Enderlin",
1306
- "email": "ivan.enderlin@hoa-project.net"
1307
- },
1308
- {
1309
- "name": "Hoa community",
1310
- "homepage": "https://hoa-project.net/"
1311
- }
1312
- ],
1313
- "description": "The Hoa\\Consistency library.",
1314
- "homepage": "https://hoa-project.net/",
1315
- "keywords": [
1316
- "autoloader",
1317
- "callable",
1318
- "consistency",
1319
- "entity",
1320
- "flex",
1321
- "keyword",
1322
- "library"
1323
- ],
1324
- "time": "2017-05-02T12:18:12+00:00"
1325
- },
1326
- {
1327
- "name": "hoa/console",
1328
- "version": "3.17.05.02",
1329
- "source": {
1330
- "type": "git",
1331
- "url": "https://github.com/hoaproject/Console.git",
1332
- "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66"
1333
- },
1334
- "dist": {
1335
- "type": "zip",
1336
- "url": "https://api.github.com/repos/hoaproject/Console/zipball/e231fd3ea70e6d773576ae78de0bdc1daf331a66",
1337
- "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66",
1338
- "shasum": ""
1339
- },
1340
- "require": {
1341
- "hoa/consistency": "~1.0",
1342
- "hoa/event": "~1.0",
1343
- "hoa/exception": "~1.0",
1344
- "hoa/file": "~1.0",
1345
- "hoa/protocol": "~1.0",
1346
- "hoa/stream": "~1.0",
1347
- "hoa/ustring": "~4.0"
1348
- },
1349
- "require-dev": {
1350
- "hoa/test": "~2.0"
1351
- },
1352
- "suggest": {
1353
- "ext-pcntl": "To enable hoa://Event/Console/Window:resize.",
1354
- "hoa/dispatcher": "To use the console kit.",
1355
- "hoa/router": "To use the console kit."
1356
- },
1357
- "type": "library",
1358
- "extra": {
1359
- "branch-alias": {
1360
- "dev-master": "3.x-dev"
1361
- }
1362
- },
1363
- "autoload": {
1364
- "psr-4": {
1365
- "Hoa\\Console\\": "."
1366
- }
1367
- },
1368
- "notification-url": "https://packagist.org/downloads/",
1369
- "license": [
1370
- "BSD-3-Clause"
1371
- ],
1372
- "authors": [
1373
- {
1374
- "name": "Ivan Enderlin",
1375
- "email": "ivan.enderlin@hoa-project.net"
1376
- },
1377
- {
1378
- "name": "Hoa community",
1379
- "homepage": "https://hoa-project.net/"
1380
- }
1381
- ],
1382
- "description": "The Hoa\\Console library.",
1383
- "homepage": "https://hoa-project.net/",
1384
- "keywords": [
1385
- "autocompletion",
1386
- "chrome",
1387
- "cli",
1388
- "console",
1389
- "cursor",
1390
- "getoption",
1391
- "library",
1392
- "option",
1393
- "parser",
1394
- "processus",
1395
- "readline",
1396
- "terminfo",
1397
- "tput",
1398
- "window"
1399
- ],
1400
- "time": "2017-05-02T12:26:19+00:00"
1401
- },
1402
- {
1403
- "name": "hoa/event",
1404
- "version": "1.17.01.13",
1405
- "source": {
1406
- "type": "git",
1407
- "url": "https://github.com/hoaproject/Event.git",
1408
- "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54"
1409
- },
1410
- "dist": {
1411
- "type": "zip",
1412
- "url": "https://api.github.com/repos/hoaproject/Event/zipball/6c0060dced212ffa3af0e34bb46624f990b29c54",
1413
- "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54",
1414
- "shasum": ""
1415
- },
1416
- "require": {
1417
- "hoa/consistency": "~1.0",
1418
- "hoa/exception": "~1.0"
1419
- },
1420
- "require-dev": {
1421
- "hoa/test": "~2.0"
1422
- },
1423
- "type": "library",
1424
- "extra": {
1425
- "branch-alias": {
1426
- "dev-master": "1.x-dev"
1427
- }
1428
- },
1429
- "autoload": {
1430
- "psr-4": {
1431
- "Hoa\\Event\\": "."
1432
- }
1433
- },
1434
- "notification-url": "https://packagist.org/downloads/",
1435
- "license": [
1436
- "BSD-3-Clause"
1437
- ],
1438
- "authors": [
1439
- {
1440
- "name": "Ivan Enderlin",
1441
- "email": "ivan.enderlin@hoa-project.net"
1442
- },
1443
- {
1444
- "name": "Hoa community",
1445
- "homepage": "https://hoa-project.net/"
1446
- }
1447
- ],
1448
- "description": "The Hoa\\Event library.",
1449
- "homepage": "https://hoa-project.net/",
1450
- "keywords": [
1451
- "event",
1452
- "library",
1453
- "listener",
1454
- "observer"
1455
- ],
1456
- "time": "2017-01-13T15:30:50+00:00"
1457
- },
1458
- {
1459
- "name": "hoa/exception",
1460
- "version": "1.17.01.16",
1461
- "source": {
1462
- "type": "git",
1463
- "url": "https://github.com/hoaproject/Exception.git",
1464
- "reference": "091727d46420a3d7468ef0595651488bfc3a458f"
1465
- },
1466
- "dist": {
1467
- "type": "zip",
1468
- "url": "https://api.github.com/repos/hoaproject/Exception/zipball/091727d46420a3d7468ef0595651488bfc3a458f",
1469
- "reference": "091727d46420a3d7468ef0595651488bfc3a458f",
1470
- "shasum": ""
1471
- },
1472
- "require": {
1473
- "hoa/consistency": "~1.0",
1474
- "hoa/event": "~1.0"
1475
- },
1476
- "require-dev": {
1477
- "hoa/test": "~2.0"
1478
- },
1479
- "type": "library",
1480
- "extra": {
1481
- "branch-alias": {
1482
- "dev-master": "1.x-dev"
1483
- }
1484
- },
1485
- "autoload": {
1486
- "psr-4": {
1487
- "Hoa\\Exception\\": "."
1488
- }
1489
- },
1490
- "notification-url": "https://packagist.org/downloads/",
1491
- "license": [
1492
- "BSD-3-Clause"
1493
- ],
1494
- "authors": [
1495
- {
1496
- "name": "Ivan Enderlin",
1497
- "email": "ivan.enderlin@hoa-project.net"
1498
- },
1499
- {
1500
- "name": "Hoa community",
1501
- "homepage": "https://hoa-project.net/"
1502
- }
1503
- ],
1504
- "description": "The Hoa\\Exception library.",
1505
- "homepage": "https://hoa-project.net/",
1506
- "keywords": [
1507
- "exception",
1508
- "library"
1509
- ],
1510
- "time": "2017-01-16T07:53:27+00:00"
1511
- },
1512
- {
1513
- "name": "hoa/file",
1514
- "version": "1.17.07.11",
1515
- "source": {
1516
- "type": "git",
1517
- "url": "https://github.com/hoaproject/File.git",
1518
- "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca"
1519
- },
1520
- "dist": {
1521
- "type": "zip",
1522
- "url": "https://api.github.com/repos/hoaproject/File/zipball/35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
1523
- "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
1524
- "shasum": ""
1525
- },
1526
- "require": {
1527
- "hoa/consistency": "~1.0",
1528
- "hoa/event": "~1.0",
1529
- "hoa/exception": "~1.0",
1530
- "hoa/iterator": "~2.0",
1531
- "hoa/stream": "~1.0"
1532
- },
1533
- "require-dev": {
1534
- "hoa/test": "~2.0"
1535
- },
1536
- "type": "library",
1537
- "extra": {
1538
- "branch-alias": {
1539
- "dev-master": "1.x-dev"
1540
- }
1541
- },
1542
- "autoload": {
1543
- "psr-4": {
1544
- "Hoa\\File\\": "."
1545
- }
1546
- },
1547
- "notification-url": "https://packagist.org/downloads/",
1548
- "license": [
1549
- "BSD-3-Clause"
1550
- ],
1551
- "authors": [
1552
- {
1553
- "name": "Ivan Enderlin",
1554
- "email": "ivan.enderlin@hoa-project.net"
1555
- },
1556
- {
1557
- "name": "Hoa community",
1558
- "homepage": "https://hoa-project.net/"
1559
- }
1560
- ],
1561
- "description": "The Hoa\\File library.",
1562
- "homepage": "https://hoa-project.net/",
1563
- "keywords": [
1564
- "Socket",
1565
- "directory",
1566
- "file",
1567
- "finder",
1568
- "library",
1569
- "link",
1570
- "temporary"
1571
- ],
1572
- "time": "2017-07-11T07:42:15+00:00"
1573
- },
1574
- {
1575
- "name": "hoa/iterator",
1576
- "version": "2.17.01.10",
1577
- "source": {
1578
- "type": "git",
1579
- "url": "https://github.com/hoaproject/Iterator.git",
1580
- "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc"
1581
- },
1582
- "dist": {
1583
- "type": "zip",
1584
- "url": "https://api.github.com/repos/hoaproject/Iterator/zipball/d1120ba09cb4ccd049c86d10058ab94af245f0cc",
1585
- "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc",
1586
- "shasum": ""
1587
- },
1588
- "require": {
1589
- "hoa/consistency": "~1.0",
1590
- "hoa/exception": "~1.0"
1591
- },
1592
- "require-dev": {
1593
- "hoa/test": "~2.0"
1594
- },
1595
- "type": "library",
1596
- "extra": {
1597
- "branch-alias": {
1598
- "dev-master": "2.x-dev"
1599
- }
1600
- },
1601
- "autoload": {
1602
- "psr-4": {
1603
- "Hoa\\Iterator\\": "."
1604
- }
1605
- },
1606
- "notification-url": "https://packagist.org/downloads/",
1607
- "license": [
1608
- "BSD-3-Clause"
1609
- ],
1610
- "authors": [
1611
- {
1612
- "name": "Ivan Enderlin",
1613
- "email": "ivan.enderlin@hoa-project.net"
1614
- },
1615
- {
1616
- "name": "Hoa community",
1617
- "homepage": "https://hoa-project.net/"
1618
- }
1619
- ],
1620
- "description": "The Hoa\\Iterator library.",
1621
- "homepage": "https://hoa-project.net/",
1622
- "keywords": [
1623
- "iterator",
1624
- "library"
1625
- ],
1626
- "time": "2017-01-10T10:34:47+00:00"
1627
- },
1628
- {
1629
- "name": "hoa/protocol",
1630
- "version": "1.17.01.14",
1631
- "source": {
1632
- "type": "git",
1633
- "url": "https://github.com/hoaproject/Protocol.git",
1634
- "reference": "5c2cf972151c45f373230da170ea015deecf19e2"
1635
- },
1636
- "dist": {
1637
- "type": "zip",
1638
- "url": "https://api.github.com/repos/hoaproject/Protocol/zipball/5c2cf972151c45f373230da170ea015deecf19e2",
1639
- "reference": "5c2cf972151c45f373230da170ea015deecf19e2",
1640
- "shasum": ""
1641
- },
1642
- "require": {
1643
-