Facebook for WooCommerce - Version 2.5.0

Version Description

  • 2021-05-19 =
    • New - Option to allow larger sites to opt-out of feed generation (product sync) job
    • New - Log connection errors to allow easier troubleshooting
    • Fix - Reduce default feed generation (product sync) interval to once per day to reduce overhead
    • Fix - Trigger feed (product sync) job from to admin_init to reduce impact on front-end requests
    • Fix - Ensure variable product attribute values containing comma (,) sync correctly
    • Fix - Use existing / current tab for connection Get Started button
    • Dev - Require PHP version 7.0 or newer
    • Dev - Adopt Composer autoloader to avoid manually requireing PHP class files
    • Dev - Adopt WooRelease release tool for deploying releases
    • Dev - Use wp-scripts to build assets
    • Dev - Add phpcs tooling to help standardise PHP code style
    • Dev - Add JobRegistry engine for managing periodic background batch jobs
Download this release

Release Info

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

Code changes from version 2.4.1 to 2.5.0

Files changed (255) hide show
  1. README.md +33 -0
  2. assets/build/admin/google-product-category-fields.asset.php +1 -0
  3. assets/build/admin/google-product-category-fields.js +1 -0
  4. assets/build/admin/index.asset.php +1 -0
  5. assets/build/admin/index.js +1 -0
  6. assets/build/admin/infobanner.asset.php +1 -0
  7. assets/build/admin/infobanner.js +1 -0
  8. assets/build/admin/metabox.asset.php +1 -0
  9. assets/build/admin/metabox.js +1 -0
  10. assets/build/admin/modal.asset.php +1 -0
  11. assets/build/admin/modal.js +1 -0
  12. assets/build/admin/orders.asset.php +1 -0
  13. assets/build/admin/orders.js +1 -0
  14. assets/build/admin/product-categories.asset.php +1 -0
  15. assets/build/admin/product-categories.js +1 -0
  16. assets/build/admin/product-sets-admin.asset.php +1 -0
  17. assets/build/admin/product-sets-admin.js +1 -0
  18. assets/build/admin/products-admin.asset.php +1 -0
  19. assets/build/admin/products-admin.js +1 -0
  20. assets/build/admin/settings-commerce.asset.php +1 -0
  21. assets/build/admin/settings-commerce.js +1 -0
  22. assets/build/admin/settings-sync.asset.php +1 -0
  23. assets/build/admin/settings-sync.js +1 -0
  24. assets/js/admin/facebook-for-woocommerce-product-sets-admin.min.js +0 -1
  25. assets/js/admin/facebook-for-woocommerce-products-admin.min.js +0 -1
  26. assets/js/admin/facebook-for-woocommerce-products-admin.min.old.js +0 -2
  27. assets/js/admin/facebook-for-woocommerce-products-admin.min.old.min.js +0 -1
  28. assets/js/admin/facebook-for-woocommerce-settings-messenger.js +0 -96
  29. assets/js/admin/facebook-for-woocommerce-settings-messenger.min.js +0 -1
  30. assets/js/admin/facebook-for-woocommerce-settings-sync.min.js +0 -1
  31. assets/js/admin/google-product-category-fields.min.js +0 -1
  32. assets/js/admin/index.js +1 -0
  33. assets/js/{facebook-infobanner.js → admin/infobanner.js} +0 -0
  34. assets/js/{facebook-metabox.js → admin/metabox.js} +0 -0
  35. assets/js/{facebook-for-woocommerce-modal.js → admin/modal.js} +0 -0
  36. assets/js/admin/orders.min.js +0 -1
  37. assets/js/admin/product-categories.min.js +0 -1
  38. assets/js/admin/{facebook-for-woocommerce-product-sets-admin.js → product-sets-admin.js} +0 -0
  39. assets/js/admin/{facebook-for-woocommerce-products-admin.js → products-admin.js} +0 -0
  40. assets/js/admin/settings-commerce.min.js +0 -1
  41. assets/js/admin/{facebook-for-woocommerce-settings-sync.js → settings-sync.js} +0 -0
  42. assets/js/facebook-for-woocommerce-modal.min.js +0 -1
  43. assets/js/facebook-infobanner.min.js +0 -1
  44. assets/js/facebook-metabox.min.js +0 -1
  45. assets/js/facebook-settings.min.js +0 -1
  46. changelog.txt +14 -0
  47. class-wc-facebookcommerce.php +101 -73
  48. facebook-commerce-events-tracker.php +113 -110
  49. facebook-commerce-messenger-chat.php +10 -2
  50. facebook-commerce-pixel-event.php +155 -82
  51. facebook-commerce.php +177 -163
  52. facebook-config-warmer.php +2 -2
  53. facebook-for-woocommerce.php +54 -30
  54. i18n/languages/facebook-for-woocommerce.pot +181 -181
  55. includes/AJAX.php +82 -71
  56. includes/API.php +74 -60
  57. includes/API/Catalog/Product_Group/Products/Read/Request.php +7 -5
  58. includes/API/Catalog/Product_Group/Products/Read/Response.php +1 -1
  59. includes/API/Catalog/Product_Item/Find/Request.php +1 -1
  60. includes/API/Catalog/Request.php +2 -2
  61. includes/API/Catalog/Send_Item_Updates/Request.php +5 -5
  62. includes/API/FBE/Configuration/Messenger.php +12 -9
  63. includes/API/FBE/Configuration/Read/Response.php +1 -1
  64. includes/API/FBE/Configuration/Request.php +3 -3
  65. includes/API/FBE/Configuration/Update/Request.php +3 -3
  66. includes/API/FBE/Installation/Read/Request.php +3 -3
  67. includes/API/FBE/Installation/Read/Response.php +3 -3
  68. includes/API/FBE/Installation/Request.php +1 -1
  69. includes/API/Orders/Acknowledge/Request.php +7 -5
  70. includes/API/Orders/Cancel/Request.php +11 -9
  71. includes/API/Orders/Fulfillment/Request.php +2 -2
  72. includes/API/Orders/Order.php +5 -5
  73. includes/API/Orders/Read/Request.php +21 -18
  74. includes/API/Orders/Refund/Request.php +2 -2
  75. includes/API/Orders/Request.php +21 -18
  76. includes/API/Orders/Response.php +1 -1
  77. includes/API/Pages/Read/Request.php +2 -2
  78. includes/API/Pages/Read/Response.php +1 -1
  79. includes/API/Pixel/Events/Request.php +5 -5
  80. includes/API/Request.php +1 -1
  81. includes/API/Traits/Paginated_Response.php +1 -1
  82. includes/API/Traits/Rate_Limited_API.php +2 -2
  83. includes/API/Traits/Rate_Limited_Response.php +1 -1
  84. includes/API/User/Permissions/Delete/Request.php +1 -1
  85. includes/API/User/Request.php +1 -1
  86. includes/Admin.php +98 -46
  87. includes/Admin/Abstract_Settings_Screen.php +1 -0
  88. includes/Admin/Enhanced_Catalog_Attribute_Fields.php +1 -1
  89. includes/Admin/Product_Categories.php +2 -2
  90. includes/Admin/Settings.php +25 -22
  91. includes/Admin/Settings_Screens/Advertise.php +14 -14
  92. includes/Admin/Settings_Screens/Connection.php +57 -46
  93. includes/Admin/Settings_Screens/Messenger.php +28 -21
  94. includes/Admin/Settings_Screens/Product_Sync.php +98 -74
  95. includes/Commerce/Orders.php +103 -84
  96. includes/Events/AAMSettings.php +20 -22
  97. includes/Events/Event.php +41 -36
  98. includes/Events/Normalizer.php +53 -56
  99. includes/Exceptions/ConnectWCAPIException.php +14 -0
  100. includes/Handlers/Connection.php +88 -61
  101. includes/Integrations/Bookings.php +7 -7
  102. includes/Integrations/Integrations.php +2 -2
  103. includes/Jobs/AbstractChainedJob.php +39 -0
  104. includes/Jobs/GenerateProductFeed.php +145 -0
  105. includes/Jobs/JobRegistry.php +31 -0
  106. includes/Jobs/LoggingTrait.php +38 -0
  107. includes/Lifecycle.php +29 -14
  108. includes/Locale.php +4 -5
  109. includes/ProductSets/Sync.php +2 -2
  110. includes/Product_Categories.php +1 -1
  111. includes/Products.php +8 -8
  112. includes/Products/FBCategories.php +2 -2
  113. includes/Products/Feed.php +19 -16
  114. includes/Products/Stock.php +11 -8
  115. includes/Products/Sync.php +11 -9
  116. includes/Products/Sync/Background.php +44 -25
  117. includes/Utilities/Background_Handle_Virtual_Products_Variations.php +3 -3
  118. includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php +8 -6
  119. includes/Utilities/Shipment.php +512 -512
  120. includes/Utilities/Tracker.php +1 -1
  121. includes/fbgraph.php +38 -36
  122. includes/fbproduct.php +6 -5
  123. includes/fbproductfeed.php +21 -24
  124. includes/fbutils.php +21 -21
  125. includes/fbwpml.php +2 -2
  126. includes/test/facebook-integration-test.php +1 -1
  127. readme.txt +16 -2
  128. vendor/autoload.php +7 -0
  129. vendor/composer/ClassLoader.php +445 -0
  130. vendor/composer/InstalledVersions.php +250 -0
  131. vendor/composer/LICENSE +21 -0
  132. vendor/composer/autoload_classmap.php +10 -0
  133. vendor/composer/autoload_namespaces.php +9 -0
  134. vendor/composer/autoload_psr4.php +12 -0
  135. vendor/composer/autoload_real.php +57 -0
  136. vendor/composer/autoload_static.php +52 -0
  137. vendor/composer/installed.json +231 -0
  138. vendor/composer/installed.php +65 -0
  139. vendor/composer/installers/LICENSE +19 -0
  140. vendor/composer/installers/phpstan.neon.dist +10 -0
  141. vendor/composer/installers/src/Composer/Installers/AglInstaller.php +21 -0
  142. vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php +9 -0
  143. vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php +11 -0
  144. vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php +49 -0
  145. vendor/composer/installers/src/Composer/Installers/AttogramInstaller.php +9 -0
  146. vendor/composer/installers/src/Composer/Installers/BaseInstaller.php +137 -0
  147. vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php +126 -0
  148. vendor/composer/installers/src/Composer/Installers/BonefishInstaller.php +9 -0
  149. vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php +65 -0
  150. vendor/composer/installers/src/Composer/Installers/ChefInstaller.php +11 -0
  151. vendor/composer/installers/src/Composer/Installers/CiviCrmInstaller.php +9 -0
  152. vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php +10 -0
  153. vendor/composer/installers/src/Composer/Installers/CockpitInstaller.php +32 -0
  154. vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php +11 -0
  155. vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php +13 -0
  156. vendor/composer/installers/src/Composer/Installers/CraftInstaller.php +35 -0
  157. vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php +21 -0
  158. vendor/composer/installers/src/Composer/Installers/DecibelInstaller.php +10 -0
  159. vendor/composer/installers/src/Composer/Installers/DframeInstaller.php +10 -0
  160. vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php +50 -0
  161. vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php +16 -0
  162. vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php +22 -0
  163. vendor/composer/installers/src/Composer/Installers/ElggInstaller.php +9 -0
  164. vendor/composer/installers/src/Composer/Installers/EliasisInstaller.php +12 -0
  165. vendor/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php +29 -0
  166. vendor/composer/installers/src/Composer/Installers/EzPlatformInstaller.php +10 -0
  167. vendor/composer/installers/src/Composer/Installers/FuelInstaller.php +11 -0
  168. vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php +9 -0
  169. vendor/composer/installers/src/Composer/Installers/GravInstaller.php +30 -0
  170. vendor/composer/installers/src/Composer/Installers/HuradInstaller.php +25 -0
  171. vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php +11 -0
  172. vendor/composer/installers/src/Composer/Installers/Installer.php +297 -0
  173. vendor/composer/installers/src/Composer/Installers/ItopInstaller.php +9 -0
  174. vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php +15 -0
  175. vendor/composer/installers/src/Composer/Installers/KanboardInstaller.php +18 -0
  176. vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php +11 -0
  177. vendor/composer/installers/src/Composer/Installers/KnownInstaller.php +11 -0
  178. vendor/composer/installers/src/Composer/Installers/KodiCMSInstaller.php +10 -0
  179. vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php +9 -0
  180. vendor/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php +27 -0
  181. vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php +9 -0
  182. vendor/composer/installers/src/Composer/Installers/LavaLiteInstaller.php +10 -0
  183. vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php +10 -0
  184. vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php +9 -0
  185. vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php +16 -0
  186. vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php +11 -0
  187. vendor/composer/installers/src/Composer/Installers/MajimaInstaller.php +37 -0
  188. vendor/composer/installers/src/Composer/Installers/MakoInstaller.php +9 -0
  189. vendor/composer/installers/src/Composer/Installers/MantisBTInstaller.php +23 -0
  190. vendor/composer/installers/src/Composer/Installers/MauticInstaller.php +48 -0
  191. vendor/composer/installers/src/Composer/Installers/MayaInstaller.php +33 -0
  192. vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php +51 -0
  193. vendor/composer/installers/src/Composer/Installers/MiaoxingInstaller.php +10 -0
  194. vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php +119 -0
  195. vendor/composer/installers/src/Composer/Installers/ModxInstaller.php +12 -0
  196. vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php +59 -0
  197. vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php +48 -0
  198. vendor/composer/installers/src/Composer/Installers/OntoWikiInstaller.php +24 -0
  199. vendor/composer/installers/src/Composer/Installers/OsclassInstaller.php +14 -0
  200. vendor/composer/installers/src/Composer/Installers/OxidInstaller.php +59 -0
  201. vendor/composer/installers/src/Composer/Installers/PPIInstaller.php +9 -0
  202. vendor/composer/installers/src/Composer/Installers/PhiftyInstaller.php +11 -0
  203. vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php +11 -0
  204. vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php +21 -0
  205. vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php +32 -0
  206. vendor/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php +29 -0
  207. vendor/composer/installers/src/Composer/Installers/Plugin.php +27 -0
  208. vendor/composer/installers/src/Composer/Installers/PortoInstaller.php +9 -0
  209. vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php +10 -0
  210. vendor/composer/installers/src/Composer/Installers/ProcessWireInstaller.php +22 -0
  211. vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php +11 -0
  212. vendor/composer/installers/src/Composer/Installers/PxcmsInstaller.php +63 -0
  213. vendor/composer/installers/src/Composer/Installers/RadPHPInstaller.php +24 -0
  214. vendor/composer/installers/src/Composer/Installers/ReIndexInstaller.php +10 -0
  215. vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php +10 -0
  216. vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php +10 -0
  217. vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php +22 -0
  218. vendor/composer/installers/src/Composer/Installers/SMFInstaller.php +10 -0
  219. vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php +60 -0
  220. vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php +35 -0
  221. vendor/composer/installers/src/Composer/Installers/SiteDirectInstaller.php +25 -0
  222. vendor/composer/installers/src/Composer/Installers/StarbugInstaller.php +12 -0
  223. vendor/composer/installers/src/Composer/Installers/SyDESInstaller.php +47 -0
  224. vendor/composer/installers/src/Composer/Installers/SyliusInstaller.php +9 -0
  225. vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php +26 -0
  226. vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php +16 -0
  227. vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php +38 -0
  228. vendor/composer/installers/src/Composer/Installers/TaoInstaller.php +30 -0
  229. vendor/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php +32 -0
  230. vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php +12 -0
  231. vendor/composer/installers/src/Composer/Installers/TuskInstaller.php +14 -0
  232. vendor/composer/installers/src/Composer/Installers/UserFrostingInstaller.php +9 -0
  233. vendor/composer/installers/src/Composer/Installers/VanillaInstaller.php +10 -0
  234. vendor/composer/installers/src/Composer/Installers/VgmcpInstaller.php +49 -0
  235. vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php +21 -0
  236. vendor/composer/installers/src/Composer/Installers/WinterInstaller.php +58 -0
  237. vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php +9 -0
  238. vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php +12 -0
  239. vendor/composer/installers/src/Composer/Installers/YawikInstaller.php +32 -0
  240. vendor/composer/installers/src/Composer/Installers/ZendInstaller.php +11 -0
  241. vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php +10 -0
  242. vendor/composer/installers/src/bootstrap.php +13 -0
  243. vendor/composer/platform_check.php +26 -0
  244. vendor/woocommerce/action-scheduler-job-framework/LICENSE +674 -0
  245. vendor/woocommerce/action-scheduler-job-framework/README.md +116 -0
  246. vendor/woocommerce/action-scheduler-job-framework/phpcs.xml.dist +57 -0
  247. vendor/woocommerce/action-scheduler-job-framework/src/AbstractChainedJob.php +251 -0
  248. vendor/woocommerce/action-scheduler-job-framework/src/AbstractJob.php +76 -0
  249. vendor/woocommerce/action-scheduler-job-framework/src/ChainedJobInterface.php +72 -0
  250. vendor/woocommerce/action-scheduler-job-framework/src/JobInterface.php +48 -0
  251. vendor/woocommerce/action-scheduler-job-framework/src/Proxies/ActionScheduler.php +97 -0
  252. vendor/woocommerce/action-scheduler-job-framework/src/Proxies/ActionSchedulerInterface.php +89 -0
  253. vendor/woocommerce/action-scheduler-job-framework/src/Utilities/BatchQueryOffset.php +26 -0
  254. vendor/woocommerce/action-scheduler-job-framework/src/Utilities/BatchSize.php +22 -0
  255. webpack.config.js +35 -0
README.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Facebook for WooCommerce
2
+
3
+ This is the development repository for the Facebook for WooCommerce plugin.
4
+
5
+ - [WooCommerce.com product page](https://woocommerce.com/products/facebook)
6
+ - [WordPress.org plugin page](https://wordpress.org/plugins/facebook-for-woocommerce/)
7
+ - [User documentation](https://docs.woocommerce.com/document/facebook-for-woocommerce)
8
+
9
+ ## Support
10
+ The best place to get support is the [WordPress.org Facebook for WooCommerce forum](https://wordpress.org/support/plugin/facebook-for-woocommerce/).
11
+
12
+ If you have a WooCommerce.com account, you can [start a chat or open a ticket on WooCommerce.com](https://woocommerce.com/my-account/create-a-ticket/).
13
+
14
+ ## Development
15
+ ### Developing
16
+ - Clone this repository into the `wp-content/plugins/` folder your WooCommerce development environment.
17
+ - Install dependencies:
18
+ - `npm install`
19
+ - `composer install`
20
+ - Build assets:
21
+ - `npm start` to build a development version
22
+ - Linting:
23
+ - `npm run lint:php` to run PHPCS linter on all PHP files
24
+
25
+ #### Production build
26
+ This plugin uses a custom build tool called [`sake`](https://github.com/skyverge/sake).
27
+
28
+ If you have `sake` set up on your system, these commands can be used to generate a production build.
29
+
30
+ - `npm run build` builds and zips to `/build/facebook-for-woocommerce.{version}.zip`.
31
+
32
+ ### Releasing
33
+ Refer to the [wiki for details of how to build and release the plugin](https://github.com/woocommerce/facebook-for-woocommerce/wiki/Build-&-Release).
assets/build/admin/google-product-category-fields.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '00c66c0ea3ceb2d3751483c954e5a649');
assets/build/admin/google-product-category-fields.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function o(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,o),n.l=!0,n.exports}o.m=e,o.c=t,o.d=function(e,t,a){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(o.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)o.d(a,n,function(t){return e[t]}.bind(null,n));return a},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=3)}([function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){function o(e,t){for(var o=0;o<t.length;o++){var a=t[o];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}e.exports=function(e,t,a){return t&&o(e.prototype,t),a&&o(e,a),e}},,function(e,t,o){"use strict";o.r(t);var a=o(0),n=o.n(a),r=o(1),c=o.n(r);jQuery(document).ready((function(e){window.WC_Facebook_Google_Product_Category_Fields=function(){function t(o,a){var r=this;n()(this,t),this.categories=o,this.input_id=a;var c=e("#"+this.input_id);e('<div id="wc-facebook-google-product-category-fields"></div>').insertBefore(c).on("change","select.wc-facebook-google-product-category-select",(function(t){r.onChange(e(t.target))})),this.addInitialSelects(c.val());var i=this.globalsHolder().enhanced_attribute_optional_selector;void 0!==i&&e("#"+i).on("change",(function(){e(".wc-facebook-enhanced-catalog-attribute-optional-row").toggleClass("hidden",!e(this).prop("checked"))}))}return c()(t,[{key:"globalsHolder",value:function(){return"undefined"!=typeof facebook_for_woocommerce_product_categories?facebook_for_woocommerce_product_categories:"undefined"!=typeof facebook_for_woocommerce_settings_sync?facebook_for_woocommerce_settings_sync:facebook_for_woocommerce_products_admin}},{key:"getPageType",value:function(){return"undefined"!=typeof facebook_for_woocommerce_product_categories?0===e("input[name=tag_ID]").length?this.globalsHolder().enhanced_attribute_page_type_add_category:this.globalsHolder().enhanced_attribute_page_type_edit_category:this.globalsHolder().enhanced_attribute_page_type_edit_product}},{key:"addInitialSelects",value:function(e){var t=this;if(e){this.getSelectedCategoryIds(e).forEach((function(e){t.addSelect(t.getOptions(e[1]),e[0])}));var o=this.getOptions(e);Object.keys(o).length&&this.addSelect(o)}else this.addSelect(this.getOptions()),this.addSelect({})}},{key:"requestAttributesIfValid",value:function(){if("true"===e("#wc_facebook_can_show_enhanced_catalog_attributes_id").val()&&(e(".wc-facebook-enhanced-catalog-attribute-row").remove(),this.isValid())){var t="#"+this.input_id,o=e(t).parents("div.form-field"),a=this.globalsHolder().enhanced_attribute_optional_selector;this.getPageType()===this.globalsHolder().enhanced_attribute_page_type_edit_category?o=e(t).parents("tr.form-field"):this.getPageType()===this.globalsHolder().enhanced_attribute_page_type_edit_product&&(o=e(t).parents("p.form-field")),e.get(this.globalsHolder().ajax_url,{action:"wc_facebook_enhanced_catalog_attributes",security:"",selected_category:e(t).val(),tag_id:parseInt(e("input[name=tag_ID]").val(),10),taxonomy:e("input[name=taxonomy]").val(),item_id:parseInt(e("input[name=post_ID]").val(),10),page_type:this.getPageType()},(function(t){var n=e(t);e("#"+a,n).on("change",(function(){e(".wc-facebook-enhanced-catalog-attribute-optional-row").toggleClass("hidden",!e(this).prop("checked"))})),n.insertAfter(o),e(document.body).trigger("init_tooltips")}))}}},{key:"onChange",value:function(t){t.hasClass("locked")&&t.closest(".wc-facebook-google-product-category-field").nextAll().remove();var o=t.val();if(o){var a=this.getOptions(o);Object.keys(a).length&&this.addSelect(a)}else(o=t.closest("#wc-facebook-google-product-category-fields").find(".wc-facebook-google-product-category-select").not(t).last().val())||this.addSelect({});e("#"+this.input_id).val(o),this.requestAttributesIfValid()}},{key:"isValid",value:function(){return e(".wc-facebook-google-product-category-select").filter((function(t,o){return""!==e(o).val()})).length>=2}},{key:"addSelect",value:function(t,o){var a=e("#wc-facebook-google-product-category-fields"),n=a.find(".wc-facebook-google-product-category-select"),r=e('<select class="wc-enhanced-select wc-facebook-google-product-category-select"></select>');n.addClass("locked"),a.append(e('<div class="wc-facebook-google-product-category-field" style="margin-bottom: 16px">').append(r)),r.attr("data-placeholder",this.getSelectPlaceholder(n,t)).append(e('<option value=""></option>')),Object.keys(t).forEach((function(o){r.append(e('<option value="'+o+'">'+t[o]+"</option>"))})),r.val(o).select2({allowClear:!0})}},{key:"getSelectPlaceholder",value:function(e,t){return 0===e.length?facebook_for_woocommerce_google_product_category.i18n.top_level_dropdown_placeholder:1===e.length&&0===Object.keys(t).length?facebook_for_woocommerce_google_product_category.i18n.second_level_empty_dropdown_placeholder:facebook_for_woocommerce_google_product_category.i18n.general_dropdown_placeholder}},{key:"getOptions",value:function(e){return void 0===e||""===e?this.getTopLevelOptions():void 0===this.categories[e]||void 0===this.categories[e].options?[]:this.categories[e].options}},{key:"getTopLevelOptions",value:function(){var e=this,t={};return Object.keys(this.categories).forEach((function(o){e.categories[o].parent||(t[o]=e.categories[o].label)})),t}},{key:"getSelectedCategoryIds",value:function(e){var t=[];do{void 0!==this.categories[e]&&(t.push([e,this.categories[e].parent]),e=this.categories[e].parent)}while(""!==e);return t.reverse()}}]),t}()}))}]);
assets/build/admin/index.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '92d945b180080836a3157ac9b263c06c');
assets/build/admin/index.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=2)}({2:function(e,t){}});
assets/build/admin/infobanner.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '06a723733c2f5d1b1540bfd66802907d');
assets/build/admin/infobanner.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t){}});
assets/build/admin/metabox.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '11491c9b1bc47798db6b39ee4dcc4f56');
assets/build/admin/metabox.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=5)}({5:function(e,t){}});
assets/build/admin/modal.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '5893f2331640da5de5c8690f39ba5f3e');
assets/build/admin/modal.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var n={};function o(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=e,o.c=n,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="",o(o.s=6)}({6:function(e,n){var o;o=jQuery,window.isModalBlocked=function(){var e=o(".wc-backbone-modal-content");return e.is(".processing")||e.parents(".processing").length},window.blockModal=function(){if(!isModalBlocked())return o(".wc-backbone-modal-content").addClass("processing").block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},window.unBlockModal=function(){o(".wc-backbone-modal-content").removeClass("processing").unblock()},window.closeExistingModal=function(){o("#wc-backbone-modal-dialog .modal-close").trigger("click")}}});
assets/build/admin/orders.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '869fc31f506af286cd2b4e213105990a');
assets/build/admin/orders.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var o={};function r(c){if(o[c])return o[c].exports;var n=o[c]={i:c,l:!1,exports:{}};return e[c].call(n.exports,n,n.exports,r),n.l=!0,n.exports}r.m=e,r.c=o,r.d=function(e,o,c){r.o(e,o)||Object.defineProperty(e,o,{enumerable:!0,get:c})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,o){if(1&o&&(e=r(e)),8&o)return e;if(4&o&&"object"==typeof e&&e&&e.__esModule)return e;var c=Object.create(null);if(r.r(c),Object.defineProperty(c,"default",{enumerable:!0,value:e}),2&o&&"string"!=typeof e)for(var n in e)r.d(c,n,function(o){return e[o]}.bind(null,n));return c},r.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(o,"a",o),o},r.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},r.p="",r(r.s=7)}({7:function(e,o){jQuery(document).ready((function(e){"use strict";var o=Boolean(wc_facebook_commerce_orders.is_commerce_order),r={restrict_order_statuses:function(e){e.find("option").each((function(e,o){-1===wc_facebook_commerce_orders.allowed_commerce_statuses.indexOf(o.value)&&o.remove()}))},toggle_created_date_fields_status:function(o){r.toggle_field(e("#order_data").find("input[name*=order_date]"),o)},disable_order_status_field:function(e){r.toggle_field(e,!1)},toggle_order_customer_field:function(o){e("#order_data").find(".form-field.wc-customer-user").toggleClass("hidden",o)},toggle_billing_and_shipping_fields:function(o){e("#order_data").find("a.edit_address").toggleClass("hidden",o)},disable_pending_order_related_fields:function(e){r.toggle_created_date_fields_status(!1),r.disable_order_status_field(e),r.toggle_order_customer_field(!0),r.toggle_billing_and_shipping_fields(!0)},maybe_disable_refunds:function(){"completed"!==wc_facebook_commerce_orders.order_status&&e(".wc-order-bulk-actions .refund-items").hide()},toggle_field:function(e,o){e.hasClass("wc-enhanced-select")&&(e=e.next("span.select2-container")),o?e.css("pointer-events","all").css("opacity","1.0"):e.css("pointer-events","none").css("opacity","0.4")}},c=e('form[id="post"]'),n=e("#order_status"),a=n.val(),t=wc_facebook_commerce_orders.shipment_tracking,d="",_="",i="",s="";function l(o){unBlockModal(),e(".facebook-for-woocommerce-modal .wc-backbone-modal-content article").html("<p>"+o+"</p>"),e(".facebook-for-woocommerce-modal .wc-backbone-modal-content footer").remove()}function f(){var o=arguments.length>0&&void 0!==arguments[0]&&arguments[0],r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(!r.length)return alert(wc_facebook_commerce_orders.i18n.missing_tracking_number_error),!1;o&&blockModal(),c.find("button[type=submit].save_order").prop("disabled",!0).append('<span class="spinner is-active"></span>'),e.post(ajaxurl,{action:wc_facebook_commerce_orders.complete_order_action,order_id:e("#post_ID").val(),tracking_number:r,carrier_code:n,nonce:wc_facebook_commerce_orders.complete_order_nonce},(function(e){if(o&&unBlockModal(),e&&e.success)c.data("allow-submit",!0).trigger("submit");else{var r=e&&e.data?e.data:wc_facebook_commerce_orders.i18n.unknown_error;alert(r)}})).fail((function(){l(wc_facebook_commerce_orders.i18n.unknown_error)})).always((function(){c.find("button[type=submit].save_order").prop("disabled",!1).find("span.spinner").remove()}))}function m(){var o=e("#refund_reason"),r=e("#wc_facebook_refund_reason").clone().css("width",o.css("width")),c=o.closest("tr"),n=c.clone();c.find("td.total").css("width","16em").end().find("#refund_reason").replaceWith(r.show()).end().find('label[for="refund_reason"]').attr("for","wc_facebook_refund_reason"),c.after(n),u(c,"wc_facebook_refund_reason",wc_facebook_commerce_orders.i18n.refund_reason_label,wc_facebook_commerce_orders.i18n.refund_reason_tooltip),u(n,"refund_reason",wc_facebook_commerce_orders.i18n.refund_description_label,wc_facebook_commerce_orders.i18n.refund_description_tooltip)}function u(e,o,r,c){var n=e.find('label[for="'+o+'"]'),a=n.find(".woocommerce-help-tip").clone();n.text(r),c&&a.length&&(n.prepend(a),a.attr("data-tip",c).tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}))}Array.isArray(t)&&t[0]&&(d=t[0].tracking_number,_=t[0].carrier_code),o&&(r.restrict_order_statuses(n),"pending"===wc_facebook_commerce_orders.order_status&&r.disable_pending_order_related_fields(n),"cancelled"===wc_facebook_commerce_orders.order_status&&r.disable_order_status_field(n),r.maybe_disable_refunds()),o&&(m(),function(){if(void 0!==window.MutationObserver){var o=document.querySelector("#woocommerce-order-items .inside");o&&new MutationObserver((function(o){o.forEach((function(o){Array.prototype.forEach.call(o.addedNodes,(function(o){e(o).is(".wc-order-refund-items")&&m()}))}))})).observe(o,{childList:!0})}}()),c.on("submit",(function(r){if(!e("#post").data("skip-cancel-modal")&&"wc-cancelled"!==a&&o&&"wc-cancelled"===n.val())return function(o){return o.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.cancel_modal_message,buttons:wc_facebook_commerce_orders.cancel_modal_buttons}}),e(".facebook-for-woocommerce-modal #btn-ok").off("click.facebook_for_commerce").on("click.facebook_for_commerce",(function(o){o.preventDefault(),o.stopPropagation(),blockModal(),e.post(ajaxurl,{action:wc_facebook_commerce_orders.cancel_order_action,order_id:e("#post_ID").val(),reason_code:e('.facebook-for-woocommerce-modal [name="wc_facebook_cancel_reason"]').val(),security:wc_facebook_commerce_orders.cancel_order_nonce},(function(o){o&&o.success?e("#post").data("skip-cancel-modal",!0).trigger("submit"):l(o&&o.data?o.data:wc_facebook_commerce_orders.i18n.unknown_error)})).fail((function(){l(wc_facebook_commerce_orders.i18n.unknown_error)}))})),!1}(r);if(o&&!c.data("allow-submit")){var t=n.val();"wc-refunded"===t&&a!==t&&function(o){o.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.refund_modal_message,buttons:wc_facebook_commerce_orders.refund_modal_buttons}}),e(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",(function(){e("#refund_reason").val(e("#wc_facebook_refund_reason_modal").val()),c.data("allow-submit",!0).submit()}))}(r),"wc-completed"===t&&(r.preventDefault(),d||_?f(!1,d,_):(e("#wc-backbone-modal-dialog .modal-close").trigger("click"),(s||i)&&e(document.body).off("wc_backbone_modal_loaded").on("wc_backbone_modal_loaded",(function(){s&&e("#wc_facebook_carrier").val(s),i&&e("#wc_facebook_tracking_number").val(i)})),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.complete_modal_message,buttons:wc_facebook_commerce_orders.complete_modal_buttons}}),e(".facebook-for-woocommerce-modal #btn-ok").off("click.facebook_for_commerce").on("click.facebook_for_commerce",(function(o){o.preventDefault(),o.stopPropagation(),s=e("#wc_facebook_carrier").val(),f(!0,i=e("#wc_facebook_tracking_number").val(),s)}))))}}))}))}});
assets/build/admin/product-categories.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '4bedc509a19a74c8c274f145cdaff75a');
assets/build/admin/product-categories.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var o={};function t(r){if(o[r])return o[r].exports;var n=o[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,t),n.l=!0,n.exports}t.m=e,t.c=o,t.d=function(e,o,r){t.o(e,o)||Object.defineProperty(e,o,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,o){if(1&o&&(e=t(e)),8&o)return e;if(4&o&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&o&&"string"!=typeof e)for(var n in e)t.d(r,n,function(o){return e[o]}.bind(null,n));return r},t.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(o,"a",o),o},t.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},t.p="",t(t.s=8)}({8:function(e,o){jQuery(document).ready((function(e){var o=e('form[id="edittag"]'),t=e("#wc_facebook_google_product_category_id"),r=t.val();o.on("submit",(function(n){o.data("allow-submit")||t.val()===r||(n.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_product_categories.default_google_product_category_modal_message,buttons:facebook_for_woocommerce_product_categories.default_google_product_category_modal_buttons}}),e(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",(function(){o.data("allow-submit",!0).find(":submit").trigger("click")})))}))}))}});
assets/build/admin/product-sets-admin.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '98d555c2e6a0ef6072bdf3f337f9302c');
assets/build/admin/product-sets-admin.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=9)}({9:function(e,t){jQuery(document).ready((function(e){jQuery(".select2.wc-facebook").length&&(jQuery(".select2.wc-facebook").select2().addClass("visible").attr("disabled",!1),jQuery(".select2.updating-message").addClass("hidden"),jQuery(document).ajaxSuccess((function(e,t,n){var r=new URLSearchParams(n.data);r.has("action")&&"add-tag"===r.get("action")&&r.has("taxonomy")&&"fb_product_set"===r.get("taxonomy")&&jQuery(".select2.wc-facebook").select2().val(null).trigger("change")})))}))}});
assets/build/admin/products-admin.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => 'c40e4d58318863c75d4136c0885d16ea');
assets/build/admin/products-admin.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(o){var e={};function t(c){if(e[c])return e[c].exports;var n=e[c]={i:c,l:!1,exports:{}};return o[c].call(n.exports,n,n.exports,t),n.l=!0,n.exports}t.m=o,t.c=e,t.d=function(o,e,c){t.o(o,e)||Object.defineProperty(o,e,{enumerable:!0,get:c})},t.r=function(o){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(o,"__esModule",{value:!0})},t.t=function(o,e){if(1&e&&(o=t(o)),8&e)return o;if(4&e&&"object"==typeof o&&o&&o.__esModule)return o;var c=Object.create(null);if(t.r(c),Object.defineProperty(c,"default",{enumerable:!0,value:o}),2&e&&"string"!=typeof o)for(var n in o)t.d(c,n,function(e){return o[e]}.bind(null,n));return c},t.n=function(o){var e=o&&o.__esModule?function(){return o.default}:function(){return o};return t.d(e,"a",e),e},t.o=function(o,e){return Object.prototype.hasOwnProperty.call(o,e)},t.p="",t(t.s=10)}({10:function(o,e){jQuery(document).ready((function(o){var e=window.pagenow.length?window.pagenow:"";if(window.typenow.length&&window.typenow,"edit-product"===e){var t=!1;o("input#doaction, input#doaction2").on("click",(function(e){if(t)return!0;e.preventDefault();var c=o(this),n=c.prev("select").val();if("facebook_include"===n){var r=[];o.each(o('input[name="post[]"]:checked'),(function(){r.push(parseInt(o(this).val(),10))})),o.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:n,products:r},(function(e){e&&!e.success?(closeExistingModal(),new o.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:e.data})):(t=!0,c.trigger("click"))}))}else t=!0,c.trigger("click")}))}if("product"===e){function c(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)}function n(o,e){o?(e.find("option[value='sync_and_show']").show(),e.prop("original")&&e.val(e.prop("original"))):(e.find("option[value='sync_and_show']").hide(),"sync_and_show"===e.val()&&e.val("sync_and_hide"))}function r(o,e){var t=e.find("#wc_facebook_commerce_enabled"),c=t.prop("original");t.prop("checked",!!o&&c).prop("disabled",!o),t.trigger("change"),t.prop("original",c),e.find("#product-not-ready-notice, #variable-product-not-ready-notice").hide(),s()&&!i()?e.find("#variable-product-not-ready-notice").show():o||e.find("#product-not-ready-notice").show()}function a(){return!!(s()?i():"sync_disabled"!==v.val())&&!!(s()||(o("#_regular_price").val()||o("#fb_product_price").val()))&&!(!o("#_manage_stock").prop("checked")||!o("#_stock").val())}function i(){var e=o(".js-variable-fb-sync-toggle");return 0===e.length?!!facebook_for_woocommerce_products_admin.is_sync_enabled_for_product:!!e.map((function(e,t){return"sync_disabled"!==o(t).val()?t:null})).length}function s(){var e=o("select#product-type").val();return!(!e||!e.match(/variable/))}function d(o){o.attr("data-original-value",o.val())}function l(e){return e.map((function(e,t){var c=o(t),n=c.val();return"sync_disabled"===n&&n!==c.attr("data-original-value")})).toArray().indexOf(!0)>-1}function _(e){return v===e?o("input#post_ID").val():e.closest(".woocommerce_variation").find("input[name^=variable_post_id]").val()}function u(e){closeExistingModal(),m=e,g=_(e),new o.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_removed_from_sync_confirm_modal_message,buttons:facebook_for_woocommerce_products_admin.product_removed_from_sync_confirm_modal_buttons}})}function f(){o(facebook_for_woocommerce_products_admin.product_removed_from_sync_field_id).val(b.join(","))}function p(o){b=b.filter((function(e){return e!==o})),f()}var m=null,g=null,b=[];o(document.body).on("click","button.button-product-removed-from-sync-delete",(function(){g&&(closeExistingModal(),b.push(g),f())})).on("click","button.button-product-removed-from-sync-cancel",(function(){var o;closeExistingModal(),m&&((o=m).val(o.attr("data-original-value")),m=null),f()})),o("#facebook_options #wc_facebook_commerce_enabled").on("change",(function(){var e=o(this).prop("checked");e?o(".wc_facebook_commerce_fields").show():o(".wc_facebook_commerce_fields").hide(),o(".product_attributes").find(".woocommerce_attribute").length?o(".show_if_has_attributes").show():o(".show_if_has_attributes").hide(),o(this).prop("original",e)})).trigger("change");var v=o("#wc_facebook_sync_mode"),h=v.closest(".woocommerce_options_panel");d(v),v.on("change",(function(){var o,e,t="sync_disabled"!==v.val();c(t,h),o=t,e=h.find(".wc-facebook-commerce-options-group"),o?e.show():e.hide(),t&&p(_(v)),v.prop("original",v.val()),l(v)&&u(v)})).trigger("change"),o("#_virtual").on("change",(function(){n(!o(this).prop("checked"),v)})).trigger("change");var w=o("#woocommerce-product-data");w.on("change","#_regular_price, #_manage_stock, #_stock, #wc_facebook_sync_mode, #fb_product_price",(function(e){setTimeout((function(){r(a(),o("#facebook_options"))}),1)})),o(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",(function(){var e=o(this),t="sync_disabled"!==e.val();c(t,e.closest(".wc-metabox-content")),r(a(),o("#facebook_options")),t&&p(_(e)),e.prop("original",e.val()),l(e)&&u(e)})),w.on("woocommerce_variations_loaded",(function(){w.find(".js-variable-fb-sync-toggle").each((function(e,t){var n=o(t);c("sync_disabled"!==n.val(),n.closest(".wc-metabox-content")),n.prop("original",n.val()),d(n)})),o(".variable_is_virtual").on("change",(function(){var e=o(this).closest(".wc-metabox-content").find(".js-variable-fb-sync-toggle");n(!o(this).prop("checked"),e)})),r(a(),o("#facebook_options"))})),w.on("change",".js-fb-product-image-source",(function(){var e=o(this).closest(".woocommerce_options_panel, .wc-metabox-content"),t=o(this).val();e.find(".product-image-source-field").closest(".form-field").hide(),e.find(".show-if-product-image-source-".concat(t)).closest(".form-field").show()})),o(".js-fb-product-image-source:checked:visible").trigger("change"),w.on("woocommerce_variations_loaded",(function(){o(".js-variable-fb-sync-toggle:visible").trigger("change"),o(".js-fb-product-image-source:checked:visible").trigger("change"),o(".variable_is_virtual:visible").trigger("change")})),o("#facebook_options").on("click","#product-not-ready-notice-open-modal",(function(e){e.preventDefault(),closeExistingModal(),new o.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_not_ready_modal_message,buttons:facebook_for_woocommerce_products_admin.product_not_ready_modal_buttons}})})),r(a(),h);var y=!1;o('form#post input[type="submit"]').on("click",(function(e){if(o("#wc_facebook_commerce_enabled").prop("checked")&&a()&&o(".wc_facebook_commerce_fields .wc-facebook-google-product-category-select").map((function(e,t){return o(t).val()?o(t).val():null})).length<2)return e.preventDefault(),alert(facebook_for_woocommerce_products_admin.i18n.missing_google_product_category_message),!1;if(y)return!0;e.preventDefault();var t=o(this),c=parseInt(o("input#post_ID").val(),10),n=[],r=o('textarea[name="tax_input[product_tag]"]').length?o('textarea[name="tax_input[product_tag]"]').val().split(","):[],s="sync_disabled"!==v.val(),d=i();o('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each((function(){n.push(parseInt(o(this).val(),10))})),o('#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked').each((function(){r.push(parseInt(o(this).val(),10))})),c>0?o.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:s?"enabled":"disabled",var_sync_enabled:d?"enabled":"disabled",product:c,categories:n,tags:r},(function(e){e&&!e.success&&s?(closeExistingModal(),new o.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:e.data})):(y=!0,t.trigger("click"))})):(y=!0,t.trigger("click"))}))}}))}});
assets/build/admin/settings-commerce.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => '9513715b2a35d2eda9c958baf192798e');
assets/build/admin/settings-commerce.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var o={};function t(r){if(o[r])return o[r].exports;var c=o[r]={i:r,l:!1,exports:{}};return e[r].call(c.exports,c,c.exports,t),c.l=!0,c.exports}t.m=e,t.c=o,t.d=function(e,o,r){t.o(e,o)||Object.defineProperty(e,o,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,o){if(1&o&&(e=t(e)),8&o)return e;if(4&o&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&o&&"string"!=typeof e)for(var c in e)t.d(r,c,function(o){return e[o]}.bind(null,c));return r},t.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(o,"a",o),o},t.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},t.p="",t(t.s=11)}({11:function(e,o){jQuery(document).ready((function(e){var o=e("form.wc-facebook-settings"),t=e("#wc_facebook_google_product_category_id"),r=t.val();o.on("submit",(function(c){o.data("allow-submit")||t.val()===r||(c.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:t.val()?facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_message:facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_message_empty,buttons:facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_buttons}}),e(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",(function(){o.data("allow-submit",!0).find(":submit").trigger("click")})))})),e(".woocommerce-help-tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200})}))}});
assets/build/admin/settings-sync.asset.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php return array('dependencies' => array('wp-polyfill'), 'version' => 'ca852c21b4cd9d57cc0fce8606957fc3');
assets/build/admin/settings-sync.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var o={};function c(n){if(o[n])return o[n].exports;var t=o[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.m=e,c.c=o,c.d=function(e,o,n){c.o(e,o)||Object.defineProperty(e,o,{enumerable:!0,get:n})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,o){if(1&o&&(e=c(e)),8&o)return e;if(4&o&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(c.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&o&&"string"!=typeof e)for(var t in e)c.d(n,t,function(o){return e[o]}.bind(null,t));return n},c.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(o,"a",o),o},c.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},c.p="",c(c.s=12)}({12:function(e,o){jQuery(document).ready((function(e){function o(o){e(".product-sync-field").each((function(){var c=e(this);e(this).hasClass("wc-enhanced-select")&&(c=e(this).next("span.select2-container")),o?c.css("pointer-events","all").css("opacity","1.0"):c.css("pointer-events","none").css("opacity","0.4")}))}e(".woocommerce-help-tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}),e("form.wc-facebook-settings").hasClass("disconnected")&&o(!1),e("input#wc_facebook_enable_product_sync").on("change",(function(c){e("form.wc-facebook-settings").hasClass("disconnected")?e(this).css("pointer-events","none").css("opacity","0.4"):o(e(this).is(":checked"))})).trigger("change");var c=!1;function n(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;t(e),window.syncStatusInterval||(window.syncStatusInterval=setInterval(r,1e4))}function t(){var c=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;o(!1),e('input#wc_facebook_enable_product_sync, input[name="save_product_sync_settings"]').css("pointer-events","none").css("opacity","0.4");var n=facebook_for_woocommerce_settings_sync.i18n.sync_in_progress;c&&(n=(n+=c>1?facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_plural:facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_singular).replace("{count}",c)),e("#sync_progress").show().html(n).css("color","inherit"),facebook_for_woocommerce_settings_sync.sync_in_progress=!0}function s(){var c=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";facebook_for_woocommerce_settings_sync.sync_in_progress=!1,clearInterval(window.syncStatusInterval),window.syncStatusInterval=null,o(!0),e('input#wc_facebook_enable_product_sync, input[name="save_product_sync_settings"]').css("pointer-events","all").css("opacity","1"),c?e("#sync_progress").show().html(c).css("color","#DC3232"):e("#sync_progress").hide()}function r(){facebook_for_woocommerce_settings_sync.sync_in_progress&&e.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"wc_facebook_get_sync_status",nonce:facebook_for_woocommerce_settings_sync.sync_status_nonce},(function(e){console.log(e),e.success&&(e.data>0?n(e.data):s())}))}e('input[name="save_product_sync_settings"]').on("click",(function(o){if(c)return!0;o.preventDefault();var n,t,s,r,_=e(this),i=(s=e("#wc_facebook_excluded_product_category_ids").val(),r=[],window.facebook_for_woocommerce_settings_sync&&window.facebook_for_woocommerce_settings_sync.excluded_category_ids&&(r=window.facebook_for_woocommerce_settings_sync.excluded_category_ids),e(s).not(r).get()),a=(n=e("#wc_facebook_excluded_product_tag_ids").val(),t=[],window.facebook_for_woocommerce_settings_sync&&window.facebook_for_woocommerce_settings_sync.excluded_tag_ids&&(t=window.facebook_for_woocommerce_settings_sync.excluded_tag_ids),e(n).not(t).get());i.length>0||a.length>0?e.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"facebook_for_woocommerce_set_excluded_terms_prompt",security:facebook_for_woocommerce_settings_sync.set_excluded_terms_prompt_nonce,categories:i,tags:a},(function(o){o&&!o.success?(e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),e(".facebook-for-woocommerce-confirm-settings-change").on("click",(function(){blockModal(),c=!0,_.trigger("click")}))):(c=!0,_.trigger("click"))})):(c=!0,_.trigger("click"))})),facebook_for_woocommerce_settings_sync.sync_in_progress&&n(),e("#woocommerce-facebook-settings-sync-products").click((function(o){if(o.preventDefault(),confirm(facebook_for_woocommerce_settings_sync.i18n.confirm_sync)){t();var c=Date.now();e.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"wc_facebook_sync_products",nonce:facebook_for_woocommerce_settings_sync.sync_products_nonce},(function(e){if(console.log(e),e.success)setTimeout(r,Math.max(0,1e4-(Date.now()-c)));else{var o=facebook_for_woocommerce_settings_sync.i18n.general_error;e.data&&e.data.length>0&&(o=e.data),s(o)}})).fail((function(){s(facebook_for_woocommerce_settings_sync.i18n.general_error)}))}}))}))}});
assets/js/admin/facebook-for-woocommerce-product-sets-admin.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(e){jQuery(".select2.wc-facebook").length&&(jQuery(".select2.wc-facebook").select2().addClass("visible").attr("disabled",!1),jQuery(".select2.updating-message").addClass("hidden"),jQuery(document).ajaxSuccess(function(e,a,t){t=new URLSearchParams(t.data);t.has("action")&&"add-tag"===t.get("action")&&t.has("taxonomy")&&"fb_product_set"===t.get("taxonomy")&&jQuery(".select2.wc-facebook").select2().val(null).trigger("change")}))});
 
assets/js/admin/facebook-for-woocommerce-products-admin.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(r){var t,n,e,c,i,o,s,a,_,d,l,u,p,f,m,g,b,v,h,w,k,y,x,j,M,D,E=window.pagenow.length?window.pagenow:"";window.typenow.length&&window.typenow;"edit-product"===E&&(t=!1,r("input#doaction, input#doaction2").on("click",function(o){if(t)return!0;o.preventDefault();var e,c=r(this),o=c.prev("select").val();"facebook_include"===o?(e=[],r.each(r('input[name="post[]"]:checked'),function(){e.push(parseInt(r(this).val(),10))}),r.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:o,products:e},function(o){o&&!o.success?(closeExistingModal(),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(t=!0,c.trigger("click"))})):(t=!0,c.trigger("click"))})),"product"===E&&(n=function(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)},e=function(o,e){o?(e.find("option[value='sync_and_show']").show(),e.prop("original")&&e.val(e.prop("original"))):(e.find("option[value='sync_and_show']").hide(),"sync_and_show"===e.val()&&e.val("sync_and_hide"))},c=function(o,e){var c=e.find("#wc_facebook_commerce_enabled"),t=c.prop("original");c.prop("checked",!!o&&t).prop("disabled",!o),c.trigger("change"),c.prop("original",t),e.find("#product-not-ready-notice, #variable-product-not-ready-notice").hide(),d()&&!s()?e.find("#variable-product-not-ready-notice").show():o||e.find("#product-not-ready-notice").show()},i=function(){return!!o()&&(!!_()&&!!u())},o=function(){return(d()?s:a)()},s=function(){var o=r(".js-variable-fb-sync-toggle");return 0===o.length?!!facebook_for_woocommerce_products_admin.is_sync_enabled_for_product:!!o.map(function(o,e){return"sync_disabled"!==r(e).val()?e:null}).length},a=function(){return"sync_disabled"!==x.val()},_=function(){return!!d()||l()},d=function(){var o=r("select#product-type").val();return!(!o||!o.match(/variable/))},l=function(){return!(!r("#_regular_price").val()&&!r("#fb_product_price").val())},u=function(){return p()},p=function(){return!(!r("#_manage_stock").prop("checked")||!r("#_stock").val())},f=function(o){o.attr("data-original-value",o.val())},m=function(o){return-1<o.map(function(o,e){var c=r(e),e=c.val();return"sync_disabled"===e&&e!==c.attr("data-original-value")}).toArray().indexOf(!0)},g=function(o){return(x===o?r("input#post_ID"):o.closest(".woocommerce_variation").find("input[name^=variable_post_id]")).val()},b=function(o){closeExistingModal(),k=g(w=o),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_removed_from_sync_confirm_modal_message,buttons:facebook_for_woocommerce_products_admin.product_removed_from_sync_confirm_modal_buttons}})},v=function(){r(facebook_for_woocommerce_products_admin.product_removed_from_sync_field_id).val(y.join(","))},h=function(e){y=y.filter(function(o){return o!==e}),v()},k=w=null,y=[],r(document.body).on("click","button.button-product-removed-from-sync-delete",function(){k&&(closeExistingModal(),y.push(k),v())}).on("click","button.button-product-removed-from-sync-cancel",function(){var o;closeExistingModal(),w&&((o=w).val(o.attr("data-original-value")),w=null),v()}),r("#facebook_options #wc_facebook_commerce_enabled").on("change",function(){var o=r(this).prop("checked");o?r(".wc_facebook_commerce_fields").show():r(".wc_facebook_commerce_fields").hide(),r(".product_attributes").find(".woocommerce_attribute").length?r(".show_if_has_attributes").show():r(".show_if_has_attributes").hide(),r(this).prop("original",o)}).trigger("change"),x=r("#wc_facebook_sync_mode"),j=x.closest(".woocommerce_options_panel"),f(x),x.on("change",function(){var o,e,c="sync_disabled"!==x.val();n(c,j),o=c,e=(e=j).find(".wc-facebook-commerce-options-group"),o?e.show():e.hide(),c&&h(g(x)),x.prop("original",x.val()),m(x)&&b(x)}).trigger("change"),r("#_virtual").on("change",function(){e(!r(this).prop("checked"),x)}).trigger("change"),(M=r("#woocommerce-product-data")).on("change","#_regular_price, #_manage_stock, #_stock, #wc_facebook_sync_mode, #fb_product_price",function(o){setTimeout(function(){c(i(),r("#facebook_options"))},1)}),r(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",function(){var o=r(this),e="sync_disabled"!==o.val();n(e,o.closest(".wc-metabox-content")),c(i(),r("#facebook_options")),e&&h(g(o)),o.prop("original",o.val()),m(o)&&b(o)}),M.on("woocommerce_variations_loaded",function(){M.find(".js-variable-fb-sync-toggle").each(function(o,e){e=r(e);n("sync_disabled"!==e.val(),e.closest(".wc-metabox-content")),e.prop("original",e.val()),f(e)}),r(".variable_is_virtual").on("change",function(){var o=r(this).closest(".wc-metabox-content").find(".js-variable-fb-sync-toggle");e(!r(this).prop("checked"),o)}),c(i(),r("#facebook_options"))}),M.on("change",".js-fb-product-image-source",function(){var o=r(this).closest(".woocommerce_options_panel, .wc-metabox-content"),e=r(this).val();o.find(".product-image-source-field").closest(".form-field").hide(),o.find(".show-if-product-image-source-"+e).closest(".form-field").show()}),r(".js-fb-product-image-source:checked:visible").trigger("change"),M.on("woocommerce_variations_loaded",function(){r(".js-variable-fb-sync-toggle:visible").trigger("change"),r(".js-fb-product-image-source:checked:visible").trigger("change"),r(".variable_is_virtual:visible").trigger("change")}),r("#facebook_options").on("click","#product-not-ready-notice-open-modal",function(o){o.preventDefault(),closeExistingModal(),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_not_ready_modal_message,buttons:facebook_for_woocommerce_products_admin.product_not_ready_modal_buttons}})}),c(i(),j),D=!1,r('form#post input[type="submit"]').on("click",function(o){if(!!r("#wc_facebook_commerce_enabled").prop("checked")&&(!!i()&&r(".wc_facebook_commerce_fields .wc-facebook-google-product-category-select").map(function(o,e){return r(e).val()?r(e).val():null}).length<2))return o.preventDefault(),alert(facebook_for_woocommerce_products_admin.i18n.missing_google_product_category_message),!1;if(D)return!0;o.preventDefault();var e=r(this),c=parseInt(r("input#post_ID").val(),10),t=[],n=r('textarea[name="tax_input[product_tag]"]').length?r('textarea[name="tax_input[product_tag]"]').val().split(","):[],a="sync_disabled"!==x.val(),o=s();r('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each(function(){t.push(parseInt(r(this).val(),10))}),r('#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked').each(function(){n.push(parseInt(r(this).val(),10))}),0<c?r.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:a?"enabled":"disabled",var_sync_enabled:o?"enabled":"disabled",product:c,categories:t,tags:n},function(o){o&&!o.success&&a?(closeExistingModal(),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(D=!0,e.trigger("click"))}):(D=!0,e.trigger("click"))}))});
 
assets/js/admin/facebook-for-woocommerce-products-admin.min.old.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";jQuery(document).ready(function(i){var o=window.pagenow.length?window.pagenow:"";window.typenow.length&&window.typenow;if("edit-product"===o){var a=!1;i("input#doaction, input#doaction2").on("click",function(o){if(a)return!0;o.preventDefault();var e=i(this),c=e.prev("select").val();if("facebook_include"===c){var t=[];i.each(i('input[name="post[]"]:checked'),function(){t.push(parseInt(i(this).val(),10))}),i.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:c,products:t},function(o){o&&!o.success?(i("#wc-backbone-modal-dialog .modal-close").trigger("click"),new i.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(a=!0,e.trigger("click"))})}else a=!0,e.trigger("click")})}if("product"===o){var t=function(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)},e=function(o,e){o?(e.find("option[value='sync_and_show']").show(),e.prop("original")&&e.val(e.prop("original"))):(e.find("option[value='sync_and_show']").hide(),"sync_and_show"===e.val()&&e.val("sync_and_hide"))},c=function(o,e){var c=e.find("#wc_facebook_commerce_enabled"),t=c.prop("original");c.prop("checked",!!o&&t).prop("disabled",!o),c.trigger("change"),c.prop("original",t),e.find("#product-not-ready-notice, #variable-product-not-ready-notice").hide(),i("select#product-type").val().match(/variable/)&&!r()?e.find("#variable-product-not-ready-notice").show():o||e.find("#product-not-ready-notice").show()},s=function(){return!!n()&&(!!_()&&!!u())},n=function(){return(i("select#product-type").val().match(/variable/)?r:d)()},r=function(){var o=i(".js-variable-fb-sync-toggle");return 0===o.length?!!facebook_for_woocommerce_products_admin.is_sync_enabled_for_product:!!o.map(function(o,e){return"sync_disabled"!==i(e).val()?e:null}).length},d=function(){return"sync_disabled"!==i("#wc_facebook_sync_mode").val()},_=function(){return!!i("select#product-type").val().match(/variable/)||l()},l=function(){return i("#_regular_price").val().length||i("#fb_product_price").val().length},u=function(){return p()},p=function(){return i("#_manage_stock").prop("checked")&&i("#_stock").val().length};i("#facebook_options #wc_facebook_commerce_enabled").on("change",function(){var o=i(this).prop("checked");o?i(".wc_facebook_commerce_fields").show():i(".wc_facebook_commerce_fields").hide(),i(".product_attributes").find(".woocommerce_attribute").length?i(".show_if_has_attributes").show():i(".show_if_has_attributes").hide(),i(this).prop("original",o)}).trigger("change");var f=i("#wc_facebook_sync_mode"),m=f.closest(".woocommerce_options_panel");f.on("change",function(){var o,e,c="sync_disabled"!==i(this).val();t(c,m),o=c,e=m.find(".wc-facebook-commerce-options-group"),o?e.show():e.hide(),f.prop("original",i(this).val())}).trigger("change"),i("#_virtual").on("change",function(){e(!i(this).prop("checked"),f)}).trigger("change"),i("#woocommerce-product-data").on("change","#_regular_price, #_manage_stock, #_stock, #wc_facebook_sync_mode, #fb_product_price",function(o){setTimeout(function(){c(s(),i("#facebook_options"))},1)}),i(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",function(){t("sync_disabled"!==i(this).val(),i(this).closest(".wc-metabox-content")),c(s(),i("#facebook_options")),i(this).prop("original",i(this).val())}),i("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){i(".js-variable-fb-sync-toggle").each(function(){t("sync_disabled"!==i(this).val(),i(this).closest(".wc-metabox-content")),i(this).prop("original",i(this).val())}),i(".variable_is_virtual").on("change",function(){var o=i(this).closest(".wc-metabox-content").find(".js-variable-fb-sync-toggle");e(!i(this).prop("checked"),o)}),c(s(),i("#facebook_options"))}),i("#woocommerce-product-data").on("change",".js-fb-product-image-source",function(){var o=i(this).closest(".woocommerce_options_panel, .wc-metabox-content"),e=i(this).val();o.find(".product-image-source-field").closest(".form-field").hide(),o.find(".show-if-product-image-source-"+e).closest(".form-field").show()}),i(".js-fb-product-image-source:checked:visible").trigger("change"),i("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){i(".js-variable-fb-sync-toggle:visible").trigger("change"),i(".js-fb-product-image-source:checked:visible").trigger("change"),i(".variable_is_virtual:visible").trigger("change")}),i("#facebook_options").on("click","#product-not-ready-notice-open-modal",function(o){o.preventDefault(),i("#wc-backbone-modal-dialog .modal-close").trigger("click"),new i.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_not_ready_modal_message,buttons:facebook_for_woocommerce_products_admin.product_not_ready_modal_buttons}})}),c(s(),m);var g=!1;i('form#post input[type="submit"]').on("click",function(o){if(!!i("#wc_facebook_commerce_enabled").prop("checked")&&!!s()&&i(".wc_facebook_commerce_fields .wc-facebook-google-product-category-select").map(function(o,e){return i(e).val()?i(e).val():null}).length<2)return o.preventDefault(),alert(facebook_for_woocommerce_products_admin.i18n.missing_google_product_category_message),!1;if(g)return!0;o.preventDefault();var e=i(this),c=parseInt(i("input#post_ID").val(),10),t=[],a=i('textarea[name="tax_input[product_tag]"]').length?i('textarea[name="tax_input[product_tag]"]').val().split(","):[],n="sync_disabled"!==i("#wc_facebook_sync_mode").val(),r="sync_disabled"!==i(".js-variable-fb-sync-toggle").val();i('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each(function(){t.push(parseInt(i(this).val(),10))}),i('#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked').each(function(){a.push(parseInt(i(this).val(),10))}),0<c?i.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:n?"enabled":"disabled",var_sync_enabled:r?"enabled":"disabled",product:c,categories:t,tags:a},function(o){o&&!o.success&&n?(i("#wc-backbone-modal-dialog .modal-close").trigger("click"),new i.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(g=!0,e.trigger("click"))}):(g=!0,e.trigger("click"))})}});
2
- //# sourceMappingURL=facebook-for-woocommerce-products-admin.min.js.map
 
 
assets/js/admin/facebook-for-woocommerce-products-admin.min.old.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(r){var t,c,e,a,i,o,n,s,d,_,l,u,p,f,m,g=window.pagenow.length?window.pagenow:"";window.typenow.length&&window.typenow,"edit-product"===g&&(t=!1,r("input#doaction, input#doaction2").on("click",function(o){if(t)return!0;o.preventDefault();var e,c=r(this),o=c.prev("select").val();"facebook_include"===o?(e=[],r.each(r('input[name="post[]"]:checked'),function(){e.push(parseInt(r(this).val(),10))}),r.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:o,products:e},function(o){o&&!o.success?(r("#wc-backbone-modal-dialog .modal-close").trigger("click"),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(t=!0,c.trigger("click"))})):(t=!0,c.trigger("click"))})),"product"===g&&(c=function(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)},e=function(o,e){o?(e.find("option[value='sync_and_show']").show(),e.prop("original")&&e.val(e.prop("original"))):(e.find("option[value='sync_and_show']").hide(),"sync_and_show"===e.val()&&e.val("sync_and_hide"))},a=function(o,e){var c,t=(c=e.find("#wc_facebook_commerce_enabled")).prop("original");c.prop("checked",!!o&&t).prop("disabled",!o),c.trigger("change"),c.prop("original",t),e.find("#product-not-ready-notice, #variable-product-not-ready-notice").hide(),r("select#product-type").val().match(/variable/)&&!n()?e.find("#variable-product-not-ready-notice").show():o||e.find("#product-not-ready-notice").show()},i=function(){return!!o()&&!!d()&&!!l()},o=function(){return(r("select#product-type").val().match(/variable/)?n:s)()},n=function(){var o=r(".js-variable-fb-sync-toggle");return 0===o.length?!!facebook_for_woocommerce_products_admin.is_sync_enabled_for_product:!!o.map(function(o,e){return"sync_disabled"!==r(e).val()?e:null}).length},s=function(){return"sync_disabled"!==r("#wc_facebook_sync_mode").val()},d=function(){return!!r("select#product-type").val().match(/variable/)||_()},_=function(){return r("#_regular_price").val().length||r("#fb_product_price").val().length},l=function(){return u()},u=function(){return r("#_manage_stock").prop("checked")&&r("#_stock").val().length},r("#facebook_options #wc_facebook_commerce_enabled").on("change",function(){var o=r(this).prop("checked");o?r(".wc_facebook_commerce_fields").show():r(".wc_facebook_commerce_fields").hide(),r(".product_attributes").find(".woocommerce_attribute").length?r(".show_if_has_attributes").show():r(".show_if_has_attributes").hide(),r(this).prop("original",o)}).trigger("change"),p=r("#wc_facebook_sync_mode"),f=p.closest(".woocommerce_options_panel"),p.on("change",function(){var o,e="sync_disabled"!==r(this).val();c(e,f),o=e,e=f.find(".wc-facebook-commerce-options-group"),o?e.show():e.hide(),p.prop("original",r(this).val())}).trigger("change"),r("#_virtual").on("change",function(){e(!r(this).prop("checked"),p)}).trigger("change"),r("#woocommerce-product-data").on("change","#_regular_price, #_manage_stock, #_stock, #wc_facebook_sync_mode, #fb_product_price",function(o){setTimeout(function(){a(i(),r("#facebook_options"))},1)}),r(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",function(){c("sync_disabled"!==r(this).val(),r(this).closest(".wc-metabox-content")),a(i(),r("#facebook_options")),r(this).prop("original",r(this).val())}),r("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){r(".js-variable-fb-sync-toggle").each(function(){c("sync_disabled"!==r(this).val(),r(this).closest(".wc-metabox-content")),r(this).prop("original",r(this).val())}),r(".variable_is_virtual").on("change",function(){var o=r(this).closest(".wc-metabox-content").find(".js-variable-fb-sync-toggle");e(!r(this).prop("checked"),o)}),a(i(),r("#facebook_options"))}),r("#woocommerce-product-data").on("change",".js-fb-product-image-source",function(){var o=r(this).closest(".woocommerce_options_panel, .wc-metabox-content"),e=r(this).val();o.find(".product-image-source-field").closest(".form-field").hide(),o.find(".show-if-product-image-source-"+e).closest(".form-field").show()}),r(".js-fb-product-image-source:checked:visible").trigger("change"),r("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){r(".js-variable-fb-sync-toggle:visible").trigger("change"),r(".js-fb-product-image-source:checked:visible").trigger("change"),r(".variable_is_virtual:visible").trigger("change")}),r("#facebook_options").on("click","#product-not-ready-notice-open-modal",function(o){o.preventDefault(),r("#wc-backbone-modal-dialog .modal-close").trigger("click"),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_products_admin.product_not_ready_modal_message,buttons:facebook_for_woocommerce_products_admin.product_not_ready_modal_buttons}})}),a(i(),f),m=!1,r('form#post input[type="submit"]').on("click",function(o){if(r("#wc_facebook_commerce_enabled").prop("checked")&&i()&&r(".wc_facebook_commerce_fields .wc-facebook-google-product-category-select").map(function(o,e){return r(e).val()?r(e).val():null}).length<2)return o.preventDefault(),alert(facebook_for_woocommerce_products_admin.i18n.missing_google_product_category_message),!1;if(m)return!0;o.preventDefault();var e=r(this),c=parseInt(r("input#post_ID").val(),10),t=[],a=r('textarea[name="tax_input[product_tag]"]').length?r('textarea[name="tax_input[product_tag]"]').val().split(","):[],n="sync_disabled"!==r("#wc_facebook_sync_mode").val(),o="sync_disabled"!==r(".js-variable-fb-sync-toggle").val();r('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each(function(){t.push(parseInt(r(this).val(),10))}),r('#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked').each(function(){a.push(parseInt(r(this).val(),10))}),0<c?r.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:n?"enabled":"disabled",var_sync_enabled:o?"enabled":"disabled",product:c,categories:t,tags:a},function(o){o&&!o.success&&n?(r("#wc-backbone-modal-dialog .modal-close").trigger("click"),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data})):(m=!0,e.trigger("click"))}):(m=!0,e.trigger("click"))}))});
 
assets/js/admin/facebook-for-woocommerce-settings-messenger.js DELETED
@@ -1,96 +0,0 @@
1
- jQuery( document ).ready( function( $ ) {
2
-
3
- /**
4
- * Toggles availability of input in setting groups.
5
- *
6
- * @param {boolean} enable whether fields in this group should be enabled or not
7
- */
8
- function toggleSettingOptions( enable ) {
9
-
10
- $( '.messenger-field' ).each( function() {
11
-
12
- let $element = $( this );
13
-
14
- if ( $( this ).hasClass( 'wc-enhanced-select' ) ) {
15
- $element = $( this ).next( 'span.select2-container' );
16
- }
17
-
18
- if ( enable ) {
19
- $element.css( 'pointer-events', 'all' ).css( 'opacity', '1.0' );
20
- } else {
21
- $element.css( 'pointer-events', 'none' ).css( 'opacity', '0.4' );
22
- }
23
- } );
24
- }
25
-
26
- if ( $( 'form.wc-facebook-settings' ).hasClass( 'disconnected' ) ) {
27
- toggleSettingOptions( false );
28
- }
29
-
30
- $( 'input#wc_facebook_enable_messenger' ).on( 'change', function ( e ) {
31
-
32
- if ( $( 'form.wc-facebook-settings' ).hasClass( 'disconnected' ) ) {
33
- $( this ).css( 'pointer-events', 'none' ).css( 'opacity', '0.4' );
34
- return;
35
- }
36
-
37
- toggleSettingOptions( $( this ).is( ':checked' ) );
38
-
39
- } ).trigger( 'change' );
40
-
41
- // adds a character counter on the Messenger greeting textarea
42
- $( 'textarea#wc_facebook_messenger_greeting' ).on( 'focus change keyup keydown keypress', function() {
43
-
44
- const maxChars = parseInt( $( this ).attr( 'maxlength' ), 10 );
45
- let chars = $( this ).val().length,
46
- $counter = $( 'span.characters-counter' ),
47
- $warning = $counter.find( 'span' );
48
-
49
- $counter.html( chars + ' / ' + maxChars + '<br/>' ).append( $warning ).css( 'display', 'block' );
50
-
51
- if ( chars > maxChars ) {
52
- $counter.css( 'color', '#DC3232' ).find( 'span' ).show();
53
- } else {
54
- $counter.css( 'color', '#999999' ).find( 'span' ).hide();
55
- }
56
-
57
- } );
58
-
59
-
60
- // init the color picker
61
- $( '#wc_facebook_messenger_color_hex' )
62
-
63
- .iris( {
64
- change: function( event, ui ) {
65
- $( this ).parent().find( '.colorpickpreview' ).css( { backgroundColor: ui.color.toString() } );
66
- },
67
- hide: true,
68
- border: true
69
- })
70
-
71
- .on( 'click focus', function( event ) {
72
- event.stopPropagation();
73
- $( '.iris-picker' ).hide();
74
- $( this ).closest( 'td' ).find( '.iris-picker' ).show();
75
- $( this ).data( 'original-value', $( this ).val() );
76
- } )
77
-
78
- .on( 'change', function() {
79
-
80
- if ( $( this ).is( '.iris-error' ) ) {
81
-
82
- var original_value = $( this ).data( 'original-value' );
83
-
84
- if ( original_value.match( /^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/ ) ) {
85
- $( this ).val( $( this ).data( 'original-value' ) ).change();
86
- } else {
87
- $( this ).val( '' ).change();
88
- }
89
- }
90
- } );
91
-
92
- $( 'body' ).on( 'click', function() {
93
- $( '.iris-picker' ).hide();
94
- } );
95
-
96
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/admin/facebook-for-woocommerce-settings-messenger.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(c){function s(s){c(".messenger-field").each(function(){var e=c(this);c(this).hasClass("wc-enhanced-select")&&(e=c(this).next("span.select2-container")),s?e.css("pointer-events","all").css("opacity","1.0"):e.css("pointer-events","none").css("opacity","0.4")})}c("form.wc-facebook-settings").hasClass("disconnected")&&s(!1),c("input#wc_facebook_enable_messenger").on("change",function(e){c("form.wc-facebook-settings").hasClass("disconnected")?c(this).css("pointer-events","none").css("opacity","0.4"):s(c(this).is(":checked"))}).trigger("change"),c("textarea#wc_facebook_messenger_greeting").on("focus change keyup keydown keypress",function(){var e=parseInt(c(this).attr("maxlength"),10),s=c(this).val().length,n=c("span.characters-counter"),i=n.find("span");n.html(s+" / "+e+"<br/>").append(i).css("display","block"),e<s?n.css("color","#DC3232").find("span").show():n.css("color","#999999").find("span").hide()}),c("#wc_facebook_messenger_color_hex").iris({change:function(e,s){c(this).parent().find(".colorpickpreview").css({backgroundColor:s.color.toString()})},hide:!0,border:!0}).on("click focus",function(e){e.stopPropagation(),c(".iris-picker").hide(),c(this).closest("td").find(".iris-picker").show(),c(this).data("original-value",c(this).val())}).on("change",function(){c(this).is(".iris-error")&&(c(this).data("original-value").match(/^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/)?c(this).val(c(this).data("original-value")):c(this).val("")).change()}),c("body").on("click",function(){c(".iris-picker").hide()})});
 
assets/js/admin/facebook-for-woocommerce-settings-sync.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(s){function c(e){s(".product-sync-field").each(function(){var o=s(this);s(this).hasClass("wc-enhanced-select")&&(o=s(this).next("span.select2-container")),e?o.css("pointer-events","all").css("opacity","1.0"):o.css("pointer-events","none").css("opacity","0.4")})}s(".woocommerce-help-tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}),s("form.wc-facebook-settings").hasClass("disconnected")&&c(!1),s("input#wc_facebook_enable_product_sync").on("change",function(o){s("form.wc-facebook-settings").hasClass("disconnected")?s(this).css("pointer-events","none").css("opacity","0.4"):c(s(this).is(":checked"))}).trigger("change");var t=!1;function e(o){n(0<arguments.length&&void 0!==o?o:null),window.syncStatusInterval||(window.syncStatusInterval=setInterval(r,1e4))}function n(o){var e=0<arguments.length&&void 0!==o?o:null;c(!1),s('input#wc_facebook_enable_product_sync, input[name="save_product_sync_settings"]').css("pointer-events","none").css("opacity","0.4");o=facebook_for_woocommerce_settings_sync.i18n.sync_in_progress;e&&(o=(o+=1<e?facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_plural:facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_singular).replace("{count}",e)),s("#sync_progress").show().html(o).css("color","inherit"),facebook_for_woocommerce_settings_sync.sync_in_progress=!0}function _(o){o=0<arguments.length&&void 0!==o?o:"";facebook_for_woocommerce_settings_sync.sync_in_progress=!1,clearInterval(window.syncStatusInterval),c(!(window.syncStatusInterval=null)),s('input#wc_facebook_enable_product_sync, input[name="save_product_sync_settings"]').css("pointer-events","all").css("opacity","1"),o?s("#sync_progress").show().html(o).css("color","#DC3232"):s("#sync_progress").hide()}function r(){facebook_for_woocommerce_settings_sync.sync_in_progress&&s.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"wc_facebook_get_sync_status",nonce:facebook_for_woocommerce_settings_sync.sync_status_nonce},function(o){console.log(o),o.success&&(0<o.data?e(o.data):_())})}s('input[name="save_product_sync_settings"]').on("click",function(o){if(t)return!0;o.preventDefault();var e,c,n=s(this),o=(e=s("#wc_facebook_excluded_product_category_ids").val(),c=[],window.facebook_for_woocommerce_settings_sync&&window.facebook_for_woocommerce_settings_sync.excluded_category_ids&&(c=window.facebook_for_woocommerce_settings_sync.excluded_category_ids),s(e).not(c).get()),c=(e=s("#wc_facebook_excluded_product_tag_ids").val(),c=[],window.facebook_for_woocommerce_settings_sync&&window.facebook_for_woocommerce_settings_sync.excluded_tag_ids&&(c=window.facebook_for_woocommerce_settings_sync.excluded_tag_ids),s(e).not(c).get());0<o.length||0<c.length?s.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"facebook_for_woocommerce_set_excluded_terms_prompt",security:facebook_for_woocommerce_settings_sync.set_excluded_terms_prompt_nonce,categories:o,tags:c},function(o){o&&!o.success?(s("#wc-backbone-modal-dialog .modal-close").trigger("click"),new s.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),s(".facebook-for-woocommerce-confirm-settings-change").on("click",function(){blockModal(),t=!0,n.trigger("click")})):(t=!0,n.trigger("click"))}):(t=!0,n.trigger("click"))}),facebook_for_woocommerce_settings_sync.sync_in_progress&&e(),s("#woocommerce-facebook-settings-sync-products").click(function(o){var c;o.preventDefault(),confirm(facebook_for_woocommerce_settings_sync.i18n.confirm_sync)&&(n(),c=Date.now(),s.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"wc_facebook_sync_products",nonce:facebook_for_woocommerce_settings_sync.sync_products_nonce},function(o){var e;console.log(o),o.success?setTimeout(r,Math.max(0,1e4-(Date.now()-c))):(e=facebook_for_woocommerce_settings_sync.i18n.general_error,_(e=o.data&&0<o.data.length?o.data:e))}).fail(function(){_(facebook_for_woocommerce_settings_sync.i18n.general_error)}))})});
 
assets/js/admin/google-product-category-fields.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";var _createClass=function(){function a(e,t){for(var o=0;o<t.length;o++){var a=t[o];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(e,t,o){return t&&a(e.prototype,t),o&&a(e,o),e}}();function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}jQuery(document).ready(function(n){function a(e,t){var o=this;_classCallCheck(this,a),this.categories=e,this.input_id=t;t=n("#"+this.input_id);n('<div id="wc-facebook-google-product-category-fields"></div>').insertBefore(t).on("change","select.wc-facebook-google-product-category-select",function(e){o.onChange(n(e.target))}),this.addInitialSelects(t.val());t=this.globalsHolder().enhanced_attribute_optional_selector;void 0!==t&&n("#"+t).on("change",function(){n(".wc-facebook-enhanced-catalog-attribute-optional-row").toggleClass("hidden",!n(this).prop("checked"))})}window.WC_Facebook_Google_Product_Category_Fields=(_createClass(a,[{key:"globalsHolder",value:function(){return"undefined"!=typeof facebook_for_woocommerce_product_categories?facebook_for_woocommerce_product_categories:"undefined"!=typeof facebook_for_woocommerce_settings_sync?facebook_for_woocommerce_settings_sync:facebook_for_woocommerce_products_admin}},{key:"getPageType",value:function(){return"undefined"!=typeof facebook_for_woocommerce_product_categories?0===n("input[name=tag_ID]").length?this.globalsHolder().enhanced_attribute_page_type_add_category:this.globalsHolder().enhanced_attribute_page_type_edit_category:this.globalsHolder().enhanced_attribute_page_type_edit_product}},{key:"addInitialSelects",value:function(e){var t=this;e?(this.getSelectedCategoryIds(e).forEach(function(e){t.addSelect(t.getOptions(e[1]),e[0])}),e=this.getOptions(e),Object.keys(e).length&&this.addSelect(e)):(this.addSelect(this.getOptions()),this.addSelect({}))}},{key:"requestAttributesIfValid",value:function(){var e,t,o;"true"===n("#wc_facebook_can_show_enhanced_catalog_attributes_id").val()&&(n(".wc-facebook-enhanced-catalog-attribute-row").remove(),this.isValid()&&(e="#"+this.input_id,t=n(e).parents("div.form-field"),o=this.globalsHolder().enhanced_attribute_optional_selector,this.getPageType()===this.globalsHolder().enhanced_attribute_page_type_edit_category?t=n(e).parents("tr.form-field"):this.getPageType()===this.globalsHolder().enhanced_attribute_page_type_edit_product&&(t=n(e).parents("p.form-field")),n.get(this.globalsHolder().ajax_url,{action:"wc_facebook_enhanced_catalog_attributes",security:"",selected_category:n(e).val(),tag_id:parseInt(n("input[name=tag_ID]").val(),10),taxonomy:n("input[name=taxonomy]").val(),item_id:parseInt(n("input[name=post_ID]").val(),10),page_type:this.getPageType()},function(e){e=n(e);n("#"+o,e).on("change",function(){n(".wc-facebook-enhanced-catalog-attribute-optional-row").toggleClass("hidden",!n(this).prop("checked"))}),e.insertAfter(t),n(document.body).trigger("init_tooltips")})))}},{key:"onChange",value:function(e){e.hasClass("locked")&&e.closest(".wc-facebook-google-product-category-field").nextAll().remove();var t,o=e.val();o?(t=this.getOptions(o),Object.keys(t).length&&this.addSelect(t)):(o=e.closest("#wc-facebook-google-product-category-fields").find(".wc-facebook-google-product-category-select").not(e).last().val())||this.addSelect({}),n("#"+this.input_id).val(o),this.requestAttributesIfValid()}},{key:"isValid",value:function(){return 2<=n(".wc-facebook-google-product-category-select").filter(function(e,t){return""!==n(t).val()}).length}},{key:"addSelect",value:function(t,e){var o=n("#wc-facebook-google-product-category-fields"),a=o.find(".wc-facebook-google-product-category-select"),c=n('<select class="wc-enhanced-select wc-facebook-google-product-category-select"></select>');a.addClass("locked"),o.append(n('<div class="wc-facebook-google-product-category-field" style="margin-bottom: 16px">').append(c)),c.attr("data-placeholder",this.getSelectPlaceholder(a,t)).append(n('<option value=""></option>')),Object.keys(t).forEach(function(e){c.append(n('<option value="'+e+'">'+t[e]+"</option>"))}),c.val(e).select2({allowClear:!0})}},{key:"getSelectPlaceholder",value:function(e,t){return 0===e.length?facebook_for_woocommerce_google_product_category.i18n.top_level_dropdown_placeholder:1===e.length&&0===Object.keys(t).length?facebook_for_woocommerce_google_product_category.i18n.second_level_empty_dropdown_placeholder:facebook_for_woocommerce_google_product_category.i18n.general_dropdown_placeholder}},{key:"getOptions",value:function(e){return void 0===e||""===e?this.getTopLevelOptions():void 0===this.categories[e]||void 0===this.categories[e].options?[]:this.categories[e].options}},{key:"getTopLevelOptions",value:function(){var t=this,o={};return Object.keys(this.categories).forEach(function(e){t.categories[e].parent||(o[e]=t.categories[e].label)}),o}},{key:"getSelectedCategoryIds",value:function(e){for(var t=[];void 0!==this.categories[e]&&(t.push([e,this.categories[e].parent]),e=this.categories[e].parent),""!==e;);return t.reverse()}}]),a)});
 
assets/js/admin/index.js ADDED
@@ -0,0 +1 @@
 
1
+ // Add new any JS here
assets/js/{facebook-infobanner.js → admin/infobanner.js} RENAMED
File without changes
assets/js/{facebook-metabox.js → admin/metabox.js} RENAMED
File without changes
assets/js/{facebook-for-woocommerce-modal.js → admin/modal.js} RENAMED
File without changes
assets/js/admin/orders.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(a){var e,c=Boolean(wc_facebook_commerce_orders.is_commerce_order),o={restrict_order_statuses:function(e){e.find("option").each(function(e,o){-1===wc_facebook_commerce_orders.allowed_commerce_statuses.indexOf(o.value)&&o.remove()})},toggle_created_date_fields_status:function(e){o.toggle_field(a("#order_data").find("input[name*=order_date]"),e)},disable_order_status_field:function(e){o.toggle_field(e,!1)},toggle_order_customer_field:function(e){a("#order_data").find(".form-field.wc-customer-user").toggleClass("hidden",e)},toggle_billing_and_shipping_fields:function(e){a("#order_data").find("a.edit_address").toggleClass("hidden",e)},disable_pending_order_related_fields:function(e){o.toggle_created_date_fields_status(!1),o.disable_order_status_field(e),o.toggle_order_customer_field(!0),o.toggle_billing_and_shipping_fields(!0)},maybe_disable_refunds:function(){"completed"!==wc_facebook_commerce_orders.order_status&&a(".wc-order-bulk-actions .refund-items").hide()},toggle_field:function(e,o){e.hasClass("wc-enhanced-select")&&(e=e.next("span.select2-container")),o?e.css("pointer-events","all").css("opacity","1.0"):e.css("pointer-events","none").css("opacity","0.4")}},n=a('form[id="post"]'),r=a("#order_status"),t=r.val(),_=wc_facebook_commerce_orders.shipment_tracking,d="",s="",i="",l="";function m(e){unBlockModal(),a(".facebook-for-woocommerce-modal .wc-backbone-modal-content article").html("<p>"+e+"</p>"),a(".facebook-for-woocommerce-modal .wc-backbone-modal-content footer").remove()}function f(e,o,c){var r=0<arguments.length&&void 0!==e&&e,o=1<arguments.length&&void 0!==o?o:null,c=2<arguments.length&&void 0!==c?c:null;o.length?(r&&blockModal(),n.find("button[type=submit].save_order").prop("disabled",!0).append('<span class="spinner is-active"></span>'),a.post(ajaxurl,{action:wc_facebook_commerce_orders.complete_order_action,order_id:a("#post_ID").val(),tracking_number:o,carrier_code:c,nonce:wc_facebook_commerce_orders.complete_order_nonce},function(e){r&&unBlockModal(),e&&e.success?n.data("allow-submit",!0).trigger("submit"):(e=e&&e.data?e.data:wc_facebook_commerce_orders.i18n.unknown_error,alert(e))}).fail(function(){m(wc_facebook_commerce_orders.i18n.unknown_error)}).always(function(){n.find("button[type=submit].save_order").prop("disabled",!1).find("span.spinner").remove()})):alert(wc_facebook_commerce_orders.i18n.missing_tracking_number_error)}function b(){var e=a("#refund_reason"),o=a("#wc_facebook_refund_reason").clone().css("width",e.css("width")),c=e.closest("tr"),e=c.clone();c.find("td.total").css("width","16em").end().find("#refund_reason").replaceWith(o.show()).end().find('label[for="refund_reason"]').attr("for","wc_facebook_refund_reason"),c.after(e),u(c,"wc_facebook_refund_reason",wc_facebook_commerce_orders.i18n.refund_reason_label,wc_facebook_commerce_orders.i18n.refund_reason_tooltip),u(e,"refund_reason",wc_facebook_commerce_orders.i18n.refund_description_label,wc_facebook_commerce_orders.i18n.refund_description_tooltip)}function u(e,o,c,r){e=e.find('label[for="'+o+'"]'),o=e.find(".woocommerce-help-tip").clone();e.text(c),r&&o.length&&(e.prepend(o),o.attr("data-tip",r).tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200}))}Array.isArray(_)&&_[0]&&(d=_[0].tracking_number,s=_[0].carrier_code),c&&(o.restrict_order_statuses(r),"pending"===wc_facebook_commerce_orders.order_status&&o.disable_pending_order_related_fields(r),"cancelled"===wc_facebook_commerce_orders.order_status&&o.disable_order_status_field(r),o.maybe_disable_refunds()),c&&(b(),void 0===window.MutationObserver||(e=document.querySelector("#woocommerce-order-items .inside"))&&new MutationObserver(function(e){e.forEach(function(e){Array.prototype.forEach.call(e.addedNodes,function(e){a(e).is(".wc-order-refund-items")&&b()})})}).observe(e,{childList:!0})),n.on("submit",function(e){return!a("#post").data("skip-cancel-modal")&&"wc-cancelled"!==t&&(c&&"wc-cancelled"===r.val())?(e.preventDefault(),a("#wc-backbone-modal-dialog .modal-close").trigger("click"),new a.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.cancel_modal_message,buttons:wc_facebook_commerce_orders.cancel_modal_buttons}}),a(".facebook-for-woocommerce-modal #btn-ok").off("click.facebook_for_commerce").on("click.facebook_for_commerce",function(e){e.preventDefault(),e.stopPropagation(),blockModal(),a.post(ajaxurl,{action:wc_facebook_commerce_orders.cancel_order_action,order_id:a("#post_ID").val(),reason_code:a('.facebook-for-woocommerce-modal [name="wc_facebook_cancel_reason"]').val(),security:wc_facebook_commerce_orders.cancel_order_nonce},function(e){e&&e.success?a("#post").data("skip-cancel-modal",!0).trigger("submit"):m(e&&e.data?e.data:wc_facebook_commerce_orders.i18n.unknown_error)}).fail(function(){m(wc_facebook_commerce_orders.i18n.unknown_error)})}),!1):void(c&&!n.data("allow-submit")&&("wc-refunded"===(o=r.val())&&t!==o&&(e.preventDefault(),a("#wc-backbone-modal-dialog .modal-close").trigger("click"),new a.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.refund_modal_message,buttons:wc_facebook_commerce_orders.refund_modal_buttons}}),a(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",function(){a("#refund_reason").val(a("#wc_facebook_refund_reason_modal").val()),n.data("allow-submit",!0).submit()})),"wc-completed"===o&&(e.preventDefault(),d||s?f(!1,d,s):(a("#wc-backbone-modal-dialog .modal-close").trigger("click"),(l||i)&&a(document.body).off("wc_backbone_modal_loaded").on("wc_backbone_modal_loaded",function(){l&&a("#wc_facebook_carrier").val(l),i&&a("#wc_facebook_tracking_number").val(i)}),new a.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:wc_facebook_commerce_orders.complete_modal_message,buttons:wc_facebook_commerce_orders.complete_modal_buttons}}),a(".facebook-for-woocommerce-modal #btn-ok").off("click.facebook_for_commerce").on("click.facebook_for_commerce",function(e){e.preventDefault(),e.stopPropagation(),l=a("#wc_facebook_carrier").val(),f(!0,i=a("#wc_facebook_tracking_number").val(),l)})))));var o})});
 
assets/js/admin/product-categories.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(e){var c=e('form[id="edittag"]'),a=e("#wc_facebook_google_product_category_id"),t=a.val();c.on("submit",function(o){c.data("allow-submit")||a.val()===t||(o.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:facebook_for_woocommerce_product_categories.default_google_product_category_modal_message,buttons:facebook_for_woocommerce_product_categories.default_google_product_category_modal_buttons}}),e(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",function(){c.data("allow-submit",!0).find(":submit").trigger("click")}))})});
 
assets/js/admin/{facebook-for-woocommerce-product-sets-admin.js → product-sets-admin.js} RENAMED
File without changes
assets/js/admin/{facebook-for-woocommerce-products-admin.js → products-admin.js} RENAMED
File without changes
assets/js/admin/settings-commerce.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";jQuery(document).ready(function(e){var c=e("form.wc-facebook-settings"),t=e("#wc_facebook_google_product_category_id"),a=t.val();c.on("submit",function(o){c.data("allow-submit")||t.val()===a||(o.preventDefault(),e("#wc-backbone-modal-dialog .modal-close").trigger("click"),new e.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:{message:t.val()?facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_message:facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_message_empty,buttons:facebook_for_woocommerce_settings_commerce.default_google_product_category_modal_buttons}}),e(document.body).off("wc_backbone_modal_response.facebook_for_commerce").on("wc_backbone_modal_response.facebook_for_commerce",function(){c.data("allow-submit",!0).find(":submit").trigger("click")}))}),e(".woocommerce-help-tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200})});
 
assets/js/admin/{facebook-for-woocommerce-settings-sync.js → settings-sync.js} RENAMED
File without changes
assets/js/facebook-for-woocommerce-modal.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";!function(n){window.isModalBlocked=function(){var o=n(".wc-backbone-modal-content");return o.is(".processing")||o.parents(".processing").length},window.blockModal=function(){if(!isModalBlocked())return n(".wc-backbone-modal-content").addClass("processing").block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},window.unBlockModal=function(){n(".wc-backbone-modal-content").removeClass("processing").unblock()},window.closeExistingModal=function(){n("#wc-backbone-modal-dialog .modal-close").trigger("click")}}(jQuery);
 
assets/js/facebook-infobanner.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";function ajax(n){var o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,a=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,t=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,o=Object.assign({},{action:n},o);jQuery.post(ajaxurl,o,function(n){a&&a(n)}).fail(function(n){t&&t(n)})}function fb_woo_infobanner_post_click(){return console.log("Woo infobanner post tip click!"),ajax("ajax_woo_infobanner_post_click",{_ajax_nonce:wc_facebook_infobanner_jsx.nonce})}function fb_woo_infobanner_post_xout(){return console.log("Woo infobanner post tip xout!"),ajax("ajax_woo_infobanner_post_xout",{_ajax_nonce:wc_facebook_infobanner_jsx.nonce})}
 
assets/js/facebook-metabox.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";function ajax(e){var o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,t=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,a=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,o=Object.assign({},{action:e},o);jQuery.post(ajaxurl,o,function(e){t&&t(e)}).fail(function(e){a&&a(e)})}function fb_reset_product(e){if(confirm("Resetting Facebook metadata will not remove this product from your shop. If you have duplicated another product and are trying to publish a new Facebook product, click OK to proceed. Otherwise, Facebook metadata will be restored when this product is updated again.")){var o=document.querySelector("#fb_metadata");return o&&(o.innerHTML="<b>This product is not yet synced to Facebook.</b>"),ajax("ajax_reset_single_fb_product",{wp_id:e,_ajax_nonce:wc_facebook_metabox_jsx.nonce})}}function fb_delete_product(e){if(confirm('Are you sure you want to delete this product on Facebook? If you only want to "hide" the product, change the "Facebook sync" setting to "Sync and hide" and hit "Update". If you delete a product on Facebook and hit "Update" after, this product will be recreated. To permanently remove this product from Facebook, hit "OK" and close the window.This will not delete the product from WooCommerce.')){var o=document.querySelector("#fb_metadata");return o&&(o.innerHTML="<b>This product is not yet synced to Facebook.</b>"),ajax("ajax_delete_fb_product",{wp_id:e,_ajax_nonce:wc_facebook_metabox_jsx.nonce})}}
 
assets/js/facebook-settings.min.js DELETED
@@ -1 +0,0 @@
1
- "use strict";var fb_sync_no_response_count=0,fb_show_advanced_options=!1;function openPopup(){var e,o=screen.height/2-404,n=screen.width/2-576.5;window.originParam=window.location.protocol+"//"+window.location.host,window.facebookAdsToolboxConfig.popupOrigin.includes("staticxx")&&(window.facebookAdsToolboxConfig.popupOrigin="https://www.facebook.com/"),window.facebookAdsToolboxConfig.popupOrigin=prepend_protocol(window.facebookAdsToolboxConfig.popupOrigin),e=window.facebookAdsToolboxConfig.popupOrigin;var t=window.open(e+"/login.php?display=popup&next="+encodeURIComponent(e+"/ads/dia?origin="+window.originParam+" &merchant_settings_id="+window.facebookAdsToolboxConfig.diaSettingId),"DiaWizard",["toolbar=no","location=no","directories=no","status=no","menubar=no","scrollbars=no","resizable=no","copyhistory=no","width=1153","height=808","top="+o,"left="+n].join(","));return function(e,o){t.postMessage({type:e,params:o},window.facebookAdsToolboxConfig.popupOrigin)}}function prepend_protocol(e){return 0===e.indexOf("//www.")&&(e="https:"+e),e}function get_pixel_id_box(){return document.querySelector("#woocommerce_facebookcommerce_facebook_pixel_id")}function get_pixel_use_pii_id_box(){return document.querySelector("#woocommerce_facebookcommerce_enable_advanced_matching")}function get_page_id_box(){return document.querySelector("#woocommerce_facebookcommerce_facebook_page_id")}function ajax(e){var o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,t=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,s=Object.assign({},{action:e},o);jQuery.post(ajaxurl,s,function(e){n&&n(e)}).fail(function(e){t&&t(e)})}var settings={facebook_for_woocommerce:1},pixel_settings={facebook_for_woocommerce:1};function facebookConfig(){window.sendToFacebook=openPopup(),window.diaConfig={clientSetup:window.facebookAdsToolboxConfig}}function fb_flush(){return console.log("Removing all FBIDs from all products!"),ajax("ajax_reset_all_fb_products",{_ajax_nonce:wc_facebook_settings_jsx.nonce},null,function(){console.log("Failed to reset all FB products")})}function sync_confirm(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,o="";switch(e){case"fb_force_resync":o=facebook_for_woocommerce_settings_sync.i18n.confirm_resync;break;case"fb_test_product_sync":o=facebook_for_woocommerce_settings_sync.i18n.confirm_sync_test;break;default:o=facebook_for_woocommerce_settings_sync.i18n.confirm_sync}confirm(o)&&(sync_all_products(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload,"fb_test_product_sync"==e),window.fb_sync_start_time=(new Date).getTime())}function sync_all_products(){var n=0<arguments.length&&void 0!==arguments[0]&&arguments[0],e=1<arguments.length&&void 0!==arguments[1]&&arguments[1];window.fb_connected=!0,sync_in_progress();var o={};o=n?(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload=!0,window.feed_upload=!0,ping_feed_status_queue(),e?{action:"ajax_test_sync_products_using_feed"}:{action:"ajax_sync_all_fb_products_using_feed",_ajax_nonce:wc_facebook_settings_jsx.nonce}):(check_background_processor_status(),{action:"ajax_sync_all_fb_products",_ajax_nonce:wc_facebook_settings_jsx.nonce}),jQuery.post(ajaxurl,o).then(function(e){if(!e&&n||e&&!1===e.success){clearInterval(window.fb_pings),clearInterval(window.fb_feed_pings),jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto");var o=void 0;o=e&&e.data&&e.data.error?e.data.error:facebook_for_woocommerce_settings_sync.i18n.general_error,jQuery("#sync_progress").show().html('<span style="color: #DC3232">'+o+"</span>")}})}function delete_all_settings(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null;return get_pixel_id_box()&&(get_pixel_id_box().value=""),get_pixel_use_pii_id_box()&&(get_pixel_use_pii_id_box().checked=!1),get_page_id_box()&&(get_page_id_box().value=""),jQuery("#woocommerce_facebookcommerce_enable_messenger").prop("checked",!1).trigger("change"),jQuery(".messenger-field").each(function(){void 0!==jQuery(this).data("default")&&jQuery(this).val(jQuery(this).data("default")).trigger("change")}),window.facebookAdsToolboxConfig.pixel.pixelId="",window.facebookAdsToolboxConfig.diaSettingId="",window.fb_connected=!1,not_connected(),console.log("Deleting all settings and removing all FBIDs!"),ajax("ajax_delete_fb_settings",{_ajax_nonce:wc_facebook_settings_jsx.nonce},e,o)}function save_settings(){var o=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,e=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;(e=e||settings)._ajax_nonce=wc_facebook_settings_jsx.nonce,ajax("ajax_save_fb_settings",e,function(e){o&&o(e)},function(e){n&&n(e)})}function save_settings_for_plugin(o,n){save_settings(function(e){e&&!0===e.success?(console.log(e),o(e)):(console.log("Fail response on save_settings_for_plugin"),n(e))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),n(e)})}function save_settings_and_sync(o){"api_key"in settings&&save_settings(function(e){e&&e.includes("settings_saved")?(console.log(e),window.sendToFacebook("ack set pixel",o.params),window.sendToFacebook("ack set page access token",o.params),window.sendToFacebook("ack set merchant settings",o.params)):(window.sendToFacebook("fail save_settings",e),console.log("Fail response on save_settings_and_sync"))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),window.sendToFacebook("fail save_settings_ajax",JSON.stringify(e))})}function sync_in_progress(){jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","none"),jQuery("#sync_progress").show().html(facebook_for_woocommerce_settings_sync.i18n.sync_in_progress)}function sync_not_in_progress(){jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto"),jQuery("#sync_progress").empty().hide()}function not_connected(){jQuery("#fbsetup").show(),jQuery("#integration-settings").hide(),jQuery(".woocommerce-save-button").hide()}function addAnEventListener(e,o,n){"addEventListener"in e?e.addEventListener(o,n,!1):"attachEvent"in e&&e.attachEvent("on"+o,n)}function setMerchantSettings(e){if(!e.params.setting_id)return console.error("Facebook Extension Error: got no setting_id",e.params),void window.sendToFacebook("fail set merchant settings",e.params);settings.external_merchant_settings_id=e.params.setting_id,window.facebookAdsToolboxConfig.diaSettingId=e.params.setting_id}function setCatalog(e){if(!e.params.catalog_id)return console.error("Facebook Extension Error: got no catalog_id",e.params),void window.sendToFacebook("fail set catalog",e.params);settings.product_catalog_id=e.params.catalog_id,window.sendToFacebook("ack set catalog",e.params)}function setPixel(o){if(!o.params.pixel_id)return console.error("Facebook Ads Extension Error: got no pixel_id",o.params),void window.sendToFacebook("fail set pixel",o.params);get_pixel_id_box()&&(get_pixel_id_box().value=o.params.pixel_id),settings.pixel_id=o.params.pixel_id,pixel_settings.pixel_id=settings.pixel_id,void 0!==o.params.pixel_use_pii&&(get_pixel_use_pii_id_box()&&(get_pixel_use_pii_id_box().checked=!!o.params.pixel_use_pii),settings.pixel_use_pii=o.params.pixel_use_pii,pixel_settings.pixel_use_pii=settings.pixel_use_pii),save_settings(function(e){e&&!0===e.success&&window.sendToFacebook("ack set pixel",o.params)},function(e){console.log(e),window.sendToFacebook("fail set pixel",e)},pixel_settings)}function genFeed(t){console.log("generating feed"),jQuery.get(window.facebookAdsToolboxConfig.feedPrepared.feedUrl+"&regenerate=true").done(function(e){window.sendToFacebook("ack feed",t.params)}).fail(function(e,o,n){window.sendToFacebook("fail feed",t.params)})}function setAccessTokenAndPageId(e){if(!e.params.page_token)return console.error("Facebook Ads Extension Error: got no page_token",e.params),void window.sendToFacebook("fail set page access token",e.params);get_page_id_box()&&(get_page_id_box().value=e.params.page_id),settings.api_key=e.params.page_token,settings.page_id=e.params.page_id,window.facebookAdsToolboxConfig.tokenExpired=!1,document.querySelector("#connection-message-invalid")&&(document.querySelector("#connection-message-invalid").style.display="none"),document.querySelector("#connection-message-refresh")&&(document.querySelector("#connection-message-refresh").style.display="block")}function setMsgerChatSetup(e){if(e.hasOwnProperty("is_messenger_chat_plugin_enabled")&&(settings.is_messenger_chat_plugin_enabled=e.is_messenger_chat_plugin_enabled,jQuery("#woocommerce_facebookcommerce_enable_messenger").prop("checked",e.is_messenger_chat_plugin_enabled).trigger("change")),e.hasOwnProperty("facebook_jssdk_version")&&(settings.facebook_jssdk_version=e.facebook_jssdk_version),e.hasOwnProperty("page_id")&&(settings.fb_page_id=e.page_id),e.hasOwnProperty("customization")){var o=e.customization;o.hasOwnProperty("greetingTextCode")&&(settings.msger_chat_customization_greeting_text_code=o.greetingTextCode,jQuery("#woocommerce_facebookcommerce_messenger_greeting").val(o.greetingTextCode).trigger("change")),o.hasOwnProperty("locale")&&(settings.msger_chat_customization_locale=o.locale,jQuery("#woocommerce_facebookcommerce_messenger_locale").val(o.locale).trigger("change")),o.hasOwnProperty("themeColorCode")&&(settings.msger_chat_customization_theme_color_code=o.themeColorCode,jQuery("#woocommerce_facebookcommerce_messenger_color_hex").val(o.themeColorCode).trigger("change"))}}function setFeedMigrated(e){if(!e.params.hasOwnProperty("feed_migrated"))return console.error("Facebook Extension Error: feed migrated not received",e.params),void window.sendToFacebook("fail set feed migrated",e.params);settings.feed_migrated=e.params.feed_migrated,window.facebookAdsToolboxConfig.feedPrepared.feedMigrated=e.params.feed_migrated,jQuery("#woocommerce-facebook-settings-sync-products").hide(),jQuery(".notice.wc-facebook-migrate-notice").hide(),save_settings_for_plugin(function(e){window.sendToFacebook("ack set feed migrated",event.data)},function(e){window.sendToFacebook("fail set feed migrated",event.data)})}function iFrameListener(o){var e=o.origin||o.originalEvent.origin;if(e!==window.facebookAdsToolboxConfig.popupOrigin){if(!urlFromSameDomain(e,window.facebookAdsToolboxConfig.popupOrigin))return;window.facebookAdsToolboxConfig.popupOrigin=e}switch(o.data.type){case"reset":delete_all_settings(function(e){e&&o.data.params?"Settings Deleted"===e?window.sendToFacebook("ack reset",o.data.params):(console.log(e),alert(e)):console.log("Got no response from delete_all_settings")},function(e){console.error(e)});break;case"get dia settings":window.sendToFacebook("dia settings",window.diaConfig);break;case"set catalog":setCatalog(o.data);break;case"set feed migrated":setFeedMigrated(o.data);break;case"set pixel":setPixel(o.data);break;case"gen feed":genFeed(o.data);break;case"set page access token":window.sendToFacebook("ack set page access token",o.data.params);break;case"set page":setPage(o.data);break;case"set merchant settings":setMerchantSettings(o.data),save_settings(function(e){console.log(e),e&&!0===e.success?(settings.pixel_id&&window.sendToFacebook("ack set pixel",o.data.params),settings.page_id&&window.sendToFacebook("ack set page",o.data.params),settings.external_merchant_settings_id&&(window.sendToFacebook("ack set merchant settings",o.data.params),jQuery("#fbsetup").hide(),jQuery("#integration-settings").show(),jQuery(".woocommerce-save-button").show())):(window.sendToFacebook("fail save_settings",e),console.log("Fail response on save_settings"))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),window.sendToFacebook("fail save_settings_ajax",JSON.stringify(e))});break;case"set msger chat":setMsgerChatSetup(o.data.params),save_settings_for_plugin(function(e){window.sendToFacebook("ack msger chat",o.data)},function(e){window.sendToFacebook("fail msger chat",o.data)})}}function setPage(e){if(!e.params.hasOwnProperty("page_id"))return console.error("Facebook Extension Error: page ID not received",e.params),void window.sendToFacebook("fail set page",e.params);settings.page_id=e.params.page_id,jQuery("#woocommerce_facebookcommerce_facebook_page_id").val(settings.page_id)}function urlFromSameDomain(e,o){if(!e.startsWith("http")||!o.startsWith("http"))return!1;var n=parseURL(e),t=parseURL(o),s=n.host.replace(/^\w+\./,"www."),i=t.host.replace(/^\w+\./,"www.");return n.protocol===t.protocol&&s===i}function parseURL(e){var o=document.createElement("a");return o.href=e,o}function check_background_processor_status(){window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload||(clearInterval(window.fb_pings),window.fb_pings=setInterval(function(){console.log("Pinging queue..."),check_queues()},1e4))}function ping_feed_status_queue(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:0;clearInterval(window.fb_feed_pings),window.fb_feed_pings=setInterval(function(){console.log("Pinging feed uploading queue..."),check_feed_upload_queue(e)},3e4*(1<<e))}function product_sync_complete(e){sync_not_in_progress(),e.empty().hide(),clearInterval(window.fb_pings)}function check_queues(){ajax("ajax_fb_background_check_queue",{request_time:(new Date).getTime(),_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){if(window.feed_upload)clearInterval(window.fb_pings);else{var o=jQuery("#sync_progress"),n=parse_response_check_connection(e);if(n){if(fb_sync_no_response_count=0,n){n.background||(console.log("No background sync found, disabling pings"),clearInterval(window.fb_pings));var t=!!n.processing,s=n.remaining;if(t){var i="";i=1===s?facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_singular:facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_plural,o.show().html(i.replace("{count}",s)),0===s&&product_sync_complete(o)}else{if(window.fb_sync_start_time&&n.request_time){var a=new Date(parseInt(n.request_time));if(window.fb_sync_start_time>a)return void console.log("OLD PING")}0===s&&product_sync_complete(o)}}}else 5<fb_sync_no_response_count++&&clearInterval(window.fb_pings)}})}function parse_response_check_connection(e){if(e){console.log(e);var o=e.substring(e.indexOf("{"));return(o=JSON.parse(o)).connected||window.fb_connected?o:(not_connected(),null)}return null}function check_feed_upload_queue(t){ajax("ajax_check_feed_upload_status",{_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){var o=jQuery("#sync_progress"),n=parse_response_check_connection(e);if(clearInterval(window.fb_feed_pings),n)switch(n.status){case"complete":window.feed_upload=!1,window.is_test?display_test_result():product_sync_complete(o);break;case"in progress":o.show().html(facebook_for_woocommerce_settings_sync.i18n.sync_in_progress),ping_feed_status_queue(t+1);break;default:jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto"),jQuery("#sync_progress").show().html('<span style="color: #DC3232">'+facebook_for_woocommerce_settings_sync.i18n.feed_upload_error+"</span>"),window.feed_upload=!1,window.is_test&&display_test_result()}})}function display_test_result(){ajax("ajax_display_test_result",{_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){var o=jQuery("#sync_progress"),n=document.querySelector("#sync_complete"),t=parse_response_check_connection(e);if(t)switch(t.pass){case"true":sync_not_in_progress(),n&&(n.style.display="",n.innerHTML=facebook_for_woocommerce_settings_sync.i18n.integration_test_sucessful),o.empty().hide(),window.is_test=!1;break;case"in progress":o.show().html(facebook_for_woocommerce_settings_sync.i18n.integration_test_in_progress),ping_feed_status_queue();break;default:window.debug_info=t.debug_info+"<br/>"+t.stack_trace,n&&(n.style.display="",n.innerHTML=facebook_for_woocommerce_settings_sync.i18n.integration_test_failed),o.empty().hide(),document.querySelector("#debug_info")&&(document.querySelector("#debug_info").style.display=""),window.is_test=!1}})}function show_debug_info(){var e=document.querySelector("#stack_trace");e&&(e.innerHTML=window.debug_info),document.querySelector("#debug_info")&&(document.querySelector("#debug_info").style.display="none"),window.debug_info=""}function fbe_init_nux_messages(){var r=window.jQuery;r(function(){r.each(r(".nux-message"),function(e,o){var n=r(o),t=n.data("target"),s=r("#"+t),i=s.position(),a=s.height()/2,c=s.outerWidth();n.css({top:Math.ceil(i.top+a)+"px",left:Math.ceil(i.left+c)+"px",display:"block"}),r(".nux-message-close-btn",n).click(function(){r(o).hide()})})})}function saveAutoSyncSchedule(){var e=document.getElementsByClassName("autosyncCheck")[0].checked,o=document.getElementsByClassName("autosyncTime")[0],n=(document.getElementsByClassName("autosyncSaveButton")[0],document.getElementsByClassName("autosyncSavedNotice")[0]);e?(o.removeAttribute("disabled"),n.style.transition="",n.style.opacity=1,setTimeout(function(){n.style.opacity=0,n.style.transition="opacity 5s"},3e3)):o.setAttribute("disabled",!0),ajax("ajax_schedule_force_resync",{enabled:e?1:0,time:o.value,_ajax_nonce:wc_facebook_settings_jsx.nonce})}function syncShortDescription(){var e=document.getElementsByClassName("syncShortDescription")[0].checked;ajax("ajax_update_fb_option",{option:"fb_sync_short_description",option_value:e?1:0,_ajax_nonce:wc_facebook_settings_jsx.nonce},null,function(){document.getElementsByClassName("syncShortDescription")[0].checked=!e,console.log("Failed to sync Short Description")})}addAnEventListener(window,"message",iFrameListener),jQuery(document).ready(function(e){e(".notice .wc-facebook-manage-connection").click(function(e){e.preventDefault(),facebookConfig()}),e("#woocommerce-facebook-settings-sync-products").click(function(e){e.preventDefault(),sync_confirm()})});
 
changelog.txt CHANGED
@@ -1,5 +1,19 @@
1
  *** Facebook for WooCommerce Changelog ***
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  2021.04.29 - version 2.4.1
4
  * Fix - PHP<7.1 incompatible code for Google Taxonomy Setting in products.
5
 
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
+ 2021-05-19 - version 2.5.0
4
+ * New - Option to allow larger sites to opt-out of feed generation (product sync) job #1932
5
+ * New - Log connection errors to allow easier troubleshooting #1857
6
+ * Fix - Reduce default feed generation (product sync) interval to once per day to reduce overhead #1942
7
+ * Fix - Trigger feed (product sync) job from to `admin_init` to reduce impact on front-end requests #1939
8
+ * Fix - Ensure variable product attribute values containing comma (`,`) sync correctly #1922
9
+ * Fix - Use existing / current tab for connection `Get Started` button #1885
10
+ * Dev - Require PHP version 7.0 or newer #1908
11
+ * Dev - Adopt Composer autoloader to avoid manually `require`ing PHP class files #1919
12
+ * Dev - Adopt WooRelease release tool for deploying releases #1947
13
+ * Dev - Use wp-scripts to build assets #1826
14
+ * Dev - Add `phpcs` tooling to help standardise PHP code style #1911
15
+ * Dev - Add JobRegistry engine for managing periodic background batch jobs #1913
16
+
17
  2021.04.29 - version 2.4.1
18
  * Fix - PHP<7.1 incompatible code for Google Taxonomy Setting in products.
19
 
class-wc-facebookcommerce.php CHANGED
@@ -22,7 +22,7 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
22
 
23
 
24
  /** @var string the plugin version */
25
- const VERSION = '2.4.1';
26
 
27
  /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
28
  const PLUGIN_VERSION = self::VERSION;
@@ -91,6 +91,9 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
91
  /** @var \SkyVerge\WooCommerce\Facebook\Tracker */
92
  private $tracker;
93
 
 
 
 
94
  /**
95
  * Constructs the plugin.
96
  *
@@ -101,9 +104,9 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
101
  parent::__construct(
102
  self::PLUGIN_ID,
103
  self::VERSION,
104
- [
105
  'text_domain' => 'facebook-for-woocommerce',
106
- ]
107
  );
108
 
109
  $this->init();
@@ -117,46 +120,27 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
117
  */
118
  public function init() {
119
 
120
- add_action( 'init', [ $this, 'get_integration' ] );
121
- add_action( 'init', [ $this, 'register_custom_taxonomy' ] );
122
- add_action( 'add_meta_boxes_product' , [ $this, 'remove_product_fb_product_set_metabox' ], 50 );
123
- add_filter( 'fb_product_set_row_actions', [ $this, 'product_set_links' ] );
124
- add_filter( 'manage_edit-fb_product_set_columns', [ $this, 'manage_fb_product_set_columns' ] );
125
 
126
  // Product Set breadcrumb filters
127
- add_filter( 'woocommerce_navigation_is_connected_page', [ $this, 'is_current_page_conected_filter' ], 99, 2 );
128
- add_filter( 'woocommerce_navigation_get_breadcrumbs', [ $this, 'wc_page_breadcrumbs_filter' ], 99 );
129
 
130
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
 
131
 
132
  include_once 'facebook-commerce.php';
133
 
134
  require_once $this->get_framework_path() . '/utilities/class-sv-wp-async-request.php';
135
  require_once $this->get_framework_path() . '/utilities/class-sv-wp-background-job-handler.php';
136
 
137
- require_once __DIR__ . '/includes/Locale.php';
138
- require_once __DIR__ . '/includes/AJAX.php';
139
- require_once __DIR__ . '/includes/Handlers/Connection.php';
140
- require_once __DIR__ . '/includes/Handlers/WebHook.php';
141
- require_once __DIR__ . '/includes/Integrations/Integrations.php';
142
- require_once __DIR__ . '/includes/Product_Categories.php';
143
- require_once __DIR__ . '/includes/Products.php';
144
- require_once __DIR__ . '/includes/Products/Feed.php';
145
- require_once __DIR__ . '/includes/Products/FBCategories.php';
146
- require_once __DIR__ . '/includes/Products/Stock.php';
147
- require_once __DIR__ . '/includes/Products/Sync.php';
148
- require_once __DIR__ . '/includes/Products/Sync/Background.php';
149
- require_once __DIR__ . '/includes/ProductSets/Sync.php';
150
  require_once __DIR__ . '/includes/fbproductfeed.php';
151
  require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
152
- require_once __DIR__ . '/includes/Commerce.php';
153
- require_once __DIR__ . '/includes/Events/Event.php';
154
- require_once __DIR__ . '/includes/Events/Normalizer.php';
155
- require_once __DIR__ . '/includes/Events/AAMSettings.php';
156
- require_once __DIR__ . '/includes/Utilities/Shipment.php';
157
- require_once __DIR__ . '/includes/Utilities/Tracker.php';
158
- require_once __DIR__ . '/includes/Debug/ProfilingLogger.php';
159
- require_once __DIR__ . '/includes/Debug/ProfilingLoggerProcess.php';
160
 
161
  $this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
162
  $this->products_stock_handler = new \SkyVerge\WooCommerce\Facebook\Products\Stock();
@@ -188,10 +172,14 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
188
  }
189
 
190
  $this->connection_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\Connection( $this );
191
- $this->webhook_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\WebHook( $this );
192
 
193
  $this->tracker = new \SkyVerge\WooCommerce\Facebook\Utilities\Tracker();
194
 
 
 
 
 
195
  // load admin handlers, before admin_init
196
  if ( is_admin() ) {
197
 
@@ -235,12 +223,12 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
235
  */
236
  protected function get_deprecated_hooks() {
237
 
238
- return [
239
- 'wc_facebook_page_access_token' => [
240
  'version' => '2.1.0',
241
  'replacement' => false,
242
- ],
243
- ];
244
  }
245
 
246
 
@@ -262,14 +250,20 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
262
  $message = sprintf(
263
  /* 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 */
264
  __( '%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' ),
265
- '<strong>', '</strong>',
266
- '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">', '</a>'
 
 
267
  );
268
 
269
- $this->get_admin_notice_handler()->add_admin_notice( $message, self::PLUGIN_ID . '_migrate_to_v2_0', [
270
- 'dismissible' => false,
271
- 'notice_class' => 'notice-info',
272
- ] );
 
 
 
 
273
 
274
  // direct these users to the new plugin settings page
275
  if ( ! $this->is_plugin_settings() ) {
@@ -277,16 +271,21 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
277
  $message = sprintf(
278
  /* translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link HTML tag */
279
  __( 'For your convenience, the Facebook for WooCommerce settings are now located under %1$sWooCommerce > Facebook%2$s.', 'facebook-for-woocommerce' ),
280
- '<a href="' . esc_url( facebook_for_woocommerce()->get_settings_url() ) . '">', '</a>'
 
281
  );
282
 
283
- $this->get_admin_notice_handler()->add_admin_notice( $message, self::PLUGIN_ID . '_relocated_settings', [
284
- 'dismissible' => true,
285
- 'notice_class' => 'notice-info',
286
- ] );
 
 
 
 
287
  }
288
 
289
- // otherwise, a general getting started message
290
  } elseif ( ! $this->is_plugin_settings() ) {
291
 
292
  $message = sprintf(
@@ -301,33 +300,43 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
301
  '</a>'
302
  );
303
 
304
- $this->get_admin_notice_handler()->add_admin_notice( $message, self::PLUGIN_ID . '_get_started', [
305
- 'dismissible' => true,
306
- 'notice_class' => 'notice-info',
307
- ] );
 
 
 
 
308
  }
309
 
310
- // notices for those connected to FBE 2
311
  } else {
312
 
313
  // if upgraders had messenger enabled and one of the removed settings was customized, alert them to reconfigure
314
  if (
315
  $this->get_integration()->get_external_merchant_settings_id()
316
  && $this->get_integration()->is_messenger_enabled()
317
- && ( '#0084ff' !== $this->get_integration()->get_messenger_color_hex() || ! in_array( $this->get_integration()->get_messenger_greeting(), [ 'Hi! How can we help you?', "Hi! We're here to answer any questions you may have.", '' ], true ) )
318
  ) {
319
 
320
  $message = sprintf(
321
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
322
  __( '%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' ),
323
- '<strong>', '</strong>',
324
- '<a href="' . esc_url( $this->get_connection_handler()->get_manage_url() ) . '" target="_blank">', '</a>'
 
 
325
  );
326
 
327
- $this->get_admin_notice_handler()->add_admin_notice( $message, 'update_messenger', [
328
- 'always_show_on_settings' => false,
329
- 'notice_class' => 'notice-info',
330
- ] );
 
 
 
 
331
  }
332
  }
333
 
@@ -337,19 +346,25 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
337
  $message = sprintf(
338
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
339
  __( '%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' ),
340
- '<strong>', '</strong>',
341
- '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">', '</a>'
 
 
342
  );
343
 
344
- $this->get_admin_notice_handler()->add_admin_notice( $message, 'connection_invalid', [
345
- 'notice_class' => 'notice-error',
346
- ] );
 
 
 
 
347
  }
348
 
349
  if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
350
 
351
  $is_marketing_enabled = is_callable( 'Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
352
- && Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
353
 
354
  if ( $is_marketing_enabled ) {
355
 
@@ -357,14 +372,15 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
357
  sprintf(
358
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
359
  esc_html__( 'Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu.', 'facebook-for-woocommerce' ),
360
- '<a href="' . esc_url( $this->get_settings_url() ) . '">','</a>'
 
361
  ),
362
  'settings_moved_to_marketing',
363
- [
364
  'dismissible' => true,
365
  'always_show_on_settings' => false,
366
  'notice_class' => 'notice-info',
367
- ]
368
  );
369
  }
370
  }
@@ -383,8 +399,9 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
383
  * @param string $log_id optional log id to segment the files by, defaults to plugin id
384
  */
385
  public function log( $message, $log_id = null ) {
386
- // bail if logging isn't enabled
387
- if ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) {
 
388
  return;
389
  }
390
 
@@ -398,7 +415,7 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
398
  *
399
  * @param array $request request data
400
  * @param array $response response data
401
- * @param null $log_id log ID
402
  */
403
  public function log_api_request( $request, $response, $log_id = null ) {
404
 
@@ -901,7 +918,7 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
901
  static $instance = null;
902
  if ( null === $instance ) {
903
  $is_enabled = defined( 'FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED' ) && FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED;
904
- $instance = new \SkyVerge\WooCommerce\Facebook\Debug\ProfilingLogger( $is_enabled );
905
  }
906
 
907
  return $instance;
@@ -985,6 +1002,17 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
985
  return __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' );
986
  }
987
 
 
 
 
 
 
 
 
 
 
 
 
988
 
989
  /** Conditional methods ***************************************************************************************/
990
 
22
 
23
 
24
  /** @var string the plugin version */
25
+ const VERSION = WC_Facebook_Loader::PLUGIN_VERSION;
26
 
27
  /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
28
  const PLUGIN_VERSION = self::VERSION;
91
  /** @var \SkyVerge\WooCommerce\Facebook\Tracker */
92
  private $tracker;
93
 
94
+ /** @var \SkyVerge\WooCommerce\Facebook\Jobs\JobRegistry */
95
+ public $job_registry;
96
+
97
  /**
98
  * Constructs the plugin.
99
  *
104
  parent::__construct(
105
  self::PLUGIN_ID,
106
  self::VERSION,
107
+ array(
108
  'text_domain' => 'facebook-for-woocommerce',
109
+ )
110
  );
111
 
112
  $this->init();
120
  */
121
  public function init() {
122
 
123
+ add_action( 'init', array( $this, 'get_integration' ) );
124
+ add_action( 'init', array( $this, 'register_custom_taxonomy' ) );
125
+ add_action( 'add_meta_boxes_product', array( $this, 'remove_product_fb_product_set_metabox' ), 50 );
126
+ add_filter( 'fb_product_set_row_actions', array( $this, 'product_set_links' ) );
127
+ add_filter( 'manage_edit-fb_product_set_columns', array( $this, 'manage_fb_product_set_columns' ) );
128
 
129
  // Product Set breadcrumb filters
130
+ add_filter( 'woocommerce_navigation_is_connected_page', array( $this, 'is_current_page_conected_filter' ), 99, 2 );
131
+ add_filter( 'woocommerce_navigation_get_breadcrumbs', array( $this, 'wc_page_breadcrumbs_filter' ), 99 );
132
 
133
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
134
+ require_once __DIR__ . '/vendor/autoload.php';
135
 
136
  include_once 'facebook-commerce.php';
137
 
138
  require_once $this->get_framework_path() . '/utilities/class-sv-wp-async-request.php';
139
  require_once $this->get_framework_path() . '/utilities/class-sv-wp-background-job-handler.php';
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  require_once __DIR__ . '/includes/fbproductfeed.php';
142
  require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
143
+ require_once __DIR__ . '/includes/Exceptions/ConnectWCAPIException.php';
 
 
 
 
 
 
 
144
 
145
  $this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
146
  $this->products_stock_handler = new \SkyVerge\WooCommerce\Facebook\Products\Stock();
172
  }
173
 
174
  $this->connection_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\Connection( $this );
175
+ $this->webhook_handler = new \SkyVerge\WooCommerce\Facebook\Handlers\WebHook( $this );
176
 
177
  $this->tracker = new \SkyVerge\WooCommerce\Facebook\Utilities\Tracker();
178
 
179
+ // Init jobs
180
+ $this->job_registry = new \SkyVerge\WooCommerce\Facebook\Jobs\JobRegistry();
181
+ add_action( 'init', [ $this->job_registry, 'init' ] );
182
+
183
  // load admin handlers, before admin_init
184
  if ( is_admin() ) {
185
 
223
  */
224
  protected function get_deprecated_hooks() {
225
 
226
+ return array(
227
+ 'wc_facebook_page_access_token' => array(
228
  'version' => '2.1.0',
229
  'replacement' => false,
230
+ ),
231
+ );
232
  }
233
 
234
 
250
  $message = sprintf(
251
  /* 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 */
252
  __( '%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' ),
253
+ '<strong>',
254
+ '</strong>',
255
+ '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">',
256
+ '</a>'
257
  );
258
 
259
+ $this->get_admin_notice_handler()->add_admin_notice(
260
+ $message,
261
+ self::PLUGIN_ID . '_migrate_to_v2_0',
262
+ array(
263
+ 'dismissible' => false,
264
+ 'notice_class' => 'notice-info',
265
+ )
266
+ );
267
 
268
  // direct these users to the new plugin settings page
269
  if ( ! $this->is_plugin_settings() ) {
271
  $message = sprintf(
272
  /* translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link HTML tag */
273
  __( 'For your convenience, the Facebook for WooCommerce settings are now located under %1$sWooCommerce > Facebook%2$s.', 'facebook-for-woocommerce' ),
274
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_settings_url() ) . '">',
275
+ '</a>'
276
  );
277
 
278
+ $this->get_admin_notice_handler()->add_admin_notice(
279
+ $message,
280
+ self::PLUGIN_ID . '_relocated_settings',
281
+ array(
282
+ 'dismissible' => true,
283
+ 'notice_class' => 'notice-info',
284
+ )
285
+ );
286
  }
287
 
288
+ // otherwise, a general getting started message
289
  } elseif ( ! $this->is_plugin_settings() ) {
290
 
291
  $message = sprintf(
300
  '</a>'
301
  );
302
 
303
+ $this->get_admin_notice_handler()->add_admin_notice(
304
+ $message,
305
+ self::PLUGIN_ID . '_get_started',
306
+ array(
307
+ 'dismissible' => true,
308
+ 'notice_class' => 'notice-info',
309
+ )
310
+ );
311
  }
312
 
313
+ // notices for those connected to FBE 2
314
  } else {
315
 
316
  // if upgraders had messenger enabled and one of the removed settings was customized, alert them to reconfigure
317
  if (
318
  $this->get_integration()->get_external_merchant_settings_id()
319
  && $this->get_integration()->is_messenger_enabled()
320
+ && ( '#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 ) )
321
  ) {
322
 
323
  $message = sprintf(
324
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
325
  __( '%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' ),
326
+ '<strong>',
327
+ '</strong>',
328
+ '<a href="' . esc_url( $this->get_connection_handler()->get_manage_url() ) . '" target="_blank">',
329
+ '</a>'
330
  );
331
 
332
+ $this->get_admin_notice_handler()->add_admin_notice(
333
+ $message,
334
+ 'update_messenger',
335
+ array(
336
+ 'always_show_on_settings' => false,
337
+ 'notice_class' => 'notice-info',
338
+ )
339
+ );
340
  }
341
  }
342
 
346
  $message = sprintf(
347
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
348
  __( '%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' ),
349
+ '<strong>',
350
+ '</strong>',
351
+ '<a href="' . esc_url( $this->get_connection_handler()->get_connect_url() ) . '">',
352
+ '</a>'
353
  );
354
 
355
+ $this->get_admin_notice_handler()->add_admin_notice(
356
+ $message,
357
+ 'connection_invalid',
358
+ array(
359
+ 'notice_class' => 'notice-error',
360
+ )
361
+ );
362
  }
363
 
364
  if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
365
 
366
  $is_marketing_enabled = is_callable( 'Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
367
+ && Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
368
 
369
  if ( $is_marketing_enabled ) {
370
 
372
  sprintf(
373
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
374
  esc_html__( 'Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu.', 'facebook-for-woocommerce' ),
375
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
376
+ '</a>'
377
  ),
378
  'settings_moved_to_marketing',
379
+ array(
380
  'dismissible' => true,
381
  'always_show_on_settings' => false,
382
  'notice_class' => 'notice-info',
383
+ )
384
  );
385
  }
386
  }
399
  * @param string $log_id optional log id to segment the files by, defaults to plugin id
400
  */
401
  public function log( $message, $log_id = null ) {
402
+ // Bail if site is connected and user has disabled logging.
403
+ // If site is disconnected, force-enable logging so merchant can diagnose connection issues.
404
+ if ( ( ! $this->get_integration() || ! $this->get_integration()->is_debug_mode_enabled() ) && $this->get_connection_handler()->is_connected() ) {
405
  return;
406
  }
407
 
415
  *
416
  * @param array $request request data
417
  * @param array $response response data
418
+ * @param null $log_id log ID
419
  */
420
  public function log_api_request( $request, $response, $log_id = null ) {
421
 
918
  static $instance = null;
919
  if ( null === $instance ) {
920
  $is_enabled = defined( 'FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED' ) && FACEBOOK_FOR_WOOCOMMERCE_PROFILING_LOG_ENABLED;
921
+ $instance = new \SkyVerge\WooCommerce\Facebook\Debug\ProfilingLogger( $is_enabled );
922
  }
923
 
924
  return $instance;
1002
  return __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' );
1003
  }
1004
 
1005
+ /**
1006
+ * Gets the url for the assets build directory.
1007
+ *
1008
+ * @since 2.3.4
1009
+ *
1010
+ * @return string
1011
+ */
1012
+ public function get_asset_build_dir_url() {
1013
+ return $this->get_plugin_url() . '/assets/build';
1014
+ }
1015
+
1016
 
1017
  /** Conditional methods ***************************************************************************************/
1018
 
facebook-commerce-events-tracker.php CHANGED
@@ -63,7 +63,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
63
 
64
  $this->pixel = new \WC_Facebookcommerce_Pixel( $user_info );
65
  $this->aam_settings = $aam_settings;
66
- $this->tracked_events = [];
67
 
68
  $this->add_hooks();
69
  }
@@ -100,37 +100,37 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
100
  private function add_hooks() {
101
 
102
  // inject Pixel
103
- add_action( 'wp_head', [ $this, 'inject_base_pixel' ] );
104
- add_action( 'wp_footer', [ $this, 'inject_base_pixel_noscript' ] );
105
 
106
  // ViewContent for individual products
107
- add_action( 'woocommerce_after_single_product', [ $this, 'inject_view_content_event' ] );
108
- add_action( 'woocommerce_after_single_product', [ $this, 'maybe_inject_search_event' ] );
109
 
110
- add_action( 'woocommerce_after_shop_loop', [ $this, 'inject_view_category_event' ] );
111
 
112
- add_action( 'pre_get_posts', [ $this, 'inject_search_event' ] );
113
- add_filter( 'woocommerce_redirect_single_search_result', [ $this, 'maybe_add_product_search_event_to_session' ] );
114
 
115
  // AddToCart events
116
- add_action( 'woocommerce_add_to_cart', [ $this, 'inject_add_to_cart_event' ], 40, 4 );
117
  // AddToCart while AJAX is enabled
118
- add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'add_filter_for_add_to_cart_fragments' ] );
119
  // AddToCart while using redirect to cart page
120
  if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
121
- add_filter( 'woocommerce_add_to_cart_redirect', [ $this, 'set_last_product_added_to_cart_upon_redirect' ], 10, 2 );
122
- add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'set_last_product_added_to_cart_upon_ajax_redirect' ] );
123
- add_action( 'woocommerce_after_cart', [ $this, 'inject_add_to_cart_redirect_event' ], 10, 2 );
124
  }
125
 
126
  // InitiateCheckout events
127
- add_action( 'woocommerce_after_checkout_form', [ $this, 'inject_initiate_checkout_event' ] );
128
  // Purchase and Subscribe events
129
- add_action( 'woocommerce_checkout_update_order_meta', [ $this, 'inject_purchase_event' ] );
130
- add_action( 'woocommerce_thankyou', [ $this, 'inject_purchase_event' ], 40 );
131
 
132
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
133
- add_action( 'wpcf7_contact_form', [ $this, 'inject_lead_event_hook' ], 11 );
134
  }
135
 
136
 
@@ -195,8 +195,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
195
  // if any product is a variant, fire the pixel with
196
  // content_type: product_group
197
  $content_type = 'product';
198
- $product_ids = [];
199
- $contents = [];
200
 
201
  foreach ( $products as $product ) {
202
 
@@ -204,10 +204,10 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
204
  continue;
205
  }
206
 
207
- $contents[] = [
208
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
209
  'quantity' => 1, // consider category results a quantity of 1
210
- ];
211
 
212
  $product_ids = array_merge(
213
  $product_ids,
@@ -221,17 +221,17 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
221
  $categories = WC_Facebookcommerce_Utils::get_product_categories( get_the_ID() );
222
 
223
  $event_name = 'ViewCategory';
224
- $event_data = [
225
- 'event_name' => $event_name,
226
- 'custom_data' => [
227
  'content_name' => $categories['name'],
228
  'content_category' => $categories['categories'],
229
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
230
  'content_type' => $content_type,
231
  'contents' => $contents,
232
- ],
233
- 'user_data' => $this->pixel->get_user_info()
234
- ];
235
 
236
  $event = new Event( $event_data );
237
 
@@ -296,7 +296,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
296
  */
297
  private function add_product_search_event_to_session( Event $event ) {
298
 
299
- if ( isset( WC()->session ) && is_callable( [ WC()->session, 'has_session' ] ) && WC()->session->has_session() ) {
300
  WC()->session->set( $this->search_event_data_session_variable, $event->get_data() );
301
  }
302
  }
@@ -317,7 +317,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
317
 
318
  $this->search_event = $this->get_product_search_event_from_session();
319
 
320
- if ( ! $this->search_event ) {
321
  return;
322
  }
323
 
@@ -335,7 +335,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
335
  */
336
  private function get_product_search_event_from_session() {
337
 
338
- if ( ! isset( WC()->session ) || ! is_callable( [ WC()->session, 'get' ] ) ) {
339
  return null;
340
  }
341
 
@@ -382,8 +382,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
382
  }
383
 
384
  // needs to run before wc_template_redirect, normally hooked with priority 10
385
- add_action( 'template_redirect', [ $this, 'send_search_event' ], 5 );
386
- add_action( 'woocommerce_before_shop_loop', [ $this, 'actually_inject_search_event' ] );
387
  }
388
  }
389
 
@@ -417,8 +417,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
417
 
418
  // if any product is a variant, fire the pixel with content_type: product_group
419
  $content_type = 'product';
420
- $product_ids = [];
421
- $contents = [];
422
  $total_value = 0.00;
423
 
424
  foreach ( $wp_query->posts as $post ) {
@@ -431,10 +431,10 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
431
 
432
  $product_ids = array_merge( $product_ids, WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) );
433
 
434
- $contents[] = [
435
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
436
  'quantity' => 1, // consider the search results a quantity of 1
437
- ];
438
 
439
  $total_value += (float) $product->get_price();
440
 
@@ -443,18 +443,18 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
443
  }
444
  }
445
 
446
- $event_data = [
447
  'event_name' => 'Search',
448
- 'custom_data' => [
449
  'content_type' => $content_type,
450
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
451
  'contents' => $contents,
452
  'search_string' => get_search_query(),
453
  'value' => Framework\SV_WC_Helper::number_format( $total_value ),
454
  'currency' => get_woocommerce_currency(),
455
- ],
456
- 'user_data' => $this->pixel->get_user_info()
457
- ];
458
 
459
  $this->search_event = new Event( $event_data );
460
  }
@@ -472,11 +472,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
472
 
473
  $event = $this->get_search_event();
474
 
475
- $this->pixel->inject_event( $event->get_name(), [
476
- 'event_id' => $event->get_id(),
477
- 'event_name' => $event->get_name(),
478
- 'custom_data' => $event->get_custom_data(),
479
- ] );
 
 
 
480
  }
481
 
482
 
@@ -499,7 +502,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
499
  }
500
 
501
  // if product is variable or grouped, fire the pixel with content_type: product_group
502
- if ( $product->is_type( [ 'variable', 'grouped' ] ) ) {
503
  $content_type = 'product_group';
504
  } else {
505
  $content_type = 'product';
@@ -507,26 +510,26 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
507
 
508
  $categories = \WC_Facebookcommerce_Utils::get_product_categories( $product->get_id() );
509
 
510
- $event_data = [
511
  'event_name' => 'ViewContent',
512
- 'custom_data' => [
513
  'content_name' => $product->get_title(),
514
  'content_ids' => wp_json_encode( \WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) ),
515
  'content_type' => $content_type,
516
  'contents' => wp_json_encode(
517
- [
518
- [
519
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
520
  'quantity' => 1,
521
- ]
522
- ]
523
  ),
524
  'content_category' => $categories['name'],
525
  'value' => $product->get_price(),
526
  'currency' => get_woocommerce_currency(),
527
- ],
528
- 'user_data' => $this->pixel->get_user_info(),
529
- ];
530
 
531
  $event = new Event( $event_data );
532
 
@@ -544,9 +547,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
544
  * @internal
545
  *
546
  * @param string $cart_item_key the cart item key
547
- * @param int $product_id the product identifier
548
- * @param int $quantity the added product quantity
549
- * @param int $variation_id the product variation identifier
550
  */
551
  public function inject_add_to_cart_event( $cart_item_key, $product_id, $quantity, $variation_id ) {
552
 
@@ -562,18 +565,18 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
562
  return;
563
  }
564
 
565
- $event_data = [
566
  'event_name' => 'AddToCart',
567
- 'custom_data' => [
568
  'content_ids' => $this->get_cart_content_ids(),
569
  'content_name' => $this->get_cart_content_names(),
570
  'content_type' => 'product',
571
  'contents' => $this->get_cart_contents(),
572
  'value' => $this->get_cart_total(),
573
  'currency' => get_woocommerce_currency(),
574
- ],
575
- 'user_data' => $this->pixel->get_user_info(),
576
- ];
577
 
578
  $event = new SkyVerge\WooCommerce\Facebook\Events\Event( $event_data );
579
 
@@ -601,7 +604,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
601
  public function add_filter_for_add_to_cart_fragments() {
602
 
603
  if ( 'no' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
604
- add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'add_add_to_cart_event_fragment' ] );
605
  }
606
  }
607
 
@@ -620,17 +623,17 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
620
 
621
  if ( $this->is_pixel_enabled() ) {
622
 
623
- $params = [
624
  'content_ids' => $this->get_cart_content_ids(),
625
  'content_name' => $this->get_cart_content_names(),
626
  'content_type' => 'product',
627
  'contents' => $this->get_cart_contents(),
628
  'value' => $this->get_cart_total(),
629
  'currency' => get_woocommerce_currency(),
630
- ];
631
 
632
  // send the event ID to prevent duplication
633
- if ( ! empty ( $event_id = WC()->session->get( 'facebook_for_woocommerce_add_to_cart_event_id' ) ) ) {
634
 
635
  $params['event_id'] = $event_id;
636
  }
@@ -658,7 +661,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
658
  public function add_filter_for_conditional_add_to_cart_fragment() {
659
 
660
  if ( 'no' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
661
- add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'add_conditional_add_to_cart_event_fragment' ] );
662
  }
663
  }
664
 
@@ -677,17 +680,17 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
677
 
678
  if ( $this->is_pixel_enabled() ) {
679
 
680
- $params = [
681
  'content_ids' => $this->get_cart_content_ids(),
682
  'content_name' => $this->get_cart_content_names(),
683
  'content_type' => 'product',
684
  'contents' => $this->get_cart_contents(),
685
  'value' => $this->get_cart_total(),
686
  'currency' => get_woocommerce_currency(),
687
- ];
688
 
689
  // send the event ID to prevent duplication
690
- if ( ! empty ( $event_id = WC()->session->get( 'facebook_for_woocommerce_add_to_cart_event_id' ) ) ) {
691
 
692
  $params['event_id'] = $event_id;
693
  }
@@ -720,7 +723,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
720
  *
721
  * @since 1.10.2
722
  *
723
- * @param string $redirect URL redirecting to (usually cart)
724
  * @param null|\WC_Product $product the product just added to the cart
725
  * @return string
726
  */
@@ -794,9 +797,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
794
  }
795
 
796
  $event_name = 'InitiateCheckout';
797
- $event_data = [
798
  'event_name' => $event_name,
799
- 'custom_data' => [
800
  'num_items' => $this->get_cart_num_items(),
801
  'content_ids' => $this->get_cart_content_ids(),
802
  'content_name' => $this->get_cart_content_names(),
@@ -804,9 +807,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
804
  'contents' => $this->get_cart_contents(),
805
  'value' => $this->get_cart_total(),
806
  'currency' => get_woocommerce_currency(),
807
- ],
808
- 'user_data' => $this->pixel->get_user_info()
809
- ];
810
 
811
  // if there is only one item in the cart, send its first category
812
  if ( ( $cart = WC()->cart ) && count( $cart->get_cart() ) === 1 ) {
@@ -880,9 +883,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
880
  }
881
 
882
  $content_type = 'product';
883
- $contents = [];
884
- $product_ids = [ [] ];
885
- $product_names = [];
886
 
887
  foreach ( $order->get_items() as $item ) {
888
 
@@ -905,18 +908,18 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
905
  }
906
  }
907
  // Advanced matching information is extracted from the order
908
- $event_data = [
909
  'event_name' => $event_name,
910
- 'custom_data' => [
911
  'content_ids' => wp_json_encode( array_merge( ... $product_ids ) ),
912
  'content_name' => wp_json_encode( $product_names ),
913
  'contents' => wp_json_encode( $contents ),
914
  'content_type' => $content_type,
915
  'value' => $order->get_total(),
916
  'currency' => get_woocommerce_currency(),
917
- ],
918
- 'user_data' => $this->get_user_data_from_billing_address($order)
919
- ];
920
 
921
  $event = new Event( $event_data );
922
 
@@ -945,7 +948,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
945
  */
946
  public function inject_subscribe_event( $order_id ) {
947
 
948
- if ( ! function_exists( 'wcs_get_subscriptions_for_order' ) || ! $this->is_pixel_enabled() || $this->pixel->is_last_event( 'Subscribe' ) ) {
949
  return;
950
  }
951
 
@@ -955,15 +958,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
955
  $event_name = 'Subscribe';
956
 
957
  // TODO consider including (int|float) 'predicted_ltv': "Predicted lifetime value of a subscriber as defined by the advertiser and expressed as an exact value." {FN 2020-03-20}
958
- $event_data = [
959
  'event_name' => $event_name,
960
- 'custom_data' => [
961
  'sign_up_fee' => $subscription->get_sign_up_fee(),
962
  'value' => $subscription->get_total(),
963
  'currency' => get_woocommerce_currency(),
964
- ],
965
- 'user_data' => $this->pixel->get_user_info()
966
- ];
967
 
968
  $event = new Event( $event_data );
969
 
@@ -1026,7 +1029,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1026
 
1027
  try {
1028
 
1029
- facebook_for_woocommerce()->get_api()->send_pixel_events( facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(), [ $event ] );
1030
 
1031
  $success = true;
1032
 
@@ -1065,7 +1068,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1065
  */
1066
  private function get_cart_content_ids() {
1067
 
1068
- $product_ids = [ [] ];
1069
 
1070
  if ( $cart = WC()->cart ) {
1071
 
@@ -1091,7 +1094,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1091
  */
1092
  private function get_cart_content_names() {
1093
 
1094
- $product_names = [];
1095
 
1096
  if ( $cart = WC()->cart ) {
1097
 
@@ -1117,7 +1120,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1117
  */
1118
  private function get_cart_contents() {
1119
 
1120
- $cart_contents = [];
1121
 
1122
  if ( $cart = WC()->cart ) {
1123
 
@@ -1157,28 +1160,28 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1157
  *
1158
  * @return array
1159
  */
1160
- private function get_user_data_from_billing_address($order) {
1161
- if($this->aam_settings == null || !$this->aam_settings->get_enable_automatic_matching() ){
1162
  return array();
1163
  }
1164
- $user_data= array();
1165
  $user_data['fn'] = $order->get_billing_first_name();
1166
  $user_data['ln'] = $order->get_billing_last_name();
1167
  $user_data['em'] = $order->get_billing_email();
1168
  // get_user_id() returns 0 if the current user is a guest
1169
- $user_data['external_id'] = $order->get_user_id() === 0 ? null : strval($order->get_user_id());
1170
- $user_data['zp'] = $order->get_billing_postcode();
1171
- $user_data['st'] = $order->get_billing_state();
1172
  // We can use country as key because this information is for CAPI events only
1173
  $user_data['country'] = $order->get_billing_country();
1174
- $user_data['ct'] = $order->get_billing_city();
1175
- $user_data['ph'] = $order->get_billing_phone();
1176
  // The fields contain country, so we do not need to add a condition
1177
- foreach ($user_data as $field => $value) {
1178
- if( $value === null || $value === '' ||
1179
- !in_array($field, $this->aam_settings->get_enabled_automatic_matching_fields())
1180
- ){
1181
- unset($user_data[$field]);
1182
  }
1183
  }
1184
  return $user_data;
@@ -1189,7 +1192,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
1189
  *
1190
  * @return array
1191
  */
1192
- public function get_tracked_events(){
1193
  return $this->tracked_events;
1194
  }
1195
 
63
 
64
  $this->pixel = new \WC_Facebookcommerce_Pixel( $user_info );
65
  $this->aam_settings = $aam_settings;
66
+ $this->tracked_events = array();
67
 
68
  $this->add_hooks();
69
  }
100
  private function add_hooks() {
101
 
102
  // inject Pixel
103
+ add_action( 'wp_head', array( $this, 'inject_base_pixel' ) );
104
+ add_action( 'wp_footer', array( $this, 'inject_base_pixel_noscript' ) );
105
 
106
  // ViewContent for individual products
107
+ add_action( 'woocommerce_after_single_product', array( $this, 'inject_view_content_event' ) );
108
+ add_action( 'woocommerce_after_single_product', array( $this, 'maybe_inject_search_event' ) );
109
 
110
+ add_action( 'woocommerce_after_shop_loop', array( $this, 'inject_view_category_event' ) );
111
 
112
+ add_action( 'pre_get_posts', array( $this, 'inject_search_event' ) );
113
+ add_filter( 'woocommerce_redirect_single_search_result', array( $this, 'maybe_add_product_search_event_to_session' ) );
114
 
115
  // AddToCart events
116
+ add_action( 'woocommerce_add_to_cart', array( $this, 'inject_add_to_cart_event' ), 40, 4 );
117
  // AddToCart while AJAX is enabled
118
+ add_action( 'woocommerce_ajax_added_to_cart', array( $this, 'add_filter_for_add_to_cart_fragments' ) );
119
  // AddToCart while using redirect to cart page
120
  if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
121
+ add_filter( 'woocommerce_add_to_cart_redirect', array( $this, 'set_last_product_added_to_cart_upon_redirect' ), 10, 2 );
122
+ add_action( 'woocommerce_ajax_added_to_cart', array( $this, 'set_last_product_added_to_cart_upon_ajax_redirect' ) );
123
+ add_action( 'woocommerce_after_cart', array( $this, 'inject_add_to_cart_redirect_event' ), 10, 2 );
124
  }
125
 
126
  // InitiateCheckout events
127
+ add_action( 'woocommerce_after_checkout_form', array( $this, 'inject_initiate_checkout_event' ) );
128
  // Purchase and Subscribe events
129
+ add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'inject_purchase_event' ) );
130
+ add_action( 'woocommerce_thankyou', array( $this, 'inject_purchase_event' ), 40 );
131
 
132
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
133
+ add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );
134
  }
135
 
136
 
195
  // if any product is a variant, fire the pixel with
196
  // content_type: product_group
197
  $content_type = 'product';
198
+ $product_ids = array();
199
+ $contents = array();
200
 
201
  foreach ( $products as $product ) {
202
 
204
  continue;
205
  }
206
 
207
+ $contents[] = array(
208
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
209
  'quantity' => 1, // consider category results a quantity of 1
210
+ );
211
 
212
  $product_ids = array_merge(
213
  $product_ids,
221
  $categories = WC_Facebookcommerce_Utils::get_product_categories( get_the_ID() );
222
 
223
  $event_name = 'ViewCategory';
224
+ $event_data = array(
225
+ 'event_name' => $event_name,
226
+ 'custom_data' => array(
227
  'content_name' => $categories['name'],
228
  'content_category' => $categories['categories'],
229
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
230
  'content_type' => $content_type,
231
  'contents' => $contents,
232
+ ),
233
+ 'user_data' => $this->pixel->get_user_info(),
234
+ );
235
 
236
  $event = new Event( $event_data );
237
 
296
  */
297
  private function add_product_search_event_to_session( Event $event ) {
298
 
299
+ if ( isset( WC()->session ) && is_callable( array( WC()->session, 'has_session' ) ) && WC()->session->has_session() ) {
300
  WC()->session->set( $this->search_event_data_session_variable, $event->get_data() );
301
  }
302
  }
317
 
318
  $this->search_event = $this->get_product_search_event_from_session();
319
 
320
+ if ( ! $this->search_event ) {
321
  return;
322
  }
323
 
335
  */
336
  private function get_product_search_event_from_session() {
337
 
338
+ if ( ! isset( WC()->session ) || ! is_callable( array( WC()->session, 'get' ) ) ) {
339
  return null;
340
  }
341
 
382
  }
383
 
384
  // needs to run before wc_template_redirect, normally hooked with priority 10
385
+ add_action( 'template_redirect', array( $this, 'send_search_event' ), 5 );
386
+ add_action( 'woocommerce_before_shop_loop', array( $this, 'actually_inject_search_event' ) );
387
  }
388
  }
389
 
417
 
418
  // if any product is a variant, fire the pixel with content_type: product_group
419
  $content_type = 'product';
420
+ $product_ids = array();
421
+ $contents = array();
422
  $total_value = 0.00;
423
 
424
  foreach ( $wp_query->posts as $post ) {
431
 
432
  $product_ids = array_merge( $product_ids, WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) );
433
 
434
+ $contents[] = array(
435
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
436
  'quantity' => 1, // consider the search results a quantity of 1
437
+ );
438
 
439
  $total_value += (float) $product->get_price();
440
 
443
  }
444
  }
445
 
446
+ $event_data = array(
447
  'event_name' => 'Search',
448
+ 'custom_data' => array(
449
  'content_type' => $content_type,
450
  'content_ids' => json_encode( array_slice( $product_ids, 0, 10 ) ),
451
  'contents' => $contents,
452
  'search_string' => get_search_query(),
453
  'value' => Framework\SV_WC_Helper::number_format( $total_value ),
454
  'currency' => get_woocommerce_currency(),
455
+ ),
456
+ 'user_data' => $this->pixel->get_user_info(),
457
+ );
458
 
459
  $this->search_event = new Event( $event_data );
460
  }
472
 
473
  $event = $this->get_search_event();
474
 
475
+ $this->pixel->inject_event(
476
+ $event->get_name(),
477
+ array(
478
+ 'event_id' => $event->get_id(),
479
+ 'event_name' => $event->get_name(),
480
+ 'custom_data' => $event->get_custom_data(),
481
+ )
482
+ );
483
  }
484
 
485
 
502
  }
503
 
504
  // if product is variable or grouped, fire the pixel with content_type: product_group
505
+ if ( $product->is_type( array( 'variable', 'grouped' ) ) ) {
506
  $content_type = 'product_group';
507
  } else {
508
  $content_type = 'product';
510
 
511
  $categories = \WC_Facebookcommerce_Utils::get_product_categories( $product->get_id() );
512
 
513
+ $event_data = array(
514
  'event_name' => 'ViewContent',
515
+ 'custom_data' => array(
516
  'content_name' => $product->get_title(),
517
  'content_ids' => wp_json_encode( \WC_Facebookcommerce_Utils::get_fb_content_ids( $product ) ),
518
  'content_type' => $content_type,
519
  'contents' => wp_json_encode(
520
+ array(
521
+ array(
522
  'id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
523
  'quantity' => 1,
524
+ ),
525
+ )
526
  ),
527
  'content_category' => $categories['name'],
528
  'value' => $product->get_price(),
529
  'currency' => get_woocommerce_currency(),
530
+ ),
531
+ 'user_data' => $this->pixel->get_user_info(),
532
+ );
533
 
534
  $event = new Event( $event_data );
535
 
547
  * @internal
548
  *
549
  * @param string $cart_item_key the cart item key
550
+ * @param int $product_id the product identifier
551
+ * @param int $quantity the added product quantity
552
+ * @param int $variation_id the product variation identifier
553
  */
554
  public function inject_add_to_cart_event( $cart_item_key, $product_id, $quantity, $variation_id ) {
555
 
565
  return;
566
  }
567
 
568
+ $event_data = array(
569
  'event_name' => 'AddToCart',
570
+ 'custom_data' => array(
571
  'content_ids' => $this->get_cart_content_ids(),
572
  'content_name' => $this->get_cart_content_names(),
573
  'content_type' => 'product',
574
  'contents' => $this->get_cart_contents(),
575
  'value' => $this->get_cart_total(),
576
  'currency' => get_woocommerce_currency(),
577
+ ),
578
+ 'user_data' => $this->pixel->get_user_info(),
579
+ );
580
 
581
  $event = new SkyVerge\WooCommerce\Facebook\Events\Event( $event_data );
582
 
604
  public function add_filter_for_add_to_cart_fragments() {
605
 
606
  if ( 'no' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
607
+ add_filter( 'woocommerce_add_to_cart_fragments', array( $this, 'add_add_to_cart_event_fragment' ) );
608
  }
609
  }
610
 
623
 
624
  if ( $this->is_pixel_enabled() ) {
625
 
626
+ $params = array(
627
  'content_ids' => $this->get_cart_content_ids(),
628
  'content_name' => $this->get_cart_content_names(),
629
  'content_type' => 'product',
630
  'contents' => $this->get_cart_contents(),
631
  'value' => $this->get_cart_total(),
632
  'currency' => get_woocommerce_currency(),
633
+ );
634
 
635
  // send the event ID to prevent duplication
636
+ if ( ! empty( $event_id = WC()->session->get( 'facebook_for_woocommerce_add_to_cart_event_id' ) ) ) {
637
 
638
  $params['event_id'] = $event_id;
639
  }
661
  public function add_filter_for_conditional_add_to_cart_fragment() {
662
 
663
  if ( 'no' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
664
+ add_filter( 'woocommerce_add_to_cart_fragments', array( $this, 'add_conditional_add_to_cart_event_fragment' ) );
665
  }
666
  }
667
 
680
 
681
  if ( $this->is_pixel_enabled() ) {
682
 
683
+ $params = array(
684
  'content_ids' => $this->get_cart_content_ids(),
685
  'content_name' => $this->get_cart_content_names(),
686
  'content_type' => 'product',
687
  'contents' => $this->get_cart_contents(),
688
  'value' => $this->get_cart_total(),
689
  'currency' => get_woocommerce_currency(),
690
+ );
691
 
692
  // send the event ID to prevent duplication
693
+ if ( ! empty( $event_id = WC()->session->get( 'facebook_for_woocommerce_add_to_cart_event_id' ) ) ) {
694
 
695
  $params['event_id'] = $event_id;
696
  }
723
  *
724
  * @since 1.10.2
725
  *
726
+ * @param string $redirect URL redirecting to (usually cart)
727
  * @param null|\WC_Product $product the product just added to the cart
728
  * @return string
729
  */
797
  }
798
 
799
  $event_name = 'InitiateCheckout';
800
+ $event_data = array(
801
  'event_name' => $event_name,
802
+ 'custom_data' => array(
803
  'num_items' => $this->get_cart_num_items(),
804
  'content_ids' => $this->get_cart_content_ids(),
805
  'content_name' => $this->get_cart_content_names(),
807
  'contents' => $this->get_cart_contents(),
808
  'value' => $this->get_cart_total(),
809
  'currency' => get_woocommerce_currency(),
810
+ ),
811
+ 'user_data' => $this->pixel->get_user_info(),
812
+ );
813
 
814
  // if there is only one item in the cart, send its first category
815
  if ( ( $cart = WC()->cart ) && count( $cart->get_cart() ) === 1 ) {
883
  }
884
 
885
  $content_type = 'product';
886
+ $contents = array();
887
+ $product_ids = array( array() );
888
+ $product_names = array();
889
 
890
  foreach ( $order->get_items() as $item ) {
891
 
908
  }
909
  }
910
  // Advanced matching information is extracted from the order
911
+ $event_data = array(
912
  'event_name' => $event_name,
913
+ 'custom_data' => array(
914
  'content_ids' => wp_json_encode( array_merge( ... $product_ids ) ),
915
  'content_name' => wp_json_encode( $product_names ),
916
  'contents' => wp_json_encode( $contents ),
917
  'content_type' => $content_type,
918
  'value' => $order->get_total(),
919
  'currency' => get_woocommerce_currency(),
920
+ ),
921
+ 'user_data' => $this->get_user_data_from_billing_address( $order ),
922
+ );
923
 
924
  $event = new Event( $event_data );
925
 
948
  */
949
  public function inject_subscribe_event( $order_id ) {
950
 
951
+ if ( ! function_exists( 'wcs_get_subscriptions_for_order' ) || ! $this->is_pixel_enabled() || $this->pixel->is_last_event( 'Subscribe' ) ) {
952
  return;
953
  }
954
 
958
  $event_name = 'Subscribe';
959
 
960
  // TODO consider including (int|float) 'predicted_ltv': "Predicted lifetime value of a subscriber as defined by the advertiser and expressed as an exact value." {FN 2020-03-20}
961
+ $event_data = array(
962
  'event_name' => $event_name,
963
+ 'custom_data' => array(
964
  'sign_up_fee' => $subscription->get_sign_up_fee(),
965
  'value' => $subscription->get_total(),
966
  'currency' => get_woocommerce_currency(),
967
+ ),
968
+ 'user_data' => $this->pixel->get_user_info(),
969
+ );
970
 
971
  $event = new Event( $event_data );
972
 
1029
 
1030
  try {
1031
 
1032
+ facebook_for_woocommerce()->get_api()->send_pixel_events( facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(), array( $event ) );
1033
 
1034
  $success = true;
1035
 
1068
  */
1069
  private function get_cart_content_ids() {
1070
 
1071
+ $product_ids = array( array() );
1072
 
1073
  if ( $cart = WC()->cart ) {
1074
 
1094
  */
1095
  private function get_cart_content_names() {
1096
 
1097
+ $product_names = array();
1098
 
1099
  if ( $cart = WC()->cart ) {
1100
 
1120
  */
1121
  private function get_cart_contents() {
1122
 
1123
+ $cart_contents = array();
1124
 
1125
  if ( $cart = WC()->cart ) {
1126
 
1160
  *
1161
  * @return array
1162
  */
1163
+ private function get_user_data_from_billing_address( $order ) {
1164
+ if ( $this->aam_settings == null || ! $this->aam_settings->get_enable_automatic_matching() ) {
1165
  return array();
1166
  }
1167
+ $user_data = array();
1168
  $user_data['fn'] = $order->get_billing_first_name();
1169
  $user_data['ln'] = $order->get_billing_last_name();
1170
  $user_data['em'] = $order->get_billing_email();
1171
  // get_user_id() returns 0 if the current user is a guest
1172
+ $user_data['external_id'] = $order->get_user_id() === 0 ? null : strval( $order->get_user_id() );
1173
+ $user_data['zp'] = $order->get_billing_postcode();
1174
+ $user_data['st'] = $order->get_billing_state();
1175
  // We can use country as key because this information is for CAPI events only
1176
  $user_data['country'] = $order->get_billing_country();
1177
+ $user_data['ct'] = $order->get_billing_city();
1178
+ $user_data['ph'] = $order->get_billing_phone();
1179
  // The fields contain country, so we do not need to add a condition
1180
+ foreach ( $user_data as $field => $value ) {
1181
+ if ( $value === null || $value === '' ||
1182
+ ! in_array( $field, $this->aam_settings->get_enabled_automatic_matching_fields() )
1183
+ ) {
1184
+ unset( $user_data[ $field ] );
1185
  }
1186
  }
1187
  return $user_data;
1192
  *
1193
  * @return array
1194
  */
1195
+ public function get_tracked_events() {
1196
  return $this->tracked_events;
1197
  }
1198
 
facebook-commerce-messenger-chat.php CHANGED
@@ -16,9 +16,16 @@ if ( ! class_exists( 'WC_Facebookcommerce_MessengerChat' ) ) :
16
  include_once 'includes/fbutils.php';
17
  }
18
 
 
 
 
19
  class WC_Facebookcommerce_MessengerChat {
20
 
21
-
 
 
 
 
22
  public function __construct( $settings ) {
23
 
24
  $this->page_id = isset( $settings['fb_page_id'] )
@@ -42,7 +49,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_MessengerChat' ) ) :
42
 
43
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) :
44
 
45
- printf( "
 
46
  <div
47
  attribution=\"fbe_woocommerce\"
48
  class=\"fb-customerchat\"
16
  include_once 'includes/fbutils.php';
17
  }
18
 
19
+ /**
20
+ * Messenger chat handler class.
21
+ */
22
  class WC_Facebookcommerce_MessengerChat {
23
 
24
+ /**
25
+ * Class constructor.
26
+ *
27
+ * @param array $settings FB page settings array.
28
+ */
29
  public function __construct( $settings ) {
30
 
31
  $this->page_id = isset( $settings['fb_page_id'] )
49
 
50
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) :
51
 
52
+ printf(
53
+ "
54
  <div
55
  attribution=\"fbe_woocommerce\"
56
  class=\"fb-customerchat\"
facebook-commerce-pixel-event.php CHANGED
@@ -16,38 +16,67 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
16
  class WC_Facebookcommerce_Pixel {
17
 
18
 
19
- const SETTINGS_KEY = 'facebook_config';
20
- const PIXEL_ID_KEY = 'pixel_id';
21
- const USE_PII_KEY = 'use_pii';
22
- const USE_S2S_KEY= 'use_s2s';
23
  const ACCESS_TOKEN_KEY = 'access_token';
24
 
25
- /** @var string cache key for pixel script block output */
26
- const PIXEL_RENDER = 'pixel_render';
27
- /** @var string cache key for pixel noscript block output */
 
 
 
 
 
 
 
 
 
28
  const NO_SCRIPT_RENDER = 'no_script_render';
29
 
30
- /** @var array script render memoization helper */
31
- public static $render_cache = [];
 
 
 
 
32
 
 
 
 
 
 
33
  private $user_info;
34
 
 
 
 
 
 
35
  private $last_event;
36
 
37
-
 
 
 
 
38
  public function __construct( $user_info = array() ) {
39
 
40
  $this->user_info = $user_info;
41
  $this->last_event = '';
42
  }
43
 
 
 
 
44
  public static function initialize() {
45
  if ( ! is_admin() ) {
46
  return;
47
  }
48
 
49
- // Initialize PixelID in storage - this will only need to happen when the
50
- // user is an admin
51
  $pixel_id = self::get_pixel_id();
52
  if ( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) &&
53
  class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) {
@@ -61,7 +90,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
61
  }
62
 
63
  $is_advanced_matching_enabled = self::get_use_pii_key();
64
- if ( $is_advanced_matching_enabled == null &&
65
  class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) {
66
  $fb_warm_is_advanced_matching_enabled =
67
  WC_Facebookcommerce_WarmConfig::$fb_warm_is_advanced_matching_enabled;
@@ -89,12 +118,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
89
  *
90
  * @param string $js_code
91
  */
92
- return apply_filters( 'facebook_woocommerce_pixel_init', sprintf(
93
- "fbq('init', '%s', %s, %s);\n",
94
- esc_js( self::get_pixel_id() ),
95
- json_encode( $this->user_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
96
- json_encode( [ 'agent' => $agent_string ], JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
97
- ) );
 
 
 
98
  }
99
 
100
 
@@ -107,8 +139,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
107
 
108
  $pixel_id = self::get_pixel_id();
109
 
110
- // bail if no ID or already rendered
111
- if ( empty( $pixel_id )|| ! empty( self::$render_cache[ self::PIXEL_RENDER ] ) ) {
112
  return '';
113
  }
114
 
@@ -129,11 +161,11 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
129
 
130
  <?php echo $this->get_pixel_init_code(); ?>
131
 
132
- fbq( 'track', 'PageView', <?php echo json_encode( self::build_params( [], 'PageView' ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ) ?> );
133
 
134
  document.addEventListener( 'DOMContentLoaded', function() {
135
  jQuery && jQuery( function( $ ) {
136
- // insert placeholder for events injected when a product is added to the cart through AJAX
137
  $( document.body ).append( '<div class=\"wc-facebook-pixel-event-placeholder\"></div>' );
138
  } );
139
  }, false );
@@ -198,11 +230,11 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
198
  /**
199
  * Determines if the last event in the current thread matches a given event.
200
  *
201
- * TODO remove this deprecated method by March 2020 or version 2.0.0 {FN 2020-03-25}
202
  *
203
  * @deprecated since 1.11.0
204
  *
205
- * @param string $event_name
206
  * @return bool
207
  */
208
  public function check_last_event( $event_name ) {
@@ -222,9 +254,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
222
  *
223
  * @since 1.10.2
224
  *
225
- * @param string $event_name the name of the event to track
226
- * @param array $params custom event parameters
227
- * @param string $method name of the pixel's fbq() function to call
228
  * @return string
229
  */
230
  public function get_event_code( $event_name, $params, $method = 'track' ) {
@@ -242,9 +274,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
242
  *
243
  * @since 1.10.2
244
  *
245
- * @param string $event_name the name of the event to track
246
- * @param array $params custom event parameters
247
- * @param string $method name of the pixel's fbq() function to call
248
  * @return string
249
  */
250
  public function get_event_script( $event_name, $params, $method = 'track' ) {
@@ -265,24 +297,20 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
265
 
266
  /**
267
  * Prints or enqueues the JavaScript code to track an event.
268
- *
269
  * Preferred method to inject events in a page.
 
270
  * @see \WC_Facebookcommerce_Pixel::build_event()
271
  *
272
- * @param string $event_name the name of the event to track
273
- * @param array $params custom event parameters
274
- * @param string $method name of the pixel's fbq() function to call
275
  */
276
  public function inject_event( $event_name, $params, $method = 'track' ) {
277
 
278
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
279
-
280
  \WC_Facebookcommerce_Utils::wc_enqueue_js( $this->get_event_code( $event_name, self::build_params( $params, $event_name ), $method ) );
281
-
282
  } else {
283
-
284
- // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
285
- printf( $this->get_event_script( $event_name, self::build_params( $params, $event_name ), $method ) );
286
  }
287
  }
288
 
@@ -294,10 +322,10 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
294
  *
295
  * @since 1.10.2
296
  *
297
- * @param string $event_name the name of the event to track
298
- * @param array $params custom event parameters
299
- * @param string $listener name of the JavaScript event to listen for
300
- * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching
301
  * @return string
302
  */
303
  public function get_conditional_event_script( $event_name, $params, $listener, $jsonified_pii ) {
@@ -305,7 +333,12 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
305
  $code = self::build_event( $event_name, $params, 'track' );
306
  $this->last_event = $event_name;
307
 
308
- /** TODO: use the settings stored by {@see \WC_Facebookcommerce_Integration}. The use_pii setting here is currently always disabled regardless of the value configured in the plugin settings page {WV-2020-01-02} */
 
 
 
 
 
309
  // Prepends fbq(...) with pii information to the injected code.
310
  if ( $jsonified_pii && get_option( self::SETTINGS_KEY )[ self::USE_PII_KEY ] ) {
311
  $this->user_info = '%s';
@@ -333,10 +366,10 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
333
  *
334
  * The tracking code will be executed when the given JavaScript event is triggered.
335
  *
336
- * @param string $event_name
337
- * @param array $params custom event parameters
338
- * @param string $listener name of the JavaScript event to listen for
339
- * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching
340
  * @return string
341
  */
342
  public function inject_conditional_event( $event_name, $params, $listener, $jsonified_pii = '' ) {
@@ -353,9 +386,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
353
  *
354
  * @since 1.10.2
355
  *
356
- * @param string $event_name the name of the event to track
357
- * @param array $params custom event parameters
358
- * @param string $listened_event name of the JavaScript event to listen for
359
  * @return string
360
  */
361
  public function get_conditional_one_time_event_script( $event_name, $params, $listened_event ) {
@@ -369,7 +402,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
369
  <script <?php echo self::get_script_attributes(); ?>>
370
  function handle<?php echo $event_name; ?>Event() {
371
  <?php echo $code; ?>
372
- // some weird themes (hi, Basel) are running this script twice, so two listeners are added and we need to remove them after running one
373
  jQuery( document.body ).off( '<?php echo esc_js( $listened_event ); ?>', handle<?php echo $event_name; ?>Event );
374
  }
375
 
@@ -387,28 +420,31 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
387
  *
388
  * @see \WC_Facebookcommerce_Pixel::inject_event() for the preferred method to inject an event.
389
  *
390
- * @param string $event_name event name
391
- * @param array $params event params
392
- * @param string $method optional, defaults to 'track'
393
  * @return string
394
  */
395
  public static function build_event( $event_name, $params, $method = 'track' ) {
396
 
397
- // do not send the event name in the params
398
  if ( isset( $params['event_name'] ) ) {
399
 
400
  unset( $params['event_name'] );
401
  }
402
 
403
- // if possible, send the event ID to avoid duplication
404
- // @see https://developers.facebook.com/docs/marketing-api/server-side-api/deduplicate-pixel-and-server-side-events#deduplication-best-practices
 
 
 
405
  if ( isset( $params['event_id'] ) ) {
406
 
407
  $event_id = $params['event_id'];
408
  unset( $params['event_id'] );
409
  }
410
 
411
- // if custom data is set, send only the custom data
412
  if ( isset( $params['custom_data'] ) ) {
413
 
414
  $params = $params['custom_data'];
@@ -417,7 +453,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
417
  if ( ! empty( $event_id ) ) {
418
  $event = sprintf(
419
  "/* %s Facebook Integration Event Tracking */\n" .
420
- "fbq('set', 'agent', '%s', '%s');\n".
421
  "fbq('%s', '%s', %s, %s);",
422
  WC_Facebookcommerce_Utils::getIntegrationName(),
423
  Event::get_platform_identifier(),
@@ -425,14 +461,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
425
  esc_js( $method ),
426
  esc_js( $event_name ),
427
  json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
428
- json_encode( [ 'eventID' => $event_id ], JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
429
  );
430
 
431
  } else {
432
 
433
  $event = sprintf(
434
  "/* %s Facebook Integration Event Tracking */\n" .
435
- "fbq('set', 'agent', '%s', '%s');\n".
436
  "fbq('%s', '%s', %s);",
437
  WC_Facebookcommerce_Utils::getIntegrationName(),
438
  Event::get_platform_identifier(),
@@ -454,11 +490,11 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
454
  *
455
  * @since 1.10.2
456
  *
457
- * @param array $params user defined parameters
458
- * @param string $event the event name the params are for
459
  * @return array
460
  */
461
- private static function build_params( $params = [], $event = '' ) {
462
 
463
  $params = array_replace( Event::get_version_info(), $params );
464
 
@@ -467,8 +503,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
467
  *
468
  * @since 1.10.2
469
  *
470
- * @param array $params user defined parameters
471
- * @param string $event the event name
472
  */
473
  return (array) apply_filters( 'wc_facebook_pixel_params', $params, $event );
474
  }
@@ -492,7 +528,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
492
  *
493
  * @param array $custom_attributes
494
  */
495
- $custom_attributes = (array) apply_filters( 'wc_facebook_pixel_script_attributes', [ 'type' => 'text/javascript' ] );
496
 
497
  foreach ( $custom_attributes as $tag => $value ) {
498
  $script_attributes .= ' ' . $tag . '="' . esc_attr( $value ) . '"';
@@ -501,21 +537,28 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
501
  return $script_attributes;
502
  }
503
 
504
-
 
 
505
  public static function get_pixel_id() {
506
  $fb_options = self::get_options();
507
  if ( ! $fb_options ) {
508
  return '';
509
  }
510
  return isset( $fb_options[ self::PIXEL_ID_KEY ] ) ?
511
- $fb_options[ self::PIXEL_ID_KEY ] : '';
512
  }
513
 
 
 
 
 
 
514
  public static function set_pixel_id( $pixel_id ) {
515
  $fb_options = self::get_options();
516
 
517
  if ( isset( $fb_options[ self::PIXEL_ID_KEY ] )
518
- && $fb_options[ self::PIXEL_ID_KEY ] == $pixel_id ) {
519
  return;
520
  }
521
 
@@ -523,20 +566,28 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
523
  update_option( self::SETTINGS_KEY, $fb_options );
524
  }
525
 
 
 
 
526
  public static function get_use_pii_key() {
527
  $fb_options = self::get_options();
528
  if ( ! $fb_options ) {
529
  return null;
530
  }
531
  return isset( $fb_options[ self::USE_PII_KEY ] ) ?
532
- $fb_options[ self::USE_PII_KEY ] : null;
533
  }
534
 
 
 
 
 
 
535
  public static function set_use_pii_key( $use_pii ) {
536
  $fb_options = self::get_options();
537
 
538
  if ( isset( $fb_options[ self::USE_PII_KEY ] )
539
- && $fb_options[ self::USE_PII_KEY ] == $use_pii ) {
540
  return;
541
  }
542
 
@@ -544,20 +595,28 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
544
  update_option( self::SETTINGS_KEY, $fb_options );
545
  }
546
 
 
 
 
547
  public static function get_use_s2s() {
548
  $fb_options = self::get_options();
549
  if ( ! $fb_options ) {
550
  return false;
551
  }
552
  return isset( $fb_options[ self::USE_S2S_KEY ] ) ?
553
- $fb_options[ self::USE_S2S_KEY ] : false;
554
  }
555
 
 
 
 
 
 
556
  public static function set_use_s2s( $use_s2s ) {
557
  $fb_options = self::get_options();
558
 
559
  if ( isset( $fb_options[ self::USE_S2S_KEY ] )
560
- && $fb_options[ self::USE_S2S_KEY ] == $use_s2s ) {
561
  return;
562
  }
563
 
@@ -565,20 +624,28 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
565
  update_option( self::SETTINGS_KEY, $fb_options );
566
  }
567
 
 
 
 
568
  public static function get_access_token() {
569
  $fb_options = self::get_options();
570
  if ( ! $fb_options ) {
571
  return '';
572
  }
573
  return isset( $fb_options[ self::ACCESS_TOKEN_KEY ] ) ?
574
- $fb_options[ self::ACCESS_TOKEN_KEY ] : '';
575
  }
576
 
 
 
 
 
 
577
  public static function set_access_token( $access_token ) {
578
  $fb_options = self::get_options();
579
 
580
  if ( isset( $fb_options[ self::ACCESS_TOKEN_KEY ] )
581
- && $fb_options[ self::ACCESS_TOKEN_KEY ] == $access_token ) {
582
  return;
583
  }
584
 
@@ -586,6 +653,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
586
  update_option( self::SETTINGS_KEY, $fb_options );
587
  }
588
 
 
 
 
589
  private static function get_version_info() {
590
  global $wp_version;
591
 
@@ -604,13 +674,16 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
604
  );
605
  }
606
 
 
 
 
607
  public static function get_options() {
608
  return get_option(
609
  self::SETTINGS_KEY,
610
  array(
611
- self::PIXEL_ID_KEY => '0',
612
- self::USE_PII_KEY => 0,
613
- self::USE_S2S_KEY => false,
614
  self::ACCESS_TOKEN_KEY => '',
615
  )
616
  );
@@ -636,7 +709,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
636
  *
637
  * @return string[]
638
  */
639
- public function get_user_info(){
640
  return $this->user_info;
641
  }
642
  }
16
  class WC_Facebookcommerce_Pixel {
17
 
18
 
19
+ const SETTINGS_KEY = 'facebook_config';
20
+ const PIXEL_ID_KEY = 'pixel_id';
21
+ const USE_PII_KEY = 'use_pii';
22
+ const USE_S2S_KEY = 'use_s2s';
23
  const ACCESS_TOKEN_KEY = 'access_token';
24
 
25
+ /**
26
+ * Cache key for pixel script block output.
27
+ *
28
+ * @var string cache key.
29
+ * */
30
+ const PIXEL_RENDER = 'pixel_render';
31
+
32
+ /**
33
+ * Cache key for pixel noscript block output.
34
+ *
35
+ * @var string cache key.
36
+ * */
37
  const NO_SCRIPT_RENDER = 'no_script_render';
38
 
39
+ /**
40
+ * Script render memoization helper.
41
+ *
42
+ * @var array Cache array.
43
+ */
44
+ public static $render_cache = array();
45
 
46
+ /**
47
+ * User information.
48
+ *
49
+ * @var array Information array.
50
+ */
51
  private $user_info;
52
 
53
+ /**
54
+ * The name of the last event.
55
+ *
56
+ * @var string Event name.
57
+ */
58
  private $last_event;
59
 
60
+ /**
61
+ * Class constructor.
62
+ *
63
+ * @param array $user_info User information array.
64
+ */
65
  public function __construct( $user_info = array() ) {
66
 
67
  $this->user_info = $user_info;
68
  $this->last_event = '';
69
  }
70
 
71
+ /**
72
+ * Initialize pixelID.
73
+ */
74
  public static function initialize() {
75
  if ( ! is_admin() ) {
76
  return;
77
  }
78
 
79
+ // Initialize PixelID in storage - this will only need to happen when the user is an admin.
 
80
  $pixel_id = self::get_pixel_id();
81
  if ( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) &&
82
  class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) {
90
  }
91
 
92
  $is_advanced_matching_enabled = self::get_use_pii_key();
93
+ if ( null == $is_advanced_matching_enabled &&
94
  class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) {
95
  $fb_warm_is_advanced_matching_enabled =
96
  WC_Facebookcommerce_WarmConfig::$fb_warm_is_advanced_matching_enabled;
118
  *
119
  * @param string $js_code
120
  */
121
+ return apply_filters(
122
+ 'facebook_woocommerce_pixel_init',
123
+ sprintf(
124
+ "fbq('init', '%s', %s, %s);\n",
125
+ esc_js( self::get_pixel_id() ),
126
+ json_encode( $this->user_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
127
+ json_encode( array( 'agent' => $agent_string ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
128
+ )
129
+ );
130
  }
131
 
132
 
139
 
140
  $pixel_id = self::get_pixel_id();
141
 
142
+ // Bail if no ID or already rendered.
143
+ if ( empty( $pixel_id ) || ! empty( self::$render_cache[ self::PIXEL_RENDER ] ) ) {
144
  return '';
145
  }
146
 
161
 
162
  <?php echo $this->get_pixel_init_code(); ?>
163
 
164
+ fbq( 'track', 'PageView', <?php echo json_encode( self::build_params( array(), 'PageView' ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ); ?> );
165
 
166
  document.addEventListener( 'DOMContentLoaded', function() {
167
  jQuery && jQuery( function( $ ) {
168
+ // Insert placeholder for events injected when a product is added to the cart through AJAX.
169
  $( document.body ).append( '<div class=\"wc-facebook-pixel-event-placeholder\"></div>' );
170
  } );
171
  }, false );
230
  /**
231
  * Determines if the last event in the current thread matches a given event.
232
  *
233
+ * TODO remove this deprecated method by March 2020 or version 2.0.0 {FN 2020-03-25}.
234
  *
235
  * @deprecated since 1.11.0
236
  *
237
+ * @param string $event_name Name of the event.
238
  * @return bool
239
  */
240
  public function check_last_event( $event_name ) {
254
  *
255
  * @since 1.10.2
256
  *
257
+ * @param string $event_name The name of the event to track.
258
+ * @param array $params Custom event parameters.
259
+ * @param string $method Name of the pixel's fbq() function to call.
260
  * @return string
261
  */
262
  public function get_event_code( $event_name, $params, $method = 'track' ) {
274
  *
275
  * @since 1.10.2
276
  *
277
+ * @param string $event_name The name of the event to track.
278
+ * @param array $params Custom event parameters.
279
+ * @param string $method Name of the pixel's fbq() function to call.
280
  * @return string
281
  */
282
  public function get_event_script( $event_name, $params, $method = 'track' ) {
297
 
298
  /**
299
  * Prints or enqueues the JavaScript code to track an event.
 
300
  * Preferred method to inject events in a page.
301
+ *
302
  * @see \WC_Facebookcommerce_Pixel::build_event()
303
  *
304
+ * @param string $event_name The name of the event to track.
305
+ * @param array $params Custom event parameters.
306
+ * @param string $method Name of the pixel's fbq() function to call.
307
  */
308
  public function inject_event( $event_name, $params, $method = 'track' ) {
309
 
310
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
 
311
  \WC_Facebookcommerce_Utils::wc_enqueue_js( $this->get_event_code( $event_name, self::build_params( $params, $event_name ), $method ) );
 
312
  } else {
313
+ printf( $this->get_event_script( $event_name, self::build_params( $params, $event_name ), $method ) ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
 
 
314
  }
315
  }
316
 
322
  *
323
  * @since 1.10.2
324
  *
325
+ * @param string $event_name The name of the event to track.
326
+ * @param array $params Custom event parameters.
327
+ * @param string $listener Name of the JavaScript event to listen for.
328
+ * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching.
329
  * @return string
330
  */
331
  public function get_conditional_event_script( $event_name, $params, $listener, $jsonified_pii ) {
333
  $code = self::build_event( $event_name, $params, 'track' );
334
  $this->last_event = $event_name;
335
 
336
+ /**
337
+ * TODO: use the settings stored by {@see \WC_Facebookcommerce_Integration}.
338
+ * The use_pii setting here is currently always disabled regardless of
339
+ * the value configured in the plugin settings page {WV-2020-01-02}.
340
+ */
341
+
342
  // Prepends fbq(...) with pii information to the injected code.
343
  if ( $jsonified_pii && get_option( self::SETTINGS_KEY )[ self::USE_PII_KEY ] ) {
344
  $this->user_info = '%s';
366
  *
367
  * The tracking code will be executed when the given JavaScript event is triggered.
368
  *
369
+ * @param string $event_name Name of the event.
370
+ * @param array $params Custom event parameters.
371
+ * @param string $listener Name of the JavaScript event to listen for.
372
+ * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching.
373
  * @return string
374
  */
375
  public function inject_conditional_event( $event_name, $params, $listener, $jsonified_pii = '' ) {
386
  *
387
  * @since 1.10.2
388
  *
389
+ * @param string $event_name The name of the event to track.
390
+ * @param array $params Custom event parameters.
391
+ * @param string $listened_event Name of the JavaScript event to listen for.
392
  * @return string
393
  */
394
  public function get_conditional_one_time_event_script( $event_name, $params, $listened_event ) {
402
  <script <?php echo self::get_script_attributes(); ?>>
403
  function handle<?php echo $event_name; ?>Event() {
404
  <?php echo $code; ?>
405
+ // Some weird themes (hi, Basel) are running this script twice, so two listeners are added and we need to remove them after running one.
406
  jQuery( document.body ).off( '<?php echo esc_js( $listened_event ); ?>', handle<?php echo $event_name; ?>Event );
407
  }
408
 
420
  *
421
  * @see \WC_Facebookcommerce_Pixel::inject_event() for the preferred method to inject an event.
422
  *
423
+ * @param string $event_name Event name.
424
+ * @param array $params Event params.
425
+ * @param string $method Optional, defaults to 'track'.
426
  * @return string
427
  */
428
  public static function build_event( $event_name, $params, $method = 'track' ) {
429
 
430
+ // Do not send the event name in the params.
431
  if ( isset( $params['event_name'] ) ) {
432
 
433
  unset( $params['event_name'] );
434
  }
435
 
436
+ /**
437
+ * If possible, send the event ID to avoid duplication.
438
+ *
439
+ * @see https://developers.facebook.com/docs/marketing-api/server-side-api/deduplicate-pixel-and-server-side-events#deduplication-best-practices
440
+ */
441
  if ( isset( $params['event_id'] ) ) {
442
 
443
  $event_id = $params['event_id'];
444
  unset( $params['event_id'] );
445
  }
446
 
447
+ // If custom data is set, send only the custom data.
448
  if ( isset( $params['custom_data'] ) ) {
449
 
450
  $params = $params['custom_data'];
453
  if ( ! empty( $event_id ) ) {
454
  $event = sprintf(
455
  "/* %s Facebook Integration Event Tracking */\n" .
456
+ "fbq('set', 'agent', '%s', '%s');\n" .
457
  "fbq('%s', '%s', %s, %s);",
458
  WC_Facebookcommerce_Utils::getIntegrationName(),
459
  Event::get_platform_identifier(),
461
  esc_js( $method ),
462
  esc_js( $event_name ),
463
  json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
464
+ json_encode( array( 'eventID' => $event_id ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
465
  );
466
 
467
  } else {
468
 
469
  $event = sprintf(
470
  "/* %s Facebook Integration Event Tracking */\n" .
471
+ "fbq('set', 'agent', '%s', '%s');\n" .
472
  "fbq('%s', '%s', %s);",
473
  WC_Facebookcommerce_Utils::getIntegrationName(),
474
  Event::get_platform_identifier(),
490
  *
491
  * @since 1.10.2
492
  *
493
+ * @param array $params User defined parameters.
494
+ * @param string $event The event name the params are for.
495
  * @return array
496
  */
497
+ private static function build_params( $params = array(), $event = '' ) {
498
 
499
  $params = array_replace( Event::get_version_info(), $params );
500
 
503
  *
504
  * @since 1.10.2
505
  *
506
+ * @param array $params User defined parameters.
507
+ * @param string $event The event name.
508
  */
509
  return (array) apply_filters( 'wc_facebook_pixel_params', $params, $event );
510
  }
528
  *
529
  * @param array $custom_attributes
530
  */
531
+ $custom_attributes = (array) apply_filters( 'wc_facebook_pixel_script_attributes', array( 'type' => 'text/javascript' ) );
532
 
533
  foreach ( $custom_attributes as $tag => $value ) {
534
  $script_attributes .= ' ' . $tag . '="' . esc_attr( $value ) . '"';
537
  return $script_attributes;
538
  }
539
 
540
+ /**
541
+ * Get the PixelId.
542
+ */
543
  public static function get_pixel_id() {
544
  $fb_options = self::get_options();
545
  if ( ! $fb_options ) {
546
  return '';
547
  }
548
  return isset( $fb_options[ self::PIXEL_ID_KEY ] ) ?
549
+ $fb_options[ self::PIXEL_ID_KEY ] : '';
550
  }
551
 
552
+ /**
553
+ * Set the PixelId.
554
+ *
555
+ * @param string $pixel_id PixelId.
556
+ */
557
  public static function set_pixel_id( $pixel_id ) {
558
  $fb_options = self::get_options();
559
 
560
  if ( isset( $fb_options[ self::PIXEL_ID_KEY ] )
561
+ && $fb_options[ self::PIXEL_ID_KEY ] == $pixel_id ) {
562
  return;
563
  }
564
 
566
  update_option( self::SETTINGS_KEY, $fb_options );
567
  }
568
 
569
+ /**
570
+ * Check if PII key use is enabled.
571
+ */
572
  public static function get_use_pii_key() {
573
  $fb_options = self::get_options();
574
  if ( ! $fb_options ) {
575
  return null;
576
  }
577
  return isset( $fb_options[ self::USE_PII_KEY ] ) ?
578
+ $fb_options[ self::USE_PII_KEY ] : null;
579
  }
580
 
581
+ /**
582
+ * Enable or disable use of PII key.
583
+ *
584
+ * @param string $use_pii PII key.
585
+ */
586
  public static function set_use_pii_key( $use_pii ) {
587
  $fb_options = self::get_options();
588
 
589
  if ( isset( $fb_options[ self::USE_PII_KEY ] )
590
+ && $fb_options[ self::USE_PII_KEY ] == $use_pii ) {
591
  return;
592
  }
593
 
595
  update_option( self::SETTINGS_KEY, $fb_options );
596
  }
597
 
598
+ /**
599
+ * Check if S2S is set.
600
+ */
601
  public static function get_use_s2s() {
602
  $fb_options = self::get_options();
603
  if ( ! $fb_options ) {
604
  return false;
605
  }
606
  return isset( $fb_options[ self::USE_S2S_KEY ] ) ?
607
+ $fb_options[ self::USE_S2S_KEY ] : false;
608
  }
609
 
610
+ /**
611
+ * Enable or disable use of S2S key.
612
+ *
613
+ * @param string $use_s2s S2S setting.
614
+ */
615
  public static function set_use_s2s( $use_s2s ) {
616
  $fb_options = self::get_options();
617
 
618
  if ( isset( $fb_options[ self::USE_S2S_KEY ] )
619
+ && $fb_options[ self::USE_S2S_KEY ] == $use_s2s ) {
620
  return;
621
  }
622
 
624
  update_option( self::SETTINGS_KEY, $fb_options );
625
  }
626
 
627
+ /**
628
+ * Get access token.
629
+ */
630
  public static function get_access_token() {
631
  $fb_options = self::get_options();
632
  if ( ! $fb_options ) {
633
  return '';
634
  }
635
  return isset( $fb_options[ self::ACCESS_TOKEN_KEY ] ) ?
636
+ $fb_options[ self::ACCESS_TOKEN_KEY ] : '';
637
  }
638
 
639
+ /**
640
+ * Set access token.
641
+ *
642
+ * @param string $access_token Access token.
643
+ */
644
  public static function set_access_token( $access_token ) {
645
  $fb_options = self::get_options();
646
 
647
  if ( isset( $fb_options[ self::ACCESS_TOKEN_KEY ] )
648
+ && $fb_options[ self::ACCESS_TOKEN_KEY ] == $access_token ) {
649
  return;
650
  }
651
 
653
  update_option( self::SETTINGS_KEY, $fb_options );
654
  }
655
 
656
+ /**
657
+ * Get WooCommerce/Wordpress information.
658
+ */
659
  private static function get_version_info() {
660
  global $wp_version;
661
 
674
  );
675
  }
676
 
677
+ /**
678
+ * Get PixelID related settings.
679
+ */
680
  public static function get_options() {
681
  return get_option(
682
  self::SETTINGS_KEY,
683
  array(
684
+ self::PIXEL_ID_KEY => '0',
685
+ self::USE_PII_KEY => 0,
686
+ self::USE_S2S_KEY => false,
687
  self::ACCESS_TOKEN_KEY => '',
688
  )
689
  );
709
  *
710
  * @return string[]
711
  */
712
+ public function get_user_info() {
713
  return $this->user_info;
714
  }
715
  }
facebook-commerce.php CHANGED
@@ -27,7 +27,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
27
 
28
 
29
  /**
30
- * @var string the WordPress option name where the page access token is stored
 
 
31
  * @deprecated 2.1.0
32
  */
33
  const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token';
@@ -38,6 +40,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
38
  /** @var string the WordPress option name where the external merchant settings ID is stored */
39
  const OPTION_EXTERNAL_MERCHANT_SETTINGS_ID = 'wc_facebook_external_merchant_settings_id';
40
 
 
 
 
41
  /** @var string the WordPress option name where the feed ID is stored */
42
  const OPTION_FEED_ID = 'wc_facebook_feed_id';
43
 
@@ -136,7 +141,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
136
  /** Legacy properties *********************************************************************************************/
137
 
138
 
139
- // TODO probably some of these meta keys need to be moved to Facebook\Products {FN 2020-01-13}
140
  const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
141
  const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
142
  const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
@@ -155,7 +160,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
155
  // Number of days to query tip.
156
  const FB_TIP_QUERY = 1;
157
 
158
- // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}
159
  const FB_VARIANT_IMAGE = 'fb_image';
160
 
161
  const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
@@ -171,9 +176,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
171
  public function init_pixel() {
172
  WC_Facebookcommerce_Pixel::initialize();
173
 
174
- // Migrate WC customer pixel_id from WC settings to WP options.
175
- // This is part of a larger effort to consolidate all the FB-specific
176
- // settings for all plugin integrations.
 
 
177
  if ( is_admin() ) {
178
 
179
  $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
@@ -188,8 +195,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
188
  WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
189
  }
190
 
191
- // migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
192
- // so that it works the same way the pixel ID does
 
 
193
  $settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
194
  WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
195
 
@@ -197,14 +206,13 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
197
  WC_Facebookcommerce_Pixel::set_use_s2s( $settings_use_s2s );
198
 
199
  $settings_access_token = $this->get_access_token();
200
- WC_Facebookcommerce_Pixel::set_access_token($settings_access_token);
201
  }
202
  }
203
 
204
  /**
205
  * Init and hook in the integration.
206
  *
207
- * @access public
208
  * @return void
209
  */
210
  public function __construct() {
@@ -215,11 +223,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
215
  $this->id = WC_Facebookcommerce::INTEGRATION_ID;
216
  $this->method_title = __(
217
  'Facebook for WooCommerce',
218
- 'facebook-for-commerce'
219
  );
220
  $this->method_description = __(
221
  'Facebook Commerce and Dynamic Ads (Pixel) Extension',
222
- 'facebook-for-commerce'
223
  );
224
 
225
  // Load the settings.
@@ -227,25 +235,24 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
227
 
228
  $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
229
 
230
- // if there is a pixel option saved and no integration setting saved, inherit the pixel option
231
  if ( $pixel_id && ! $this->get_facebook_pixel_id() ) {
232
  $this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_id;
233
  }
234
 
235
  $advanced_matching_enabled = WC_Facebookcommerce_Pixel::get_use_pii_key();
236
 
237
- // if Advanced Matching (use_pii) is enabled on the saved pixel option and not on the saved integration setting,
238
- // inherit the pixel option
239
  if ( $advanced_matching_enabled && ! $this->is_advanced_matching_enabled() ) {
240
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = $advanced_matching_enabled;
241
  }
242
 
243
- //For now, the values of use s2s and access token will be the ones returned from WC_Facebookcommerce_Pixel
244
- $use_s2s = WC_Facebookcommerce_Pixel::get_use_s2s();
245
- $this->settings[self::SETTING_USE_S2S] = $use_s2s;
246
 
247
- $access_token = WC_Facebookcommerce_Pixel::get_access_token();
248
- $this->settings[self::SETTING_ACCESS_TOKEN] = $access_token;
249
 
250
  if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
251
  include_once 'includes/fbutils.php';
@@ -260,7 +267,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
260
 
261
  WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
262
 
263
- // Hooks
264
  if ( is_admin() ) {
265
 
266
  $this->init_pixel();
@@ -340,30 +346,26 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
340
  self::FB_PRIORITY_MID
341
  );
342
 
343
- // don't duplicate product FBID meta
344
- add_filter( 'woocommerce_duplicate_product_exclude_meta', [ $this, 'fb_duplicate_product_reset_meta' ] );
345
 
346
- // add product processing hooks if the plugin is configured only
347
  if ( $this->is_configured() && $this->get_product_catalog_id() ) {
348
 
349
- // on_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information
350
- add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 40 );
351
 
352
  add_action(
353
  'woocommerce_product_quick_edit_save',
354
- array( $this, 'on_quick_and_bulk_edit_save' ),
355
- 10, // Action priority
356
- 1 // Args passed to on_quick_and_bulk_edit_save ('product')
357
  );
358
 
359
  add_action(
360
  'woocommerce_product_bulk_edit_save',
361
- array( $this, 'on_quick_and_bulk_edit_save' ),
362
- 10, // Action priority
363
- 1 // Args passed to on_quick_and_bulk_edit_save ('product')
364
  );
365
 
366
- add_action( 'before_delete_post', [ $this, 'on_product_delete' ] );
367
 
368
  add_action( 'add_meta_boxes', array( $this, 'fb_product_metabox' ), 10, 1 );
369
 
@@ -396,13 +398,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
396
 
397
  add_action(
398
  'wp_ajax_wpmelon_adv_bulk_edit',
399
- [ $this, 'ajax_woo_adv_bulk_edit_compat' ],
400
  self::FB_PRIORITY_MID
401
  );
402
 
403
- // used to remove the 'you need to resync' message
404
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
405
- if ( isset( $_GET['remove_sticky'] ) ) {
406
  $this->remove_sticky_message();
407
  }
408
  }
@@ -411,24 +412,26 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
411
  }
412
 
413
  // Must be outside of admin for cron to schedule correctly.
414
- add_action( 'sync_all_fb_products_using_feed', [ $this, 'handle_scheduled_resync_action' ], self::FB_PRIORITY_MID );
415
 
416
- // handle the special background feed generation action
417
- add_action( 'wc_facebook_generate_product_catalog_feed', [ $this, 'handle_generate_product_catalog_feed' ] );
418
 
419
  if ( $this->get_facebook_pixel_id() ) {
420
- $aam_settings = $this->load_aam_settings_of_pixel();
421
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
422
  $this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info, $aam_settings );
423
  }
424
 
425
- // initialize the messenger chat features
426
- $this->messenger_chat = new WC_Facebookcommerce_MessengerChat( [
427
- 'fb_page_id' => $this->get_facebook_page_id(),
428
- 'facebook_jssdk_version' => $this->get_js_sdk_version(),
429
- ] );
 
 
430
 
431
- // Product Set hooks
432
  add_action( 'fb_wc_product_set_sync', array( $this, 'create_or_update_product_set_item' ), 99, 2 );
433
  add_action( 'fb_wc_product_set_delete', array( $this, 'delete_product_set_item' ), 99 );
434
  }
@@ -442,41 +445,40 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
442
  */
443
  private function load_aam_settings_of_pixel() {
444
  $installed_pixel = $this->get_facebook_pixel_id();
445
- // If no pixel is installed, reading the DB is not needed
446
- if(!$installed_pixel ){
447
  return null;
448
  }
449
- $config_key = 'wc_facebook_aam_settings';
450
- $saved_value = get_transient( $config_key );
451
- $refresh_interval = 20*MINUTE_IN_SECONDS;
452
- $aam_settings = null;
453
- // If wc_facebook_aam_settings is present in the DB
454
- // it is converted into an AAMSettings object
455
- if( $saved_value !== false ){
456
- $cached_aam_settings = new AAMSettings(json_decode($saved_value, true));
457
  // This condition is added because
458
  // it is possible that the AAMSettings saved do not belong to the current
459
  // installed pixel
460
  // because the admin could have changed the connection to Facebook
461
- // during the refresh interval
462
- if($cached_aam_settings->get_pixel_id() == $installed_pixel){
463
  $aam_settings = $cached_aam_settings;
464
  }
465
  }
466
  // If the settings are not present or invalid
467
  // they are fetched from Facebook domain
468
- // and cached in WP database if they are not null
469
- if(!$aam_settings){
470
  $aam_settings = AAMSettings::build_from_pixel_id( $installed_pixel );
471
- if($aam_settings){
472
- set_transient($config_key, strval($aam_settings), $refresh_interval);
473
  }
474
  }
475
  return $aam_settings;
476
  }
477
 
478
  public function load_background_sync_process() {
479
- // Attempt to load background processing (Woo 3.x.x only)
480
  include_once 'includes/fbbackground.php';
481
  if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
482
  if ( ! isset( $this->background_processor ) ) {
@@ -534,7 +536,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
534
  * @internal
535
  * @deprecated since 1.10.0
536
  *
537
- * @param array $tabs array of tabs
538
  * @return array
539
  */
540
  public function fb_new_product_tab( $tabs ) {
@@ -563,7 +565,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
563
  * @internal
564
  * @deprecated since 1.10.0
565
  *
566
- * @param array $existing_columns array of columns and labels
567
  * @return array
568
  */
569
  public function fb_product_columns( $existing_columns ) {
@@ -580,7 +582,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
580
  * @internal
581
  * @deprecated since 1.10.0
582
  *
583
- * @param string $column name of the column to display
584
  */
585
  public function fb_render_product_columns( $column ) {
586
 
@@ -594,11 +596,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
594
  );
595
  wp_enqueue_script(
596
  'wc_facebook_metabox_jsx',
597
- plugins_url(
598
- '/assets/js/facebook-metabox.min.js',
599
- __FILE__
600
- ),
601
- [],
602
  \WC_Facebookcommerce::PLUGIN_VERSION
603
  );
604
  wp_localize_script(
@@ -639,7 +638,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
639
  ?>
640
 
641
  <?php echo esc_html__( 'Facebook ID:', 'facebook-for-woocommerce' ); ?> <a href="https://facebook.com/<?php echo esc_attr( $fb_product_group_id ); ?>"
642
- target="_blank"><?php echo esc_html( $fb_product_group_id ); ?></a>
643
 
644
  <?php if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) : ?>
645
 
@@ -651,7 +650,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
651
  <?php foreach ( $product_item_ids_by_variation_id as $variation_id => $product_item_id ) : ?>
652
 
653
  <?php echo esc_html( $variation_id ); ?>: <a href="https://facebook.com/<?php echo esc_attr( $product_item_id ); ?>"
654
- target="_blank"><?php echo esc_html( $product_item_id ); ?></a><br/>
655
 
656
  <?php endforeach; ?>
657
  </p>
@@ -697,8 +696,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
697
  */
698
  private function get_variation_product_item_ids( $product, $product_group_id ) {
699
 
700
- $product_item_ids_by_variation_id = [];
701
- $missing_product_item_ids = [];
702
 
703
  // get the product item IDs from meta data and build a list of variations that don't have a product item ID stored
704
  foreach ( $product->get_children() as $variation_id ) {
@@ -751,7 +750,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
751
  */
752
  private function find_variation_product_item_ids( $product_group_id ) {
753
 
754
- $product_item_ids = [];
755
 
756
  try {
757
 
@@ -761,7 +760,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
761
 
762
  $product_item_ids = array_merge( $product_item_ids, $response->get_ids() );
763
 
764
- // get up to two additional pages of results
765
  } while ( $response = facebook_for_woocommerce()->get_api()->next( $response, 2 ) );
766
 
767
  } catch ( Framework\SV_WC_API_Exception $e ) {
@@ -782,12 +781,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
782
  */
783
  public function get_product_count() {
784
 
785
- $args = [
786
  'post_type' => 'product',
787
  'post_status' => 'publish',
788
  'posts_per_page' => -1,
789
  'fields' => 'ids',
790
- ];
791
 
792
  $products = new WP_Query( $args );
793
 
@@ -803,23 +802,20 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
803
  public function load_assets() {
804
 
805
  $ajax_data = array(
806
- 'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
807
- );
808
  // load banner assets
809
  wp_enqueue_script(
810
  'wc_facebook_infobanner_jsx',
811
- plugins_url(
812
- '/assets/js/facebook-infobanner.min.js',
813
- __FILE__
814
- ),
815
- [],
816
  \WC_Facebookcommerce::PLUGIN_VERSION
817
  );
818
  wp_localize_script(
819
- 'wc_facebook_infobanner_jsx',
820
- 'wc_facebook_infobanner_jsx',
821
- $ajax_data
822
- );
823
 
824
  wp_enqueue_style(
825
  'wc_facebook_infobanner_css',
@@ -827,7 +823,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
827
  '/assets/css/facebook-infobanner.css',
828
  __FILE__
829
  ),
830
- [],
831
  \WC_Facebookcommerce::PLUGIN_VERSION
832
  );
833
 
@@ -855,7 +851,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
855
  baseCurrency:'<?php echo esc_js( WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); ?>',
856
  timezoneId: '<?php echo esc_js( date( 'Z' ) ); ?>',
857
  storeName: '<?php echo esc_js( WC_Facebookcommerce_Utils::get_store_name() ); ?>',
858
- version: '<?php echo esc_js( WC()->version ) ; ?>',
859
  php_version: '<?php echo PHP_VERSION; ?>',
860
  plugin_version: '<?php echo esc_js( WC_Facebookcommerce_Utils::PLUGIN_VERSION ); ?>'
861
  },
@@ -876,9 +872,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
876
  </script>
877
 
878
  <?php
879
- $ajax_data = [
880
  'nonce' => wp_create_nonce( 'wc_facebook_settings_jsx' ),
881
- ];
882
  wp_localize_script(
883
  'wc_facebook_settings_jsx',
884
  'wc_facebook_settings_jsx',
@@ -890,7 +886,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
890
  '/assets/css/facebook.css',
891
  __FILE__
892
  ),
893
- [],
894
  \WC_Facebookcommerce::PLUGIN_VERSION
895
  );
896
  }
@@ -909,7 +905,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
909
 
910
  $posted_products = Framework\SV_WC_Helper::get_posted_value( WC_Facebook_Product::FB_REMOVE_FROM_SYNC );
911
  if ( empty( $posted_products ) ) {
912
- return [];
913
  }
914
 
915
  return array_map( 'absint', explode( ',', $posted_products ) );
@@ -960,12 +956,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
960
 
961
  $this->delete_fb_product( $delete_product );
962
  }
963
-
964
  } else {
965
 
966
  if ( $sync_enabled ) {
967
 
968
- Products::enable_sync_for_products( [ $product ] );
969
  Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
970
 
971
  $this->save_product_settings( $product );
@@ -977,7 +972,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
977
  Admin::add_product_disabled_sync_notice();
978
  }
979
 
980
- Products::disable_sync_for_products( [ $product ] );
981
 
982
  if ( in_array( $wp_id, $products_to_delete_from_facebook, true ) ) {
983
 
@@ -997,17 +992,17 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
997
  case 'external':
998
  case 'composite':
999
  $this->on_simple_product_publish( $wp_id );
1000
- break;
1001
 
1002
  case 'variable':
1003
  $this->on_variable_product_publish( $wp_id );
1004
- break;
1005
 
1006
  case 'subscription':
1007
  case 'variable-subscription':
1008
  case 'bundle':
1009
  $this->on_product_publish( $wp_id );
1010
- break;
1011
  }
1012
  }
1013
  }
@@ -1033,8 +1028,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1033
  $woo_product->set_price( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) );
1034
  }
1035
 
1036
- if ( isset( $_POST[ 'fb_product_image_source' ] ) ) {
1037
- $product->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, sanitize_key( wp_unslash( $_POST[ 'fb_product_image_source' ] ) ) );
1038
  $product->save_meta_data();
1039
  }
1040
 
@@ -1061,10 +1056,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1061
 
1062
  /**
1063
  * bail if not enabled for sync, except if explicitly deleting from the metabox
 
1064
  * @see ajax_delete_fb_product()
1065
  */
1066
  if ( ( ! is_ajax() || ! isset( $_POST['action'] ) || 'ajax_delete_fb_product' !== $_POST['action'] )
1067
- && ! Products::published_product_should_be_synced( $product ) ) {
1068
 
1069
  return;
1070
  }
@@ -1091,11 +1087,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1091
  $retailer_id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
1092
 
1093
  // enqueue variation to be deleted in the background
1094
- facebook_for_woocommerce()->get_products_sync_handler()->delete_products( [ $retailer_id ] );
1095
 
1096
  } elseif ( $product->is_type( 'variable' ) ) {
1097
 
1098
- $retailer_ids = [];
1099
 
1100
  foreach ( $product->get_children() as $variation_id ) {
1101
 
@@ -1128,8 +1124,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1128
  *
1129
  * @internal
1130
  *
1131
- * @param string $new_status
1132
- * @param string $old_status
1133
  * @param \WP_post $post
1134
  */
1135
  public function fb_change_product_published_status( $new_status, $old_status, $post ) {
@@ -1227,7 +1223,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1227
  /**
1228
  * Syncs product to Facebook when saving a variable product.
1229
  *
1230
- * @param int $wp_id product post ID
1231
  * @param WC_Facebook_Product|null $woo_product product object
1232
  */
1233
  function on_variable_product_publish( $wp_id, $woo_product = null ) {
@@ -1261,7 +1257,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1261
  $this->create_product_group( $woo_product, $retailer_id, true );
1262
  }
1263
 
1264
- $variation_ids = [];
1265
 
1266
  // scheduled update for each variation that should be synced
1267
  foreach ( $woo_product->get_children() as $variation_id ) {
@@ -1280,7 +1276,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1280
  /**
1281
  * Syncs product to Facebook when saving a simple product.
1282
  *
1283
- * @param int $wp_id product post ID
1284
  * @param WC_Facebook_Product|null $woo_product product object
1285
  * @param WC_Facebook_Product|null $parent_product parent object
1286
  * @return int|mixed|void|null
@@ -1332,11 +1328,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1332
  WC_Facebookcommerce_Utils::fblog(
1333
  'Wrong! simple_product_publish called without group ID for
1334
  a variable product!',
1335
- [],
1336
  true
1337
  );
1338
  }
1339
-
1340
  } else {
1341
 
1342
  return $this->create_product_simple( $woo_product ); // new product
@@ -1514,9 +1509,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1514
  );
1515
  }
1516
 
1517
- $product_group_data = [
1518
  'variants' => $variants,
1519
- ];
1520
 
1521
  if ( $default_product_fbid ) {
1522
  $product_group_data['default_product_id'] = $default_product_fbid;
@@ -1586,7 +1581,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1586
  */
1587
  private function get_product_variation_attributes( $variation ) {
1588
 
1589
- $final_attributes = [];
1590
  $variation_attributes = $variation['attributes'];
1591
 
1592
  foreach ( $variation_attributes as $attribute_name => $attribute_value ) {
@@ -1732,10 +1727,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1732
 
1733
  if ( $this->is_configured() ) {
1734
 
1735
- $response = [
1736
  'connected' => true,
1737
  'status' => 'in progress',
1738
- ];
1739
 
1740
  if ( ! empty( $this->get_upload_id() ) ) {
1741
 
@@ -1757,10 +1752,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1757
 
1758
  } else {
1759
 
1760
- $response = [
1761
  'connected' => true,
1762
  'status' => 'error',
1763
- ];
1764
  }
1765
 
1766
  if ( 'complete' === $response['status'] ) {
@@ -1773,10 +1768,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1773
  )
1774
  );
1775
  }
1776
-
1777
  } else {
1778
 
1779
- $response = [ 'connected' => false ];
1780
  }
1781
 
1782
  printf( json_encode( $response ) );
@@ -1876,8 +1870,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1876
  * Deals with FB API responses, displays error if FB API returns error.
1877
  *
1878
  * @param WP_Error|array $result API response
1879
- * @param array|null $logdata additional data for logging
1880
- * @param int|null $wpid post ID
1881
  * @return array|null|void result if response is 200, null otherwise
1882
  */
1883
  function check_api_result( $result, $logdata = null, $wpid = null ) {
@@ -1919,7 +1913,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1919
  return $result;
1920
  }
1921
  }
1922
-
1923
  } else {
1924
 
1925
  $this->display_error_message_from_result( $result );
@@ -1927,10 +1920,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1927
 
1928
  WC_Facebookcommerce_Utils::log( $result );
1929
 
1930
- $data = [
1931
  'result' => $result,
1932
  'data' => $logdata,
1933
- ];
1934
  WC_Facebookcommerce_Utils::fblog(
1935
  'Non-200 error code from FB',
1936
  $data,
@@ -2031,22 +2024,22 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2031
  ob_start();
2032
 
2033
  // get up to 12 published posts that are products
2034
- $args = [
2035
  'post_type' => 'product',
2036
  'post_status' => 'publish',
2037
  'posts_per_page' => 12,
2038
  'fields' => 'ids',
2039
- ];
2040
 
2041
  $post_ids = get_posts( $args );
2042
- $items = [];
2043
 
2044
  foreach ( $post_ids as $post_id ) {
2045
 
2046
  $woo_product = new WC_Facebook_Product( $post_id );
2047
  $product_data = $woo_product->prepare_product();
2048
 
2049
- $feed_item = [
2050
  'title' => strip_tags( $product_data['name'] ),
2051
  'availability' => $woo_product->is_in_stock() ? 'in stock' :
2052
  'out of stock',
@@ -2056,7 +2049,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2056
  'brand' => Framework\SV_WC_Helper::str_truncate( wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() ), 100 ),
2057
  'link' => $product_data['url'],
2058
  'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
2059
- ];
2060
 
2061
  array_push( $items, $feed_item );
2062
  }
@@ -2066,7 +2059,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2066
 
2067
  ob_end_clean();
2068
 
2069
- return json_encode( [ $items ] );
2070
  }
2071
 
2072
  /**
@@ -2236,7 +2229,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2236
  $error_message
2237
  );
2238
 
2239
- wp_send_json_error( [ 'error' => $message ] );
2240
  }
2241
  }
2242
 
@@ -2471,7 +2464,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2471
 
2472
  if ( ! $this->fbproductfeed->sync_all_products_using_feed() ) {
2473
 
2474
- WC_Facebookcommerce_Utils::fblog( 'Sync all products using feed, curl failed', [], true );
2475
 
2476
  throw new Framework\SV_WC_Plugin_Exception( __( "We couldn't create the feed or upload the product information.", 'facebook-for-woocommerce' ) );
2477
  }
@@ -2830,7 +2823,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2830
  * @param int[] $category_ids the configured excluded product category IDs
2831
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2832
  */
2833
- return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, [] ), $this );
2834
  }
2835
 
2836
 
@@ -2851,7 +2844,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2851
  * @param int[] $tag_ids the configured excluded product tag IDs
2852
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2853
  */
2854
- return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, [] ), $this );
2855
  }
2856
 
2857
 
@@ -2874,10 +2867,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2874
  */
2875
  $mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
2876
 
2877
- $valid_modes = [
2878
  self::PRODUCT_DESCRIPTION_MODE_STANDARD,
2879
  self::PRODUCT_DESCRIPTION_MODE_SHORT,
2880
- ];
2881
 
2882
  if ( ! in_array( $mode, $valid_modes, true ) ) {
2883
  $mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
@@ -3179,6 +3172,26 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3179
  return (bool) apply_filters( 'wc_facebook_is_product_sync_enabled', 'yes' === get_option( self::SETTING_ENABLE_PRODUCT_SYNC, 'yes' ), $this );
3180
  }
3181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3182
 
3183
  /**
3184
  * Determines whether the scheduled re-sync is enabled.
@@ -3297,7 +3310,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3297
 
3298
  WC_Facebookcommerce_Utils::fblog(
3299
  $error_msg,
3300
- [],
3301
  true
3302
  );
3303
  }
@@ -3359,16 +3372,16 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3359
 
3360
  $response = facebook_for_woocommerce()->get_api()->get_page( $this->get_facebook_page_id() );
3361
 
3362
- $this->page = [
3363
  'name' => $response->get_name(),
3364
  'url' => $response->get_url(),
3365
- ];
3366
 
3367
  } catch ( Framework\SV_WC_API_Exception $e ) {
3368
 
3369
  // we intentionally set $this->page to an empty array if an error occurs to avoid additional API requests
3370
  // 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
3371
- $this->page = [];
3372
 
3373
  $message = sprintf( __( 'There was an error trying to retrieve information about the Facebook page: %s' ), $e->getMessage() );
3374
 
@@ -3376,7 +3389,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3376
  }
3377
  }
3378
 
3379
- return is_array( $this->page ) ? $this->page : [];
3380
  }
3381
 
3382
 
@@ -3415,15 +3428,15 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3415
  */
3416
  function get_nux_message_ifexist() {
3417
 
3418
- $nux_type_to_elemid_map = [
3419
  'messenger_chat' => 'connect_button',
3420
  'instagram_shopping' => 'connect_button',
3421
- ];
3422
 
3423
- $nux_type_to_message_map = [
3424
  'messenger_chat' => __( 'Get started with Messenger Customer Chat' ),
3425
  'instagram_shopping' => __( 'Get started with Instagram Shopping' ),
3426
- ];
3427
 
3428
  $message = '';
3429
 
@@ -3438,7 +3451,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3438
  ?>
3439
 
3440
  <div class="nux-message" style="display: none;"
3441
- data-target="<?php echo esc_attr( $nux_type_to_elemid_map[ $nux_type ] ); ?>">
3442
  <div class="nux-message-text">
3443
  <?php echo esc_attr( $nux_type_to_message_map[ $nux_type ] ); ?>
3444
  </div>
@@ -3518,7 +3531,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3518
  * Helper function to update FB visibility.
3519
  *
3520
  * @param int|\WC_Product $product_id product ID or product object
3521
- * @param string $visibility visibility
3522
  */
3523
  function update_fb_visibility( $product_id, $visibility ) {
3524
 
@@ -3540,7 +3553,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3540
 
3541
  Products::set_product_visibility( $product, $should_set_visible );
3542
 
3543
- facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( [ $product->get_id() ] );
3544
 
3545
  } elseif ( $product->is_type( 'variable' ) ) {
3546
 
@@ -3550,7 +3563,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3550
  // we should not add the parent product ID to the array of product IDs to be
3551
  // updated because product groups, which are used to represent the parent product
3552
  // for variable products, don't have the visibility property on Facebook
3553
- $product_ids = [];
3554
 
3555
  // set visibility for all children
3556
  foreach ( $product->get_children() as $index => $id ) {
@@ -3558,7 +3571,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3558
  $product = wc_get_product( $id );
3559
 
3560
  if ( ! $product instanceof \WC_Product ) {
3561
- continue;
3562
  }
3563
 
3564
  Products::set_product_visibility( $product, $should_set_visible );
@@ -3574,11 +3587,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3574
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $product_id );
3575
 
3576
  if ( ! $fb_product_item_id ) {
3577
- \WC_Facebookcommerce_Utils::fblog( $fb_product_item_id . " doesn't exist but underwent a visibility transform.", [], true );
3578
  return;
3579
  }
3580
 
3581
- $set_visibility = $this->fbgraph->update_product_item( $fb_product_item_id, [ 'visibility' => $visibility ] );
3582
 
3583
  if ( $this->check_api_result( $set_visibility ) ) {
3584
  Products::set_product_visibility( $product, $should_set_visible );
@@ -3618,8 +3631,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3618
  /**
3619
  * Gets Facebook product ID from meta or from Facebook API.
3620
  *
3621
- * @param string $fbid_type ID type (group or item)
3622
- * @param int $wp_id post ID
3623
  * @param WC_Facebook_Product|null $woo_product product
3624
  * @return mixed|void|null
3625
  */
@@ -3782,7 +3795,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3782
  $timestamp = $first_scheduled_time >= $current_time ? $first_scheduled_time->getTimestamp() : $next_scheduled_time->getTimestamp();
3783
 
3784
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3785
- as_schedule_single_action( $timestamp, self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' );
3786
  }
3787
 
3788
 
@@ -3794,7 +3807,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3794
  private function unschedule_resync() {
3795
 
3796
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3797
- as_unschedule_all_actions( self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' );
3798
  }
3799
 
3800
 
@@ -3810,7 +3823,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3810
  public function is_resync_scheduled() {
3811
 
3812
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3813
- return is_int( as_next_scheduled_action( self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' ) );
3814
  }
3815
 
3816
 
@@ -3829,7 +3842,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3829
 
3830
  try {
3831
  $this->sync_facebook_products_using_feed();
3832
- } catch ( Framework\SV_WC_Plugin_Exception $e ) {}
 
3833
 
3834
  $resync_offset = $this->get_scheduled_resync_offset();
3835
 
27
 
28
 
29
  /**
30
+ * The WordPress option name where the page access token is stored.
31
+ *
32
+ * @var string option name.
33
  * @deprecated 2.1.0
34
  */
35
  const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token';
40
  /** @var string the WordPress option name where the external merchant settings ID is stored */
41
  const OPTION_EXTERNAL_MERCHANT_SETTINGS_ID = 'wc_facebook_external_merchant_settings_id';
42
 
43
+ /** @var string Option name for disabling feed. */
44
+ const OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED = 'wc_facebook_legacy_feed_file_generation_enabled';
45
+
46
  /** @var string the WordPress option name where the feed ID is stored */
47
  const OPTION_FEED_ID = 'wc_facebook_feed_id';
48
 
141
  /** Legacy properties *********************************************************************************************/
142
 
143
 
144
+ // TODO probably some of these meta keys need to be moved to Facebook\Products {FN 2020-01-13}.
145
  const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
146
  const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
147
  const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
160
  // Number of days to query tip.
161
  const FB_TIP_QUERY = 1;
162
 
163
+ // TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}.
164
  const FB_VARIANT_IMAGE = 'fb_image';
165
 
166
  const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
176
  public function init_pixel() {
177
  WC_Facebookcommerce_Pixel::initialize();
178
 
179
+ /**
180
+ * Migrate WC customer pixel_id from WC settings to WP options.
181
+ * This is part of a larger effort to consolidate all the FB-specific
182
+ * settings for all plugin integrations.
183
+ */
184
  if ( is_admin() ) {
185
 
186
  $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
195
  WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
196
  }
197
 
198
+ /**
199
+ * Migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
200
+ * so that it works the same way the pixel ID does
201
+ */
202
  $settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
203
  WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
204
 
206
  WC_Facebookcommerce_Pixel::set_use_s2s( $settings_use_s2s );
207
 
208
  $settings_access_token = $this->get_access_token();
209
+ WC_Facebookcommerce_Pixel::set_access_token( $settings_access_token );
210
  }
211
  }
212
 
213
  /**
214
  * Init and hook in the integration.
215
  *
 
216
  * @return void
217
  */
218
  public function __construct() {
223
  $this->id = WC_Facebookcommerce::INTEGRATION_ID;
224
  $this->method_title = __(
225
  'Facebook for WooCommerce',
226
+ 'facebook-for-woocommerce'
227
  );
228
  $this->method_description = __(
229
  'Facebook Commerce and Dynamic Ads (Pixel) Extension',
230
+ 'facebook-for-woocommerce'
231
  );
232
 
233
  // Load the settings.
235
 
236
  $pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
237
 
238
+ // If there is a pixel option saved and no integration setting saved, inherit the pixel option.
239
  if ( $pixel_id && ! $this->get_facebook_pixel_id() ) {
240
  $this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_id;
241
  }
242
 
243
  $advanced_matching_enabled = WC_Facebookcommerce_Pixel::get_use_pii_key();
244
 
245
+ // If Advanced Matching (use_pii) is enabled on the saved pixel option and not on the saved integration setting, inherit the pixel option.
 
246
  if ( $advanced_matching_enabled && ! $this->is_advanced_matching_enabled() ) {
247
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = $advanced_matching_enabled;
248
  }
249
 
250
+ // For now, the values of use s2s and access token will be the ones returned from WC_Facebookcommerce_Pixel.
251
+ $use_s2s = WC_Facebookcommerce_Pixel::get_use_s2s();
252
+ $this->settings[ self::SETTING_USE_S2S ] = $use_s2s;
253
 
254
+ $access_token = WC_Facebookcommerce_Pixel::get_access_token();
255
+ $this->settings[ self::SETTING_ACCESS_TOKEN ] = $access_token;
256
 
257
  if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
258
  include_once 'includes/fbutils.php';
267
 
268
  WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
269
 
 
270
  if ( is_admin() ) {
271
 
272
  $this->init_pixel();
346
  self::FB_PRIORITY_MID
347
  );
348
 
349
+ // Don't duplicate product FBID meta.
350
+ add_filter( 'woocommerce_duplicate_product_exclude_meta', array( $this, 'fb_duplicate_product_reset_meta' ) );
351
 
352
+ // Add product processing hooks if the plugin is configured only.
353
  if ( $this->is_configured() && $this->get_product_catalog_id() ) {
354
 
355
+ // On_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information.
356
+ add_action( 'woocommerce_process_product_meta', array( $this, 'on_product_save' ), 40 );
357
 
358
  add_action(
359
  'woocommerce_product_quick_edit_save',
360
+ array( $this, 'on_quick_and_bulk_edit_save' )
 
 
361
  );
362
 
363
  add_action(
364
  'woocommerce_product_bulk_edit_save',
365
+ array( $this, 'on_quick_and_bulk_edit_save' )
 
 
366
  );
367
 
368
+ add_action( 'before_delete_post', array( $this, 'on_product_delete' ) );
369
 
370
  add_action( 'add_meta_boxes', array( $this, 'fb_product_metabox' ), 10, 1 );
371
 
398
 
399
  add_action(
400
  'wp_ajax_wpmelon_adv_bulk_edit',
401
+ array( $this, 'ajax_woo_adv_bulk_edit_compat' ),
402
  self::FB_PRIORITY_MID
403
  );
404
 
405
+ // Used to remove the 'you need to resync' message.
406
+ if ( isset( $_GET['remove_sticky'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 
407
  $this->remove_sticky_message();
408
  }
409
  }
412
  }
413
 
414
  // Must be outside of admin for cron to schedule correctly.
415
+ add_action( 'sync_all_fb_products_using_feed', array( $this, 'handle_scheduled_resync_action' ), self::FB_PRIORITY_MID );
416
 
417
+ // Handle the special background feed generation action.
418
+ add_action( 'wc_facebook_generate_product_catalog_feed', array( $this, 'handle_generate_product_catalog_feed' ) );
419
 
420
  if ( $this->get_facebook_pixel_id() ) {
421
+ $aam_settings = $this->load_aam_settings_of_pixel();
422
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
423
  $this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info, $aam_settings );
424
  }
425
 
426
+ // Initialize the messenger chat features.
427
+ $this->messenger_chat = new WC_Facebookcommerce_MessengerChat(
428
+ array(
429
+ 'fb_page_id' => $this->get_facebook_page_id(),
430
+ 'facebook_jssdk_version' => $this->get_js_sdk_version(),
431
+ )
432
+ );
433
 
434
+ // Product Set hooks.
435
  add_action( 'fb_wc_product_set_sync', array( $this, 'create_or_update_product_set_item' ), 99, 2 );
436
  add_action( 'fb_wc_product_set_delete', array( $this, 'delete_product_set_item' ), 99 );
437
  }
445
  */
446
  private function load_aam_settings_of_pixel() {
447
  $installed_pixel = $this->get_facebook_pixel_id();
448
+ // If no pixel is installed, reading the DB is not needed.
449
+ if ( ! $installed_pixel ) {
450
  return null;
451
  }
452
+ $config_key = 'wc_facebook_aam_settings';
453
+ $saved_value = get_transient( $config_key );
454
+ $refresh_interval = 20 * MINUTE_IN_SECONDS;
455
+ $aam_settings = null;
456
+ // If wc_facebook_aam_settings is present in the DB it is converted into an AAMSettings object.
457
+ if ( $saved_value !== false ) {
458
+ $cached_aam_settings = new AAMSettings( json_decode( $saved_value, true ) );
 
459
  // This condition is added because
460
  // it is possible that the AAMSettings saved do not belong to the current
461
  // installed pixel
462
  // because the admin could have changed the connection to Facebook
463
+ // during the refresh interval.
464
+ if ( $cached_aam_settings->get_pixel_id() == $installed_pixel ) {
465
  $aam_settings = $cached_aam_settings;
466
  }
467
  }
468
  // If the settings are not present or invalid
469
  // they are fetched from Facebook domain
470
+ // and cached in WP database if they are not null.
471
+ if ( ! $aam_settings ) {
472
  $aam_settings = AAMSettings::build_from_pixel_id( $installed_pixel );
473
+ if ( $aam_settings ) {
474
+ set_transient( $config_key, strval( $aam_settings ), $refresh_interval );
475
  }
476
  }
477
  return $aam_settings;
478
  }
479
 
480
  public function load_background_sync_process() {
481
+ // Attempt to load background processing (Woo 3.x.x only).
482
  include_once 'includes/fbbackground.php';
483
  if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
484
  if ( ! isset( $this->background_processor ) ) {
536
  * @internal
537
  * @deprecated since 1.10.0
538
  *
539
+ * @param array $tabs Array of tabs.
540
  * @return array
541
  */
542
  public function fb_new_product_tab( $tabs ) {
565
  * @internal
566
  * @deprecated since 1.10.0
567
  *
568
+ * @param array $existing_columns Array of columns and labels.
569
  * @return array
570
  */
571
  public function fb_product_columns( $existing_columns ) {
582
  * @internal
583
  * @deprecated since 1.10.0
584
  *
585
+ * @param string $column Name of the column to display.
586
  */
587
  public function fb_render_product_columns( $column ) {
588
 
596
  );
597
  wp_enqueue_script(
598
  'wc_facebook_metabox_jsx',
599
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/metabox.js',
600
+ array(),
 
 
 
601
  \WC_Facebookcommerce::PLUGIN_VERSION
602
  );
603
  wp_localize_script(
638
  ?>
639
 
640
  <?php echo esc_html__( 'Facebook ID:', 'facebook-for-woocommerce' ); ?> <a href="https://facebook.com/<?php echo esc_attr( $fb_product_group_id ); ?>"
641
+ target="_blank"><?php echo esc_html( $fb_product_group_id ); ?></a>
642
 
643
  <?php if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) : ?>
644
 
650
  <?php foreach ( $product_item_ids_by_variation_id as $variation_id => $product_item_id ) : ?>
651
 
652
  <?php echo esc_html( $variation_id ); ?>: <a href="https://facebook.com/<?php echo esc_attr( $product_item_id ); ?>"
653
+ target="_blank"><?php echo esc_html( $product_item_id ); ?></a><br/>
654
 
655
  <?php endforeach; ?>
656
  </p>
696
  */
697
  private function get_variation_product_item_ids( $product, $product_group_id ) {
698
 
699
+ $product_item_ids_by_variation_id = array();
700
+ $missing_product_item_ids = array();
701
 
702
  // get the product item IDs from meta data and build a list of variations that don't have a product item ID stored
703
  foreach ( $product->get_children() as $variation_id ) {
750
  */
751
  private function find_variation_product_item_ids( $product_group_id ) {
752
 
753
+ $product_item_ids = array();
754
 
755
  try {
756
 
760
 
761
  $product_item_ids = array_merge( $product_item_ids, $response->get_ids() );
762
 
763
+ // get up to two additional pages of results
764
  } while ( $response = facebook_for_woocommerce()->get_api()->next( $response, 2 ) );
765
 
766
  } catch ( Framework\SV_WC_API_Exception $e ) {
781
  */
782
  public function get_product_count() {
783
 
784
+ $args = array(
785
  'post_type' => 'product',
786
  'post_status' => 'publish',
787
  'posts_per_page' => -1,
788
  'fields' => 'ids',
789
+ );
790
 
791
  $products = new WP_Query( $args );
792
 
802
  public function load_assets() {
803
 
804
  $ajax_data = array(
805
+ 'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
806
+ );
807
  // load banner assets
808
  wp_enqueue_script(
809
  'wc_facebook_infobanner_jsx',
810
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/infobanner.js',
811
+ array(),
 
 
 
812
  \WC_Facebookcommerce::PLUGIN_VERSION
813
  );
814
  wp_localize_script(
815
+ 'wc_facebook_infobanner_jsx',
816
+ 'wc_facebook_infobanner_jsx',
817
+ $ajax_data
818
+ );
819
 
820
  wp_enqueue_style(
821
  'wc_facebook_infobanner_css',
823
  '/assets/css/facebook-infobanner.css',
824
  __FILE__
825
  ),
826
+ array(),
827
  \WC_Facebookcommerce::PLUGIN_VERSION
828
  );
829
 
851
  baseCurrency:'<?php echo esc_js( WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); ?>',
852
  timezoneId: '<?php echo esc_js( date( 'Z' ) ); ?>',
853
  storeName: '<?php echo esc_js( WC_Facebookcommerce_Utils::get_store_name() ); ?>',
854
+ version: '<?php echo esc_js( WC()->version ); ?>',
855
  php_version: '<?php echo PHP_VERSION; ?>',
856
  plugin_version: '<?php echo esc_js( WC_Facebookcommerce_Utils::PLUGIN_VERSION ); ?>'
857
  },
872
  </script>
873
 
874
  <?php
875
+ $ajax_data = array(
876
  'nonce' => wp_create_nonce( 'wc_facebook_settings_jsx' ),
877
+ );
878
  wp_localize_script(
879
  'wc_facebook_settings_jsx',
880
  'wc_facebook_settings_jsx',
886
  '/assets/css/facebook.css',
887
  __FILE__
888
  ),
889
+ array(),
890
  \WC_Facebookcommerce::PLUGIN_VERSION
891
  );
892
  }
905
 
906
  $posted_products = Framework\SV_WC_Helper::get_posted_value( WC_Facebook_Product::FB_REMOVE_FROM_SYNC );
907
  if ( empty( $posted_products ) ) {
908
+ return array();
909
  }
910
 
911
  return array_map( 'absint', explode( ',', $posted_products ) );
956
 
957
  $this->delete_fb_product( $delete_product );
958
  }
 
959
  } else {
960
 
961
  if ( $sync_enabled ) {
962
 
963
+ Products::enable_sync_for_products( array( $product ) );
964
  Products::set_product_visibility( $product, Admin::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
965
 
966
  $this->save_product_settings( $product );
972
  Admin::add_product_disabled_sync_notice();
973
  }
974
 
975
+ Products::disable_sync_for_products( array( $product ) );
976
 
977
  if ( in_array( $wp_id, $products_to_delete_from_facebook, true ) ) {
978
 
992
  case 'external':
993
  case 'composite':
994
  $this->on_simple_product_publish( $wp_id );
995
+ break;
996
 
997
  case 'variable':
998
  $this->on_variable_product_publish( $wp_id );
999
+ break;
1000
 
1001
  case 'subscription':
1002
  case 'variable-subscription':
1003
  case 'bundle':
1004
  $this->on_product_publish( $wp_id );
1005
+ break;
1006
  }
1007
  }
1008
  }
1028
  $woo_product->set_price( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) );
1029
  }
1030
 
1031
+ if ( isset( $_POST['fb_product_image_source'] ) ) {
1032
+ $product->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, sanitize_key( wp_unslash( $_POST['fb_product_image_source'] ) ) );
1033
  $product->save_meta_data();
1034
  }
1035
 
1056
 
1057
  /**
1058
  * bail if not enabled for sync, except if explicitly deleting from the metabox
1059
+ *
1060
  * @see ajax_delete_fb_product()
1061
  */
1062
  if ( ( ! is_ajax() || ! isset( $_POST['action'] ) || 'ajax_delete_fb_product' !== $_POST['action'] )
1063
+ && ! Products::published_product_should_be_synced( $product ) ) {
1064
 
1065
  return;
1066
  }
1087
  $retailer_id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
1088
 
1089
  // enqueue variation to be deleted in the background
1090
+ facebook_for_woocommerce()->get_products_sync_handler()->delete_products( array( $retailer_id ) );
1091
 
1092
  } elseif ( $product->is_type( 'variable' ) ) {
1093
 
1094
+ $retailer_ids = array();
1095
 
1096
  foreach ( $product->get_children() as $variation_id ) {
1097
 
1124
  *
1125
  * @internal
1126
  *
1127
+ * @param string $new_status
1128
+ * @param string $old_status
1129
  * @param \WP_post $post
1130
  */
1131
  public function fb_change_product_published_status( $new_status, $old_status, $post ) {
1223
  /**
1224
  * Syncs product to Facebook when saving a variable product.
1225
  *
1226
+ * @param int $wp_id product post ID
1227
  * @param WC_Facebook_Product|null $woo_product product object
1228
  */
1229
  function on_variable_product_publish( $wp_id, $woo_product = null ) {
1257
  $this->create_product_group( $woo_product, $retailer_id, true );
1258
  }
1259
 
1260
+ $variation_ids = array();
1261
 
1262
  // scheduled update for each variation that should be synced
1263
  foreach ( $woo_product->get_children() as $variation_id ) {
1276
  /**
1277
  * Syncs product to Facebook when saving a simple product.
1278
  *
1279
+ * @param int $wp_id product post ID
1280
  * @param WC_Facebook_Product|null $woo_product product object
1281
  * @param WC_Facebook_Product|null $parent_product parent object
1282
  * @return int|mixed|void|null
1328
  WC_Facebookcommerce_Utils::fblog(
1329
  'Wrong! simple_product_publish called without group ID for
1330
  a variable product!',
1331
+ array(),
1332
  true
1333
  );
1334
  }
 
1335
  } else {
1336
 
1337
  return $this->create_product_simple( $woo_product ); // new product
1509
  );
1510
  }
1511
 
1512
+ $product_group_data = array(
1513
  'variants' => $variants,
1514
+ );
1515
 
1516
  if ( $default_product_fbid ) {
1517
  $product_group_data['default_product_id'] = $default_product_fbid;
1581
  */
1582
  private function get_product_variation_attributes( $variation ) {
1583
 
1584
+ $final_attributes = array();
1585
  $variation_attributes = $variation['attributes'];
1586
 
1587
  foreach ( $variation_attributes as $attribute_name => $attribute_value ) {
1727
 
1728
  if ( $this->is_configured() ) {
1729
 
1730
+ $response = array(
1731
  'connected' => true,
1732
  'status' => 'in progress',
1733
+ );
1734
 
1735
  if ( ! empty( $this->get_upload_id() ) ) {
1736
 
1752
 
1753
  } else {
1754
 
1755
+ $response = array(
1756
  'connected' => true,
1757
  'status' => 'error',
1758
+ );
1759
  }
1760
 
1761
  if ( 'complete' === $response['status'] ) {
1768
  )
1769
  );
1770
  }
 
1771
  } else {
1772
 
1773
+ $response = array( 'connected' => false );
1774
  }
1775
 
1776
  printf( json_encode( $response ) );
1870
  * Deals with FB API responses, displays error if FB API returns error.
1871
  *
1872
  * @param WP_Error|array $result API response
1873
+ * @param array|null $logdata additional data for logging
1874
+ * @param int|null $wpid post ID
1875
  * @return array|null|void result if response is 200, null otherwise
1876
  */
1877
  function check_api_result( $result, $logdata = null, $wpid = null ) {
1913
  return $result;
1914
  }
1915
  }
 
1916
  } else {
1917
 
1918
  $this->display_error_message_from_result( $result );
1920
 
1921
  WC_Facebookcommerce_Utils::log( $result );
1922
 
1923
+ $data = array(
1924
  'result' => $result,
1925
  'data' => $logdata,
1926
+ );
1927
  WC_Facebookcommerce_Utils::fblog(
1928
  'Non-200 error code from FB',
1929
  $data,
2024
  ob_start();
2025
 
2026
  // get up to 12 published posts that are products
2027
+ $args = array(
2028
  'post_type' => 'product',
2029
  'post_status' => 'publish',
2030
  'posts_per_page' => 12,
2031
  'fields' => 'ids',
2032
+ );
2033
 
2034
  $post_ids = get_posts( $args );
2035
+ $items = array();
2036
 
2037
  foreach ( $post_ids as $post_id ) {
2038
 
2039
  $woo_product = new WC_Facebook_Product( $post_id );
2040
  $product_data = $woo_product->prepare_product();
2041
 
2042
+ $feed_item = array(
2043
  'title' => strip_tags( $product_data['name'] ),
2044
  'availability' => $woo_product->is_in_stock() ? 'in stock' :
2045
  'out of stock',
2049
  'brand' => Framework\SV_WC_Helper::str_truncate( wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() ), 100 ),
2050
  'link' => $product_data['url'],
2051
  'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
2052
+ );
2053
 
2054
  array_push( $items, $feed_item );
2055
  }
2059
 
2060
  ob_end_clean();
2061
 
2062
+ return json_encode( array( $items ) );
2063
  }
2064
 
2065
  /**
2229
  $error_message
2230
  );
2231
 
2232
+ wp_send_json_error( array( 'error' => $message ) );
2233
  }
2234
  }
2235
 
2464
 
2465
  if ( ! $this->fbproductfeed->sync_all_products_using_feed() ) {
2466
 
2467
+ WC_Facebookcommerce_Utils::fblog( 'Sync all products using feed, curl failed', array(), true );
2468
 
2469
  throw new Framework\SV_WC_Plugin_Exception( __( "We couldn't create the feed or upload the product information.", 'facebook-for-woocommerce' ) );
2470
  }
2823
  * @param int[] $category_ids the configured excluded product category IDs
2824
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2825
  */
2826
+ return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, array() ), $this );
2827
  }
2828
 
2829
 
2844
  * @param int[] $tag_ids the configured excluded product tag IDs
2845
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
2846
  */
2847
+ return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, array() ), $this );
2848
  }
2849
 
2850
 
2867
  */
2868
  $mode = (string) apply_filters( 'wc_facebook_product_description_mode', get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
2869
 
2870
+ $valid_modes = array(
2871
  self::PRODUCT_DESCRIPTION_MODE_STANDARD,
2872
  self::PRODUCT_DESCRIPTION_MODE_SHORT,
2873
+ );
2874
 
2875
  if ( ! in_array( $mode, $valid_modes, true ) ) {
2876
  $mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
3172
  return (bool) apply_filters( 'wc_facebook_is_product_sync_enabled', 'yes' === get_option( self::SETTING_ENABLE_PRODUCT_SYNC, 'yes' ), $this );
3173
  }
3174
 
3175
+ /**
3176
+ * Return true if (legacy) feed generation is enabled.
3177
+ *
3178
+ * Feed generation for product sync is enabled by default, and generally recommended.
3179
+ * Large stores, or stores running on shared hosting (low resources) may have issues
3180
+ * with feed generation. This option allows those stores to disable generation to
3181
+ * work around the issue.
3182
+ *
3183
+ * Note - this is temporary. In a future release, an improved feed system will be
3184
+ * implemented, which should work well for all stores. This option will not disable
3185
+ * the new improved implementation.
3186
+ *
3187
+ * @since 2.5.0
3188
+ *
3189
+ * @return bool
3190
+ */
3191
+ public function is_legacy_feed_file_generation_enabled() {
3192
+ return (bool) ( 'yes' === get_option( self::OPTION_LEGACY_FEED_FILE_GENERATION_ENABLED, 'yes' ) );
3193
+ }
3194
+
3195
 
3196
  /**
3197
  * Determines whether the scheduled re-sync is enabled.
3310
 
3311
  WC_Facebookcommerce_Utils::fblog(
3312
  $error_msg,
3313
+ array(),
3314
  true
3315
  );
3316
  }
3372
 
3373
  $response = facebook_for_woocommerce()->get_api()->get_page( $this->get_facebook_page_id() );
3374
 
3375
+ $this->page = array(
3376
  'name' => $response->get_name(),
3377
  'url' => $response->get_url(),
3378
+ );
3379
 
3380
  } catch ( Framework\SV_WC_API_Exception $e ) {
3381
 
3382
  // we intentionally set $this->page to an empty array if an error occurs to avoid additional API requests
3383
  // 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
3384
+ $this->page = array();
3385
 
3386
  $message = sprintf( __( 'There was an error trying to retrieve information about the Facebook page: %s' ), $e->getMessage() );
3387
 
3389
  }
3390
  }
3391
 
3392
+ return is_array( $this->page ) ? $this->page : array();
3393
  }
3394
 
3395
 
3428
  */
3429
  function get_nux_message_ifexist() {
3430
 
3431
+ $nux_type_to_elemid_map = array(
3432
  'messenger_chat' => 'connect_button',
3433
  'instagram_shopping' => 'connect_button',
3434
+ );
3435
 
3436
+ $nux_type_to_message_map = array(
3437
  'messenger_chat' => __( 'Get started with Messenger Customer Chat' ),
3438
  'instagram_shopping' => __( 'Get started with Instagram Shopping' ),
3439
+ );
3440
 
3441
  $message = '';
3442
 
3451
  ?>
3452
 
3453
  <div class="nux-message" style="display: none;"
3454
+ data-target="<?php echo esc_attr( $nux_type_to_elemid_map[ $nux_type ] ); ?>">
3455
  <div class="nux-message-text">
3456
  <?php echo esc_attr( $nux_type_to_message_map[ $nux_type ] ); ?>
3457
  </div>
3531
  * Helper function to update FB visibility.
3532
  *
3533
  * @param int|\WC_Product $product_id product ID or product object
3534
+ * @param string $visibility visibility
3535
  */
3536
  function update_fb_visibility( $product_id, $visibility ) {
3537
 
3553
 
3554
  Products::set_product_visibility( $product, $should_set_visible );
3555
 
3556
+ facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( array( $product->get_id() ) );
3557
 
3558
  } elseif ( $product->is_type( 'variable' ) ) {
3559
 
3563
  // we should not add the parent product ID to the array of product IDs to be
3564
  // updated because product groups, which are used to represent the parent product
3565
  // for variable products, don't have the visibility property on Facebook
3566
+ $product_ids = array();
3567
 
3568
  // set visibility for all children
3569
  foreach ( $product->get_children() as $index => $id ) {
3571
  $product = wc_get_product( $id );
3572
 
3573
  if ( ! $product instanceof \WC_Product ) {
3574
+ continue;
3575
  }
3576
 
3577
  Products::set_product_visibility( $product, $should_set_visible );
3587
  $fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $product_id );
3588
 
3589
  if ( ! $fb_product_item_id ) {
3590
+ \WC_Facebookcommerce_Utils::fblog( $fb_product_item_id . " doesn't exist but underwent a visibility transform.", array(), true );
3591
  return;
3592
  }
3593
 
3594
+ $set_visibility = $this->fbgraph->update_product_item( $fb_product_item_id, array( 'visibility' => $visibility ) );
3595
 
3596
  if ( $this->check_api_result( $set_visibility ) ) {
3597
  Products::set_product_visibility( $product, $should_set_visible );
3631
  /**
3632
  * Gets Facebook product ID from meta or from Facebook API.
3633
  *
3634
+ * @param string $fbid_type ID type (group or item)
3635
+ * @param int $wp_id post ID
3636
  * @param WC_Facebook_Product|null $woo_product product
3637
  * @return mixed|void|null
3638
  */
3795
  $timestamp = $first_scheduled_time >= $current_time ? $first_scheduled_time->getTimestamp() : $next_scheduled_time->getTimestamp();
3796
 
3797
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3798
+ as_schedule_single_action( $timestamp, self::ACTION_HOOK_SCHEDULED_RESYNC, array(), 'facebook-for-woocommerce' );
3799
  }
3800
 
3801
 
3807
  private function unschedule_resync() {
3808
 
3809
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3810
+ as_unschedule_all_actions( self::ACTION_HOOK_SCHEDULED_RESYNC, array(), 'facebook-for-woocommerce' );
3811
  }
3812
 
3813
 
3823
  public function is_resync_scheduled() {
3824
 
3825
  // TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
3826
+ return is_int( as_next_scheduled_action( self::ACTION_HOOK_SCHEDULED_RESYNC, array(), 'facebook-for-woocommerce' ) );
3827
  }
3828
 
3829
 
3842
 
3843
  try {
3844
  $this->sync_facebook_products_using_feed();
3845
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
3846
+ }
3847
 
3848
  $resync_offset = $this->get_scheduled_resync_offset();
3849
 
facebook-config-warmer.php CHANGED
@@ -18,8 +18,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_WarmConfig' ) ) :
18
  class WC_Facebookcommerce_WarmConfig {
19
  static $fb_warm_pixel_id = null;
20
  static $fb_warm_is_advanced_matching_enabled = null;
21
- static $fb_warm_use_s2s = null;
22
- static $fb_warm_access_token = null;
23
  }
24
 
25
  endif;
18
  class WC_Facebookcommerce_WarmConfig {
19
  static $fb_warm_pixel_id = null;
20
  static $fb_warm_is_advanced_matching_enabled = null;
21
+ static $fb_warm_use_s2s = null;
22
+ static $fb_warm_access_token = null;
23
  }
24
 
25
  endif;
facebook-for-woocommerce.php CHANGED
@@ -10,15 +10,16 @@
10
  * Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
11
  * Author: Facebook
12
  * Author URI: https://www.facebook.com/
13
- * Version: 2.4.1
14
  * Text Domain: facebook-for-woocommerce
15
  * WC requires at least: 3.5.0
16
  * WC tested up to: 5.2.2
 
17
  *
18
  * @package FacebookCommerce
19
  */
20
 
21
- defined( 'ABSPATH' ) or exit;
22
 
23
  /**
24
  * The plugin loader class.
@@ -27,27 +28,39 @@ defined( 'ABSPATH' ) or exit;
27
  */
28
  class WC_Facebook_Loader {
29
 
 
 
 
 
30
 
31
- /** minimum PHP version required by this plugin */
32
- const MINIMUM_PHP_VERSION = '5.6.0';
33
 
34
- /** minimum WordPress version required by this plugin */
35
  const MINIMUM_WP_VERSION = '4.4';
36
 
37
- /** minimum WooCommerce version required by this plugin */
38
  const MINIMUM_WC_VERSION = '3.5.0';
39
 
40
- /** SkyVerge plugin framework version used by this plugin */
41
  const FRAMEWORK_VERSION = '5.10.0';
42
 
43
- /** the plugin name, for displaying notices */
44
  const PLUGIN_NAME = 'Facebook for WooCommerce';
45
 
46
 
47
- /** @var \WC_Facebook_Loader single instance of this class */
 
 
 
 
48
  private static $instance;
49
 
50
- /** @var array the admin notices to add */
 
 
 
 
51
  private $notices = array();
52
 
53
 
@@ -65,7 +78,7 @@ class WC_Facebook_Loader {
65
 
66
  add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
67
 
68
- // if the environment check fails, initialize the plugin
69
  if ( $this->is_environment_compatible() ) {
70
  add_action( 'plugins_loaded', array( $this, 'init_plugin' ) );
71
  }
@@ -107,7 +120,7 @@ class WC_Facebook_Loader {
107
 
108
  $this->load_framework();
109
 
110
- require_once( plugin_dir_path( __FILE__ ) . 'class-wc-facebookcommerce.php' );
111
 
112
  // fire it up!
113
  if ( function_exists( 'facebook_for_woocommerce' ) ) {
@@ -124,7 +137,7 @@ class WC_Facebook_Loader {
124
  private function load_framework() {
125
 
126
  if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\' . $this->get_framework_version_namespace() . '\\SV_WC_Plugin' ) ) {
127
- require_once( plugin_dir_path( __FILE__ ) . 'vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin.php' );
128
  }
129
  }
130
 
@@ -204,23 +217,34 @@ class WC_Facebook_Loader {
204
 
205
  if ( ! $this->is_wp_compatible() ) {
206
 
207
- $this->add_admin_notice( 'update_wordpress', 'error', sprintf(
208
- '%s requires WordPress version %s or higher. Please %supdate WordPress &raquo;%s',
209
- '<strong>' . self::PLUGIN_NAME . '</strong>',
210
- self::MINIMUM_WP_VERSION,
211
- '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">', '</a>'
212
- ) );
 
 
 
 
 
213
  }
214
 
215
  if ( ! $this->is_wc_compatible() ) {
216
 
217
- $this->add_admin_notice( 'update_woocommerce', 'error', sprintf(
218
- '%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',
219
- '<strong>' . self::PLUGIN_NAME . '</strong>',
220
- self::MINIMUM_WC_VERSION,
221
- '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">', '</a>',
222
- '<a href="' . esc_url( 'https://downloads.wordpress.org/plugin/woocommerce.' . self::MINIMUM_WC_VERSION . '.zip' ) . '">', '</a>'
223
- ) );
 
 
 
 
 
 
224
  }
225
  }
226
 
@@ -294,15 +318,15 @@ class WC_Facebook_Loader {
294
  *
295
  * @since 1.10.0
296
  *
297
- * @param string $slug the slug for the notice
298
- * @param string $class the css class for the notice
299
- * @param string $message the notice message
300
  */
301
  private function add_admin_notice( $slug, $class, $message ) {
302
 
303
  $this->notices[ $slug ] = array(
304
  'class' => $class,
305
- 'message' => $message
306
  );
307
  }
308
 
10
  * Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
11
  * Author: Facebook
12
  * Author URI: https://www.facebook.com/
13
+ * Version: 2.5.0
14
  * Text Domain: facebook-for-woocommerce
15
  * WC requires at least: 3.5.0
16
  * WC tested up to: 5.2.2
17
+ * Requires PHP: 7.0
18
  *
19
  * @package FacebookCommerce
20
  */
21
 
22
+ defined( 'ABSPATH' ) || exit;
23
 
24
  /**
25
  * The plugin loader class.
28
  */
29
  class WC_Facebook_Loader {
30
 
31
+ /**
32
+ * @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
33
+ */
34
+ const PLUGIN_VERSION = '2.5.0'; // WRCS: DEFINED_VERSION.
35
 
36
+ // Minimum PHP version required by this plugin.
37
+ const MINIMUM_PHP_VERSION = '7.0.0';
38
 
39
+ // Minimum WordPress version required by this plugin.
40
  const MINIMUM_WP_VERSION = '4.4';
41
 
42
+ // Minimum WooCommerce version required by this plugin.
43
  const MINIMUM_WC_VERSION = '3.5.0';
44
 
45
+ // SkyVerge plugin framework version used by this plugin.
46
  const FRAMEWORK_VERSION = '5.10.0';
47
 
48
+ // The plugin name, for displaying notices.
49
  const PLUGIN_NAME = 'Facebook for WooCommerce';
50
 
51
 
52
+ /**
53
+ * This class instance.
54
+ *
55
+ * @var \WC_Facebook_Loader single instance of this class.
56
+ */
57
  private static $instance;
58
 
59
+ /**
60
+ * Admin notices to add.
61
+ *
62
+ * @var array Array of admin notices.
63
+ */
64
  private $notices = array();
65
 
66
 
78
 
79
  add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
80
 
81
+ // If the environment check fails, initialize the plugin.
82
  if ( $this->is_environment_compatible() ) {
83
  add_action( 'plugins_loaded', array( $this, 'init_plugin' ) );
84
  }
120
 
121
  $this->load_framework();
122
 
123
+ require_once plugin_dir_path( __FILE__ ) . 'class-wc-facebookcommerce.php';
124
 
125
  // fire it up!
126
  if ( function_exists( 'facebook_for_woocommerce' ) ) {
137
  private function load_framework() {
138
 
139
  if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\' . $this->get_framework_version_namespace() . '\\SV_WC_Plugin' ) ) {
140
+ require_once plugin_dir_path( __FILE__ ) . 'vendor/skyverge/wc-plugin-framework/woocommerce/class-sv-wc-plugin.php';
141
  }
142
  }
143
 
217
 
218
  if ( ! $this->is_wp_compatible() ) {
219
 
220
+ $this->add_admin_notice(
221
+ 'update_wordpress',
222
+ 'error',
223
+ sprintf(
224
+ '%s requires WordPress version %s or higher. Please %supdate WordPress &raquo;%s',
225
+ '<strong>' . self::PLUGIN_NAME . '</strong>',
226
+ self::MINIMUM_WP_VERSION,
227
+ '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
228
+ '</a>'
229
+ )
230
+ );
231
  }
232
 
233
  if ( ! $this->is_wc_compatible() ) {
234
 
235
+ $this->add_admin_notice(
236
+ 'update_woocommerce',
237
+ 'error',
238
+ sprintf(
239
+ '%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',
240
+ '<strong>' . self::PLUGIN_NAME . '</strong>',
241
+ self::MINIMUM_WC_VERSION,
242
+ '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
243
+ '</a>',
244
+ '<a href="' . esc_url( 'https://downloads.wordpress.org/plugin/woocommerce.' . self::MINIMUM_WC_VERSION . '.zip' ) . '">',
245
+ '</a>'
246
+ )
247
+ );
248
  }
249
  }
250
 
318
  *
319
  * @since 1.10.0
320
  *
321
+ * @param string $slug The slug for the notice.
322
+ * @param string $class The css class for the notice.
323
+ * @param string $message The notice message.
324
  */
325
  private function add_admin_notice( $slug, $class, $message ) {
326
 
327
  $this->notices[ $slug ] = array(
328
  'class' => $class,
329
+ 'message' => $message,
330
  );
331
  }
332
 
i18n/languages/facebook-for-woocommerce.pot CHANGED
@@ -2,10 +2,10 @@
2
  # This file is distributed under the same license as the Facebook for WooCommerce package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Facebook for WooCommerce 2.4.1\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
- "POT-Creation-Date: 2021-04-29 11:19:08+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -13,7 +13,7 @@ msgstr ""
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
15
 
16
- #: class-wc-facebookcommerce.php:264
17
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
18
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
19
  msgid ""
@@ -21,7 +21,7 @@ msgid ""
21
  "Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!"
22
  msgstr ""
23
 
24
- #: class-wc-facebookcommerce.php:279
25
  #. translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link
26
  #. HTML tag
27
  msgid ""
@@ -29,7 +29,7 @@ msgid ""
29
  "under %1$sWooCommerce > Facebook%2$s."
30
  msgstr ""
31
 
32
- #: class-wc-facebookcommerce.php:294
33
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
34
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
35
  msgid ""
@@ -37,7 +37,7 @@ msgid ""
37
  "configuration, %3$scomplete the setup steps%4$s."
38
  msgstr ""
39
 
40
- #: class-wc-facebookcommerce.php:322
41
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
42
  #. <a> tag, %4$s - </a> tag
43
  msgid ""
@@ -46,7 +46,7 @@ msgid ""
46
  "Connection%4$s area."
47
  msgstr ""
48
 
49
- #: class-wc-facebookcommerce.php:339
50
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
51
  #. <a> tag, %4$s - </a> tag
52
  msgid ""
@@ -55,51 +55,51 @@ msgid ""
55
  "products."
56
  msgstr ""
57
 
58
- #: class-wc-facebookcommerce.php:359
59
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
60
  #. </a> HTML link tag
61
  msgid "Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu."
62
  msgstr ""
63
 
64
- #: class-wc-facebookcommerce.php:429
65
  msgid "FB Product Sets"
66
  msgstr ""
67
 
68
- #: class-wc-facebookcommerce.php:430
69
  msgid "FB Product Set"
70
  msgstr ""
71
 
72
- #: class-wc-facebookcommerce.php:438
73
  #. translators: Edit item label
74
  msgid "Edit %s"
75
  msgstr ""
76
 
77
- #: class-wc-facebookcommerce.php:440
78
  #. translators: Add new label
79
  msgid "Add new %s"
80
  msgstr ""
81
 
82
- #: class-wc-facebookcommerce.php:443
83
  #. translators: No items found text
84
  msgid "No %s found."
85
  msgstr ""
86
 
87
- #: class-wc-facebookcommerce.php:445
88
  #. translators: Search label
89
  msgid "Search %s."
90
  msgstr ""
91
 
92
- #: class-wc-facebookcommerce.php:447
93
  #. translators: Text label
94
  msgid "Separate %s with commas"
95
  msgstr ""
96
 
97
- #: class-wc-facebookcommerce.php:449
98
  #. translators: Text label
99
  msgid "Choose from the most used %s"
100
  msgstr ""
101
 
102
- #: class-wc-facebookcommerce.php:565
103
  msgid "Cannot create the API instance because the access token is missing."
104
  msgstr ""
105
 
@@ -107,69 +107,69 @@ msgstr ""
107
  msgid "Facebook for WooCommerce"
108
  msgstr ""
109
 
110
- #: facebook-commerce.php:220
111
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
112
  msgstr ""
113
 
114
- #: facebook-commerce.php:641
115
  msgid "Facebook ID:"
116
  msgstr ""
117
 
118
- #: facebook-commerce.php:649
119
  msgid "Variant IDs:"
120
  msgstr ""
121
 
122
- #: facebook-commerce.php:667
123
  msgid "Reset Facebook metadata"
124
  msgstr ""
125
 
126
- #: facebook-commerce.php:672
127
  msgid "Delete product(s) on Facebook"
128
  msgstr ""
129
 
130
- #: facebook-commerce.php:680
131
  msgid "This product is not yet synced to Facebook."
132
  msgstr ""
133
 
134
- #: facebook-commerce.php:1496
135
  msgid "Nothing to update for product group for %1$s"
136
  msgstr ""
137
 
138
- #: facebook-commerce.php:1891
139
  #. translators: Placeholders %1$s - original error message from Facebook API
140
  msgid "There was an issue connecting to the Facebook API: %1$s"
141
  msgstr ""
142
 
143
- #: facebook-commerce.php:2228
144
  msgid "Your connection has expired."
145
  msgstr ""
146
 
147
- #: facebook-commerce.php:2228
148
  msgid ""
149
  "Please click Manage connection > Advanced Options > Update Token to refresh "
150
  "your connection to Facebook."
151
  msgstr ""
152
 
153
- #: facebook-commerce.php:2235
154
  #. translators: Placeholders %s - error message
155
  msgid "There was an error trying to sync the products to Facebook. %s"
156
  msgstr ""
157
 
158
- #: facebook-commerce.php:2258 facebook-commerce.php:2431
159
  msgid "Product sync is disabled."
160
  msgstr ""
161
 
162
- #: facebook-commerce.php:2265 facebook-commerce.php:2438
163
  msgid "The plugin is not configured or the Catalog ID is missing."
164
  msgstr ""
165
 
166
- #: facebook-commerce.php:2288
167
  msgid ""
168
  "A product sync is in progress. Please wait until the sync finishes before "
169
  "starting a new one."
170
  msgstr ""
171
 
172
- #: facebook-commerce.php:2300 facebook-commerce.php:2452
173
  msgid ""
174
  "We've detected that your Facebook Product Catalog is no longer valid. This "
175
  "may happen if it was deleted, but could also be a temporary error. If the "
@@ -177,33 +177,33 @@ msgid ""
177
  "and setup the plugin again."
178
  msgstr ""
179
 
180
- #: facebook-commerce.php:2476
181
  msgid "We couldn't create the feed or upload the product information."
182
  msgstr ""
183
 
184
- #: facebook-commerce.php:2944
185
  msgid "Hi! We're here to answer any questions you may have."
186
  msgstr ""
187
 
188
- #: facebook-commerce.php:3291
189
  msgid "Facebook for WooCommerce error:"
190
  msgstr ""
191
 
192
- #: facebook-commerce.php:3373
193
  msgid ""
194
  "There was an error trying to retrieve information about the Facebook page: "
195
  "%s"
196
  msgstr ""
197
 
198
- #: facebook-commerce.php:3424
199
  msgid "Get started with Messenger Customer Chat"
200
  msgstr ""
201
 
202
- #: facebook-commerce.php:3425
203
  msgid "Get started with Instagram Shopping"
204
  msgstr ""
205
 
206
- #: facebook-commerce.php:3660
207
  #. translators: Placeholders %1$s - original error message from Facebook API
208
  msgid "There was an issue connecting to the Facebook API: %s"
209
  msgstr ""
@@ -224,33 +224,33 @@ msgstr ""
224
  msgid "A valid Order ID is required."
225
  msgstr ""
226
 
227
- #: includes/AJAX.php:177
228
  msgid "Order ID is required"
229
  msgstr ""
230
 
231
- #: includes/AJAX.php:181
232
  msgid "Tracking number is required"
233
  msgstr ""
234
 
235
- #: includes/AJAX.php:185
236
  msgid "Carrier code is required"
237
  msgstr ""
238
 
239
- #: includes/AJAX.php:191
240
  msgid "Order not found"
241
  msgstr ""
242
 
243
- #: includes/AJAX.php:332 includes/AJAX.php:401
244
  msgid "Go to Settings"
245
  msgstr ""
246
 
247
- #: includes/AJAX.php:337 includes/AJAX.php:406 includes/AJAX.php:470
248
  #: includes/Admin/Product_Categories.php:118
249
- #: includes/Admin/Settings_Screens/Product_Sync.php:142 includes/Admin.php:340
250
  msgid "Cancel"
251
  msgstr ""
252
 
253
- #: includes/AJAX.php:345
254
  #. translators: Placeholder %s - <br/> tag
255
  msgid ""
256
  "This product belongs to a category or tag that is excluded from the "
@@ -259,7 +259,7 @@ msgid ""
259
  "or click Cancel and update the product's category / tag assignments."
260
  msgstr ""
261
 
262
- #: includes/AJAX.php:412
263
  msgid ""
264
  "One or more of the selected products belongs to a category or tag that is "
265
  "excluded from the Facebook catalog sync. To sync these products to "
@@ -267,11 +267,11 @@ msgid ""
267
  "settings."
268
  msgstr ""
269
 
270
- #: includes/AJAX.php:464
271
  msgid "Exclude Products"
272
  msgstr ""
273
 
274
- #: includes/AJAX.php:478
275
  #. translators: Placeholder %s - <br/> tags
276
  msgid ""
277
  "The categories and/or tags that you have selected to exclude from sync "
@@ -289,7 +289,7 @@ msgid "Show advanced options"
289
  msgstr ""
290
 
291
  #: includes/Admin/Product_Categories.php:99
292
- #: includes/Admin/Settings_Screens/Product_Sync.php:106
293
  msgid ""
294
  "Products and categories that inherit this global setting (i.e. they do not "
295
  "have a specific Google product category set) will use the new default "
@@ -297,7 +297,7 @@ msgid ""
297
  msgstr ""
298
 
299
  #: includes/Admin/Product_Categories.php:122
300
- #: includes/Admin/Settings_Screens/Product_Sync.php:146
301
  msgid "Update default Google product category"
302
  msgstr ""
303
 
@@ -316,7 +316,7 @@ msgid ""
316
  msgstr ""
317
 
318
  #: includes/Admin/Product_Categories.php:232
319
- #: includes/Admin/Settings_Screens/Product_Sync.php:316
320
  msgid "Default Google product category"
321
  msgstr ""
322
 
@@ -381,42 +381,42 @@ msgstr ""
381
  msgid "Facebook"
382
  msgstr ""
383
 
384
- #: includes/Admin/Settings.php:119
385
  #: includes/Admin/Settings_Screens/Connection.php:34
386
  #: includes/Admin/Settings_Screens/Connection.php:35
387
  msgid "Connection"
388
  msgstr ""
389
 
390
- #: includes/Admin/Settings.php:122
391
  #: includes/Admin/Settings_Screens/Messenger.php:40
392
  #: includes/Admin/Settings_Screens/Messenger.php:41
393
- #: includes/Admin/Settings_Screens/Messenger.php:144
394
  msgid "Messenger"
395
  msgstr ""
396
 
397
- #: includes/Admin/Settings.php:125
398
  #: includes/Admin/Settings_Screens/Product_Sync.php:42
399
  #: includes/Admin/Settings_Screens/Product_Sync.php:43
400
- #: includes/Admin/Settings_Screens/Product_Sync.php:167
401
- #: includes/Admin/Settings_Screens/Product_Sync.php:262
402
  msgid "Product sync"
403
  msgstr ""
404
 
405
- #: includes/Admin/Settings.php:128
406
  #: includes/Admin/Settings_Screens/Advertise.php:37
407
  #: includes/Admin/Settings_Screens/Advertise.php:38
408
  msgid "Advertise"
409
  msgstr ""
410
 
411
- #: includes/Admin/Settings.php:212
412
  msgid "You do not have permission to save these settings."
413
  msgstr ""
414
 
415
- #: includes/Admin/Settings.php:221
416
  msgid "Your settings have been saved."
417
  msgstr ""
418
 
419
- #: includes/Admin/Settings.php:228
420
  #. translators: Placeholders: %s - user-friendly error message
421
  msgid "Your settings could not be saved. %s"
422
  msgstr ""
@@ -442,120 +442,120 @@ msgid ""
442
  "touch with our support team%6$s for assistance."
443
  msgstr ""
444
 
445
- #: includes/Admin/Settings_Screens/Connection.php:123
446
  msgid "Page"
447
  msgstr ""
448
 
449
- #: includes/Admin/Settings_Screens/Connection.php:127
450
  msgid "Pixel"
451
  msgstr ""
452
 
453
- #: includes/Admin/Settings_Screens/Connection.php:131
454
  msgid "Catalog"
455
  msgstr ""
456
 
457
- #: includes/Admin/Settings_Screens/Connection.php:136
458
  msgid "Business Manager account"
459
  msgstr ""
460
 
461
- #: includes/Admin/Settings_Screens/Connection.php:140
462
  msgid "Ad Manager account"
463
  msgstr ""
464
 
465
- #: includes/Admin/Settings_Screens/Connection.php:144
466
  msgid "Instagram Business ID"
467
  msgstr ""
468
 
469
- #: includes/Admin/Settings_Screens/Connection.php:148
470
  msgid "Commerce Merchant Settings ID"
471
  msgstr ""
472
 
473
- #: includes/Admin/Settings_Screens/Connection.php:243
474
  msgid "Reach the Right People and Sell More Online"
475
  msgstr ""
476
 
477
- #: includes/Admin/Settings_Screens/Connection.php:245
478
  msgid "Grow your business on Facebook"
479
  msgstr ""
480
 
481
- #: includes/Admin/Settings_Screens/Connection.php:248
482
  msgid "Use this WooCommerce and Facebook integration to:"
483
  msgstr ""
484
 
485
- #: includes/Admin/Settings_Screens/Connection.php:250
486
  msgid "Create an ad in a few steps"
487
  msgstr ""
488
 
489
- #: includes/Admin/Settings_Screens/Connection.php:251
490
  msgid "Use built-in best practices for online sales"
491
  msgstr ""
492
 
493
- #: includes/Admin/Settings_Screens/Connection.php:252
494
  msgid "Get reporting on sales and revenue"
495
  msgstr ""
496
 
497
- #: includes/Admin/Settings_Screens/Connection.php:275
498
  msgid "Manage Connection"
499
  msgstr ""
500
 
501
- #: includes/Admin/Settings_Screens/Connection.php:279
502
  msgid "Disconnect"
503
  msgstr ""
504
 
505
- #: includes/Admin/Settings_Screens/Connection.php:285
506
  msgid "Get Started"
507
  msgstr ""
508
 
509
- #: includes/Admin/Settings_Screens/Connection.php:310
510
  msgid "Debug"
511
  msgstr ""
512
 
513
- #: includes/Admin/Settings_Screens/Connection.php:316
514
  msgid "Enable debug mode"
515
  msgstr ""
516
 
517
- #: includes/Admin/Settings_Screens/Connection.php:318
518
  msgid "Log plugin events for debugging"
519
  msgstr ""
520
 
521
- #: includes/Admin/Settings_Screens/Connection.php:319
522
  msgid "Only enable this if you are experiencing problems with the plugin."
523
  msgstr ""
524
 
525
- #: includes/Admin/Settings_Screens/Messenger.php:120
526
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
527
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
528
  msgstr ""
529
 
530
- #: includes/Admin/Settings_Screens/Messenger.php:150
531
  msgid "Enable Messenger"
532
  msgstr ""
533
 
534
- #: includes/Admin/Settings_Screens/Messenger.php:152
535
  msgid "Enable and customize Facebook Messenger on your store"
536
  msgstr ""
537
 
538
- #: includes/Admin/Settings_Screens/Messenger.php:162
539
  msgid "Language"
540
  msgstr ""
541
 
542
- #: includes/Admin/Settings_Screens/Messenger.php:167
543
  msgid "Greeting & Colors"
544
  msgstr ""
545
 
546
- #: includes/Admin/Settings_Screens/Messenger.php:189
547
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
548
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger."
549
  msgstr ""
550
 
551
- #: includes/Admin/Settings_Screens/Messenger.php:240
552
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
553
  msgid ""
554
  "There was an error communicating with the Facebook Business Extension. "
555
  "%1$sClick here%2$s to manage your Messenger settings."
556
  msgstr ""
557
 
558
- #: includes/Admin/Settings_Screens/Messenger.php:310
559
  msgid "Please try again."
560
  msgstr ""
561
 
@@ -564,19 +564,19 @@ msgstr ""
564
  msgid "Product sets"
565
  msgstr ""
566
 
567
- #: includes/Admin/Settings_Screens/Product_Sync.php:71
568
  #. translators: Placeholders: {count} number of remaining items
569
  msgid "{count} item remaining."
570
  msgid_plural "{count} items remaining."
571
  msgstr[0] ""
572
  msgstr[1] ""
573
 
574
- #: includes/Admin/Settings_Screens/Product_Sync.php:83
575
  #. translators: Placeholders %s - html code for a spinner icon
576
  msgid "Your products will now be resynced to Facebook, this may take some time."
577
  msgstr ""
578
 
579
- #: includes/Admin/Settings_Screens/Product_Sync.php:84
580
  msgid ""
581
  "Facebook for WooCommerce automatically syncs your products on "
582
  "create/update. Are you sure you want to force product resync?\n"
@@ -586,21 +586,21 @@ msgid ""
586
  "did not sync."
587
  msgstr ""
588
 
589
- #: includes/Admin/Settings_Screens/Product_Sync.php:85
590
  msgid "Your products are syncing - you may safely leave this page %s"
591
  msgstr ""
592
 
593
- #: includes/Admin/Settings_Screens/Product_Sync.php:88
594
  msgid "There was an error trying to sync the products to Facebook."
595
  msgstr ""
596
 
597
- #: includes/Admin/Settings_Screens/Product_Sync.php:89
598
  msgid ""
599
  "Something went wrong while uploading the product information, please try "
600
  "again."
601
  msgstr ""
602
 
603
- #: includes/Admin/Settings_Screens/Product_Sync.php:121
604
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
605
  msgid ""
606
  "Products and categories that inherit this global setting (they do not have "
@@ -610,62 +610,62 @@ msgid ""
610
  "Are you sure you want to proceed?"
611
  msgstr ""
612
 
613
- #: includes/Admin/Settings_Screens/Product_Sync.php:175
614
  msgid "Sync products"
615
  msgstr ""
616
 
617
- #: includes/Admin/Settings_Screens/Product_Sync.php:267
618
  msgid "Enable product sync"
619
  msgstr ""
620
 
621
- #: includes/Admin/Settings_Screens/Product_Sync.php:275
622
  msgid "Exclude categories from sync"
623
  msgstr ""
624
 
625
- #: includes/Admin/Settings_Screens/Product_Sync.php:279
626
  msgid "Products in one or more of these categories will not sync to Facebook."
627
  msgstr ""
628
 
629
- #: includes/Admin/Settings_Screens/Product_Sync.php:283
630
  msgid "Search for a product category&hellip;"
631
  msgstr ""
632
 
633
- #: includes/Admin/Settings_Screens/Product_Sync.php:289
634
  msgid "Exclude tags from sync"
635
  msgstr ""
636
 
637
- #: includes/Admin/Settings_Screens/Product_Sync.php:293
638
  msgid "Products with one or more of these tags will not sync to Facebook."
639
  msgstr ""
640
 
641
- #: includes/Admin/Settings_Screens/Product_Sync.php:297
642
  msgid "Search for a product tag&hellip;"
643
  msgstr ""
644
 
645
- #: includes/Admin/Settings_Screens/Product_Sync.php:303
646
  msgid "Product description sync"
647
  msgstr ""
648
 
649
- #: includes/Admin/Settings_Screens/Product_Sync.php:306
650
  msgid "Choose which product description to display in the Facebook catalog."
651
  msgstr ""
652
 
653
- #: includes/Admin/Settings_Screens/Product_Sync.php:309
654
  msgid "Standard description"
655
  msgstr ""
656
 
657
- #: includes/Admin/Settings_Screens/Product_Sync.php:310
658
  msgid "Short description"
659
  msgstr ""
660
 
661
- #: includes/Admin/Settings_Screens/Product_Sync.php:317
662
  msgid ""
663
  "Choose a default Google product category for your products. Defaults can "
664
  "also be set for product categories. Products need at least two category "
665
  "levels defined for tax to be correctly applied."
666
  msgstr ""
667
 
668
- #: includes/Admin/Settings_Screens/Product_Sync.php:363
669
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
670
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage product sync."
671
  msgstr ""
@@ -674,41 +674,41 @@ msgstr ""
674
  msgid "The name is how it appears on Facebook Catalog."
675
  msgstr ""
676
 
677
- #: includes/Admin.php:181
678
  msgid ""
679
  "Please enter a Google product category and at least one sub-category to "
680
  "sell this product on Instagram."
681
  msgstr ""
682
 
683
- #: includes/Admin.php:203
684
  msgid "Search main categories..."
685
  msgstr ""
686
 
687
- #: includes/Admin.php:204
688
  msgid "Choose a main category"
689
  msgstr ""
690
 
691
- #: includes/Admin.php:205
692
  msgid "Choose a category"
693
  msgstr ""
694
 
695
- #: includes/Admin.php:244
696
  msgid ""
697
  "To sell this product on Instagram, please ensure it meets the following "
698
  "requirements:"
699
  msgstr ""
700
 
701
- #: includes/Admin.php:247
702
  msgid "Has a price defined"
703
  msgstr ""
704
 
705
- #: includes/Admin.php:250
706
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s -
707
  #. </strong> closing HTML tag
708
  msgid "Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab"
709
  msgstr ""
710
 
711
- #: includes/Admin.php:256
712
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s -
713
  #. </strong> closing HTML tag
714
  msgid ""
@@ -716,11 +716,11 @@ msgid ""
716
  "and hide\""
717
  msgstr ""
718
 
719
- #: includes/Admin.php:282
720
  msgid "Close"
721
  msgstr ""
722
 
723
- #: includes/Admin.php:305
724
  #. translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a>
725
  #. link tag
726
  msgid ""
@@ -729,44 +729,44 @@ msgid ""
729
  "the Facebook catalog as well?"
730
  msgstr ""
731
 
732
- #: includes/Admin.php:332
733
  msgid "Remove from sync only"
734
  msgstr ""
735
 
736
- #: includes/Admin.php:336
737
  msgid "Remove from sync and delete"
738
  msgstr ""
739
 
740
- #: includes/Admin.php:372 includes/Admin.php:1216 includes/Admin.php:1356
741
  msgid "Facebook sync"
742
  msgstr ""
743
 
744
- #: includes/Admin.php:399 includes/Admin.php:431
745
  msgid "Sync and show"
746
  msgstr ""
747
 
748
- #: includes/Admin.php:401 includes/Admin.php:432
749
  msgid "Sync and hide"
750
  msgstr ""
751
 
752
- #: includes/Admin.php:406 includes/Admin.php:433 includes/Admin.php:1220
753
- #: includes/Admin.php:1360
754
  msgid "Do not sync"
755
  msgstr ""
756
 
757
- #: includes/Admin.php:430
758
  msgid "Filter by Facebook sync setting"
759
  msgstr ""
760
 
761
- #: includes/Admin.php:843
762
  msgid "Include in Facebook sync"
763
  msgstr ""
764
 
765
- #: includes/Admin.php:844
766
  msgid "Exclude from Facebook sync"
767
  msgstr ""
768
 
769
- #: includes/Admin.php:1037
770
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
771
  #. <a> tag, %4$s - <a> tag
772
  msgid ""
@@ -780,11 +780,11 @@ msgid_plural ""
780
  msgstr[0] ""
781
  msgstr[1] ""
782
 
783
- #: includes/Admin.php:1044
784
  msgid "Don't show this notice again"
785
  msgstr ""
786
 
787
- #: includes/Admin.php:1078
788
  #. translators: Placeholders: %1$s - number of affected products, %2$s opening
789
  #. HTML <a> tag, %3$s - closing HTML </a> tag, %4$s - opening HTML <a> tag,
790
  #. %5$s - closing HTML </a> tag
@@ -801,7 +801,7 @@ msgid_plural ""
801
  msgstr[0] ""
802
  msgstr[1] ""
803
 
804
- #: includes/Admin.php:1144
805
  #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing
806
  #. HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a>
807
  #. tag
@@ -811,176 +811,176 @@ msgid ""
811
  "your Facebook catalog. You may still advertise Virtual products on Facebook."
812
  msgstr ""
813
 
814
- #: includes/Admin.php:1218 includes/Admin.php:1358
815
  msgid "Sync and show in catalog"
816
  msgstr ""
817
 
818
- #: includes/Admin.php:1219 includes/Admin.php:1359
819
  msgid "Sync and hide in catalog"
820
  msgstr ""
821
 
822
- #: includes/Admin.php:1229 includes/Admin.php:1372
823
  msgid "Facebook Description"
824
  msgstr ""
825
 
826
- #: includes/Admin.php:1231 includes/Admin.php:1374
827
  msgid ""
828
  "Custom (plain-text only) description for product on Facebook. If blank, "
829
  "product description will be used. If product description is blank, "
830
  "shortname will be used."
831
  msgstr ""
832
 
833
- #: includes/Admin.php:1242 includes/Admin.php:1387
834
  msgid "Facebook Product Image"
835
  msgstr ""
836
 
837
- #: includes/Admin.php:1244 includes/Admin.php:1389
838
  msgid ""
839
  "Choose the product image that should be synced to the Facebook catalog for "
840
  "this product. If using a custom image, please enter an absolute URL (e.g. "
841
  "https://domain.com/image.jpg)."
842
  msgstr ""
843
 
844
- #: includes/Admin.php:1246
845
  msgid "Use WooCommerce image"
846
  msgstr ""
847
 
848
- #: includes/Admin.php:1247 includes/Admin.php:1393
849
  msgid "Use custom image"
850
  msgstr ""
851
 
852
- #: includes/Admin.php:1258 includes/Admin.php:1405
853
  msgid "Custom Image URL"
854
  msgstr ""
855
 
856
- #: includes/Admin.php:1269 includes/Admin.php:1418
857
  #. translators: Placeholders %1$s - WC currency symbol
858
  msgid "Facebook Price (%1$s)"
859
  msgstr ""
860
 
861
- #: includes/Admin.php:1273 includes/Admin.php:1422
862
  msgid ""
863
  "Custom price for product on Facebook. Please enter in monetary decimal (.) "
864
  "format without thousand separators and currency symbols. If blank, product "
865
  "price will be used."
866
  msgstr ""
867
 
868
- #: includes/Admin.php:1391
869
  msgid "Use variation image"
870
  msgstr ""
871
 
872
- #: includes/Admin.php:1392
873
  msgid "Use parent image"
874
  msgstr ""
875
 
876
- #: includes/Admin.php:1546
877
  msgid "Close modal panel"
878
  msgstr ""
879
 
880
- #: includes/Commerce/Orders.php:425
881
  #. translators: Placeholders: %1$s - order remote id, %2$s - order created by
882
  msgid "Order %1$s paid in %2$s"
883
  msgstr ""
884
 
885
- #: includes/Commerce/Orders.php:576 includes/Commerce/Orders.php:803
886
  msgid "Remote ID not found."
887
  msgstr ""
888
 
889
- #: includes/Commerce/Orders.php:583
890
  msgid "%s is not a valid shipping carrier code."
891
  msgstr ""
892
 
893
- #: includes/Commerce/Orders.php:601 includes/Commerce/Orders.php:770
894
  msgid "No valid Facebook products were found."
895
  msgstr ""
896
 
897
- #: includes/Commerce/Orders.php:618
898
  #. translators: Placeholder: %s - sales channel name, like Facebook or
899
  #. Instagram
900
  msgid "%s order fulfilled."
901
  msgstr ""
902
 
903
- #: includes/Commerce/Orders.php:626
904
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
905
  #. Instagram, %2$s - error message
906
  msgid "%1$s order could not be fulfilled. %2$s"
907
  msgstr ""
908
 
909
- #: includes/Commerce/Orders.php:669
910
  msgid "Parent order not found."
911
  msgstr ""
912
 
913
- #: includes/Commerce/Orders.php:675
914
  msgid "Remote ID for parent order not found."
915
  msgstr ""
916
 
917
- #: includes/Commerce/Orders.php:705
918
  #. translators: Placeholder: %s - sales channel name, like Facebook or
919
  #. Instagram
920
  msgid "Order refunded on %s."
921
  msgstr ""
922
 
923
- #: includes/Commerce/Orders.php:715
924
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
925
  #. Instagram, %2$s - error message
926
  msgid "Could not refund %1$s order: %2$s"
927
  msgstr ""
928
 
929
- #: includes/Commerce/Orders.php:810
930
  #. translators: Placeholder: %s - sales channel name, like Facebook or
931
  #. Instagram
932
  msgid "%s order cancelled."
933
  msgstr ""
934
 
935
- #: includes/Commerce/Orders.php:818
936
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
937
  #. Instagram, %2$s - error message
938
  msgid "%1$s order could not be cancelled. %2$s"
939
  msgstr ""
940
 
941
- #: includes/Commerce/Orders.php:839
942
  msgid "Customer requested cancellation"
943
  msgstr ""
944
 
945
- #: includes/Commerce/Orders.php:840
946
  msgid "Product(s) are out of stock"
947
  msgstr ""
948
 
949
- #: includes/Commerce/Orders.php:841
950
  msgid "Customer address is invalid"
951
  msgstr ""
952
 
953
- #: includes/Commerce/Orders.php:842
954
  msgid "Suspicious order"
955
  msgstr ""
956
 
957
- #: includes/Commerce/Orders.php:843
958
  msgid "Other"
959
  msgstr ""
960
 
961
- #: includes/Handlers/Connection.php:310
962
  msgid "Connection successful!"
963
  msgstr ""
964
 
965
- #: includes/Handlers/Connection.php:336
966
  msgid "You do not have permission to uninstall Facebook Business Extension."
967
  msgstr ""
968
 
969
- #: includes/Handlers/Connection.php:346
970
  msgid "Disconnection successful."
971
  msgstr ""
972
 
973
- #: includes/Handlers/Connection.php:410
974
  #. translators: Placeholders: %s - API error message
975
  msgid "Could not retrieve page access data. %s"
976
  msgstr ""
977
 
978
- #: includes/Handlers/Connection.php:422
979
  #. translators: Placeholders: %s - Facebook page ID
980
  msgid "Page %s not authorized."
981
  msgstr ""
982
 
983
- #: includes/Handlers/Connection.php:1358
984
  msgid "You do not have permission to finish App Store login."
985
  msgstr ""
986
 
@@ -992,14 +992,14 @@ msgstr ""
992
  msgid "Job data key \"%s\" is not an array"
993
  msgstr ""
994
 
995
- #: includes/Products/Sync/Background.php:158
996
  msgid ""
997
  "There was an error trying sync products using the Catalog Batch API for job "
998
- "%s: %s"
999
  msgstr ""
1000
 
1001
  #: includes/fbgraph.php:105
1002
- msgid "HTTP %s: %s"
1003
  msgstr ""
1004
 
1005
  #: includes/fbinfobanner.php:211
@@ -1014,19 +1014,19 @@ msgstr ""
1014
  msgid "Dismiss"
1015
  msgstr ""
1016
 
1017
- #: includes/fbproductfeed.php:466
1018
  msgid "Could not create product catalog feed directory"
1019
  msgstr ""
1020
 
1021
- #: includes/fbproductfeed.php:532
1022
  msgid "Could not open the product catalog temporary feed file for writing"
1023
  msgstr ""
1024
 
1025
- #: includes/fbproductfeed.php:539
1026
  msgid "Could not open the product catalog feed file for writing"
1027
  msgstr ""
1028
 
1029
- #: includes/fbproductfeed.php:583
1030
  msgid "Could not rename the product catalog feed file"
1031
  msgstr ""
1032
 
@@ -1067,7 +1067,7 @@ msgstr ""
1067
  msgid "https://www.facebook.com/"
1068
  msgstr ""
1069
 
1070
- #: includes/Locale.php:181
1071
  msgctxt "language"
1072
  msgid "English (United States)"
1073
  msgstr ""
2
  # This file is distributed under the same license as the Facebook for WooCommerce package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Facebook for WooCommerce 2.5.0\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
+ "POT-Creation-Date: 2021-05-20 04:47:04+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
15
 
16
+ #: class-wc-facebookcommerce.php:252
17
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
18
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
19
  msgid ""
21
  "Facebook for WooCommerce connection. Please %3$sclick here%4$s to reconnect!"
22
  msgstr ""
23
 
24
+ #: class-wc-facebookcommerce.php:273
25
  #. translators: Placeholders %1$s - opening link HTML tag, %2$s - closing link
26
  #. HTML tag
27
  msgid ""
29
  "under %1$sWooCommerce > Facebook%2$s."
30
  msgstr ""
31
 
32
+ #: class-wc-facebookcommerce.php:293
33
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
34
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
35
  msgid ""
37
  "configuration, %3$scomplete the setup steps%4$s."
38
  msgstr ""
39
 
40
+ #: class-wc-facebookcommerce.php:325
41
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
42
  #. <a> tag, %4$s - </a> tag
43
  msgid ""
46
  "Connection%4$s area."
47
  msgstr ""
48
 
49
+ #: class-wc-facebookcommerce.php:348
50
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
51
  #. <a> tag, %4$s - </a> tag
52
  msgid ""
55
  "products."
56
  msgstr ""
57
 
58
+ #: class-wc-facebookcommerce.php:374
59
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
60
  #. </a> HTML link tag
61
  msgid "Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu."
62
  msgstr ""
63
 
64
+ #: class-wc-facebookcommerce.php:446
65
  msgid "FB Product Sets"
66
  msgstr ""
67
 
68
+ #: class-wc-facebookcommerce.php:447
69
  msgid "FB Product Set"
70
  msgstr ""
71
 
72
+ #: class-wc-facebookcommerce.php:455
73
  #. translators: Edit item label
74
  msgid "Edit %s"
75
  msgstr ""
76
 
77
+ #: class-wc-facebookcommerce.php:457
78
  #. translators: Add new label
79
  msgid "Add new %s"
80
  msgstr ""
81
 
82
+ #: class-wc-facebookcommerce.php:460
83
  #. translators: No items found text
84
  msgid "No %s found."
85
  msgstr ""
86
 
87
+ #: class-wc-facebookcommerce.php:462
88
  #. translators: Search label
89
  msgid "Search %s."
90
  msgstr ""
91
 
92
+ #: class-wc-facebookcommerce.php:464
93
  #. translators: Text label
94
  msgid "Separate %s with commas"
95
  msgstr ""
96
 
97
+ #: class-wc-facebookcommerce.php:466
98
  #. translators: Text label
99
  msgid "Choose from the most used %s"
100
  msgstr ""
101
 
102
+ #: class-wc-facebookcommerce.php:582
103
  msgid "Cannot create the API instance because the access token is missing."
104
  msgstr ""
105
 
107
  msgid "Facebook for WooCommerce"
108
  msgstr ""
109
 
110
+ #: facebook-commerce.php:228
111
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
112
  msgstr ""
113
 
114
+ #: facebook-commerce.php:640
115
  msgid "Facebook ID:"
116
  msgstr ""
117
 
118
+ #: facebook-commerce.php:648
119
  msgid "Variant IDs:"
120
  msgstr ""
121
 
122
+ #: facebook-commerce.php:666
123
  msgid "Reset Facebook metadata"
124
  msgstr ""
125
 
126
+ #: facebook-commerce.php:671
127
  msgid "Delete product(s) on Facebook"
128
  msgstr ""
129
 
130
+ #: facebook-commerce.php:679
131
  msgid "This product is not yet synced to Facebook."
132
  msgstr ""
133
 
134
+ #: facebook-commerce.php:1491
135
  msgid "Nothing to update for product group for %1$s"
136
  msgstr ""
137
 
138
+ #: facebook-commerce.php:1885
139
  #. translators: Placeholders %1$s - original error message from Facebook API
140
  msgid "There was an issue connecting to the Facebook API: %1$s"
141
  msgstr ""
142
 
143
+ #: facebook-commerce.php:2221
144
  msgid "Your connection has expired."
145
  msgstr ""
146
 
147
+ #: facebook-commerce.php:2221
148
  msgid ""
149
  "Please click Manage connection > Advanced Options > Update Token to refresh "
150
  "your connection to Facebook."
151
  msgstr ""
152
 
153
+ #: facebook-commerce.php:2228
154
  #. translators: Placeholders %s - error message
155
  msgid "There was an error trying to sync the products to Facebook. %s"
156
  msgstr ""
157
 
158
+ #: facebook-commerce.php:2251 facebook-commerce.php:2424
159
  msgid "Product sync is disabled."
160
  msgstr ""
161
 
162
+ #: facebook-commerce.php:2258 facebook-commerce.php:2431
163
  msgid "The plugin is not configured or the Catalog ID is missing."
164
  msgstr ""
165
 
166
+ #: facebook-commerce.php:2281
167
  msgid ""
168
  "A product sync is in progress. Please wait until the sync finishes before "
169
  "starting a new one."
170
  msgstr ""
171
 
172
+ #: facebook-commerce.php:2293 facebook-commerce.php:2445
173
  msgid ""
174
  "We've detected that your Facebook Product Catalog is no longer valid. This "
175
  "may happen if it was deleted, but could also be a temporary error. If the "
177
  "and setup the plugin again."
178
  msgstr ""
179
 
180
+ #: facebook-commerce.php:2469
181
  msgid "We couldn't create the feed or upload the product information."
182
  msgstr ""
183
 
184
+ #: facebook-commerce.php:2937
185
  msgid "Hi! We're here to answer any questions you may have."
186
  msgstr ""
187
 
188
+ #: facebook-commerce.php:3304
189
  msgid "Facebook for WooCommerce error:"
190
  msgstr ""
191
 
192
+ #: facebook-commerce.php:3386
193
  msgid ""
194
  "There was an error trying to retrieve information about the Facebook page: "
195
  "%s"
196
  msgstr ""
197
 
198
+ #: facebook-commerce.php:3437
199
  msgid "Get started with Messenger Customer Chat"
200
  msgstr ""
201
 
202
+ #: facebook-commerce.php:3438
203
  msgid "Get started with Instagram Shopping"
204
  msgstr ""
205
 
206
+ #: facebook-commerce.php:3673
207
  #. translators: Placeholders %1$s - original error message from Facebook API
208
  msgid "There was an issue connecting to the Facebook API: %s"
209
  msgstr ""
224
  msgid "A valid Order ID is required."
225
  msgstr ""
226
 
227
+ #: includes/AJAX.php:181
228
  msgid "Order ID is required"
229
  msgstr ""
230
 
231
+ #: includes/AJAX.php:185
232
  msgid "Tracking number is required"
233
  msgstr ""
234
 
235
+ #: includes/AJAX.php:189
236
  msgid "Carrier code is required"
237
  msgstr ""
238
 
239
+ #: includes/AJAX.php:195
240
  msgid "Order not found"
241
  msgstr ""
242
 
243
+ #: includes/AJAX.php:337 includes/AJAX.php:408
244
  msgid "Go to Settings"
245
  msgstr ""
246
 
247
+ #: includes/AJAX.php:342 includes/AJAX.php:413 includes/AJAX.php:479
248
  #: includes/Admin/Product_Categories.php:118
249
+ #: includes/Admin/Settings_Screens/Product_Sync.php:157 includes/Admin.php:391
250
  msgid "Cancel"
251
  msgstr ""
252
 
253
+ #: includes/AJAX.php:351
254
  #. translators: Placeholder %s - <br/> tag
255
  msgid ""
256
  "This product belongs to a category or tag that is excluded from the "
259
  "or click Cancel and update the product's category / tag assignments."
260
  msgstr ""
261
 
262
+ #: includes/AJAX.php:420
263
  msgid ""
264
  "One or more of the selected products belongs to a category or tag that is "
265
  "excluded from the Facebook catalog sync. To sync these products to "
267
  "settings."
268
  msgstr ""
269
 
270
+ #: includes/AJAX.php:473
271
  msgid "Exclude Products"
272
  msgstr ""
273
 
274
+ #: includes/AJAX.php:488
275
  #. translators: Placeholder %s - <br/> tags
276
  msgid ""
277
  "The categories and/or tags that you have selected to exclude from sync "
289
  msgstr ""
290
 
291
  #: includes/Admin/Product_Categories.php:99
292
+ #: includes/Admin/Settings_Screens/Product_Sync.php:120
293
  msgid ""
294
  "Products and categories that inherit this global setting (i.e. they do not "
295
  "have a specific Google product category set) will use the new default "
297
  msgstr ""
298
 
299
  #: includes/Admin/Product_Categories.php:122
300
+ #: includes/Admin/Settings_Screens/Product_Sync.php:161
301
  msgid "Update default Google product category"
302
  msgstr ""
303
 
316
  msgstr ""
317
 
318
  #: includes/Admin/Product_Categories.php:232
319
+ #: includes/Admin/Settings_Screens/Product_Sync.php:339
320
  msgid "Default Google product category"
321
  msgstr ""
322
 
381
  msgid "Facebook"
382
  msgstr ""
383
 
384
+ #: includes/Admin/Settings.php:120
385
  #: includes/Admin/Settings_Screens/Connection.php:34
386
  #: includes/Admin/Settings_Screens/Connection.php:35
387
  msgid "Connection"
388
  msgstr ""
389
 
390
+ #: includes/Admin/Settings.php:123
391
  #: includes/Admin/Settings_Screens/Messenger.php:40
392
  #: includes/Admin/Settings_Screens/Messenger.php:41
393
+ #: includes/Admin/Settings_Screens/Messenger.php:147
394
  msgid "Messenger"
395
  msgstr ""
396
 
397
+ #: includes/Admin/Settings.php:126
398
  #: includes/Admin/Settings_Screens/Product_Sync.php:42
399
  #: includes/Admin/Settings_Screens/Product_Sync.php:43
400
+ #: includes/Admin/Settings_Screens/Product_Sync.php:182
401
+ #: includes/Admin/Settings_Screens/Product_Sync.php:285
402
  msgid "Product sync"
403
  msgstr ""
404
 
405
+ #: includes/Admin/Settings.php:129
406
  #: includes/Admin/Settings_Screens/Advertise.php:37
407
  #: includes/Admin/Settings_Screens/Advertise.php:38
408
  msgid "Advertise"
409
  msgstr ""
410
 
411
+ #: includes/Admin/Settings.php:215
412
  msgid "You do not have permission to save these settings."
413
  msgstr ""
414
 
415
+ #: includes/Admin/Settings.php:224
416
  msgid "Your settings have been saved."
417
  msgstr ""
418
 
419
+ #: includes/Admin/Settings.php:231
420
  #. translators: Placeholders: %s - user-friendly error message
421
  msgid "Your settings could not be saved. %s"
422
  msgstr ""
442
  "touch with our support team%6$s for assistance."
443
  msgstr ""
444
 
445
+ #: includes/Admin/Settings_Screens/Connection.php:130
446
  msgid "Page"
447
  msgstr ""
448
 
449
+ #: includes/Admin/Settings_Screens/Connection.php:134
450
  msgid "Pixel"
451
  msgstr ""
452
 
453
+ #: includes/Admin/Settings_Screens/Connection.php:138
454
  msgid "Catalog"
455
  msgstr ""
456
 
457
+ #: includes/Admin/Settings_Screens/Connection.php:143
458
  msgid "Business Manager account"
459
  msgstr ""
460
 
461
+ #: includes/Admin/Settings_Screens/Connection.php:147
462
  msgid "Ad Manager account"
463
  msgstr ""
464
 
465
+ #: includes/Admin/Settings_Screens/Connection.php:151
466
  msgid "Instagram Business ID"
467
  msgstr ""
468
 
469
+ #: includes/Admin/Settings_Screens/Connection.php:155
470
  msgid "Commerce Merchant Settings ID"
471
  msgstr ""
472
 
473
+ #: includes/Admin/Settings_Screens/Connection.php:254
474
  msgid "Reach the Right People and Sell More Online"
475
  msgstr ""
476
 
477
+ #: includes/Admin/Settings_Screens/Connection.php:256
478
  msgid "Grow your business on Facebook"
479
  msgstr ""
480
 
481
+ #: includes/Admin/Settings_Screens/Connection.php:259
482
  msgid "Use this WooCommerce and Facebook integration to:"
483
  msgstr ""
484
 
485
+ #: includes/Admin/Settings_Screens/Connection.php:261
486
  msgid "Create an ad in a few steps"
487
  msgstr ""
488
 
489
+ #: includes/Admin/Settings_Screens/Connection.php:262
490
  msgid "Use built-in best practices for online sales"
491
  msgstr ""
492
 
493
+ #: includes/Admin/Settings_Screens/Connection.php:263
494
  msgid "Get reporting on sales and revenue"
495
  msgstr ""
496
 
497
+ #: includes/Admin/Settings_Screens/Connection.php:286
498
  msgid "Manage Connection"
499
  msgstr ""
500
 
501
+ #: includes/Admin/Settings_Screens/Connection.php:290
502
  msgid "Disconnect"
503
  msgstr ""
504
 
505
+ #: includes/Admin/Settings_Screens/Connection.php:296
506
  msgid "Get Started"
507
  msgstr ""
508
 
509
+ #: includes/Admin/Settings_Screens/Connection.php:321
510
  msgid "Debug"
511
  msgstr ""
512
 
513
+ #: includes/Admin/Settings_Screens/Connection.php:327
514
  msgid "Enable debug mode"
515
  msgstr ""
516
 
517
+ #: includes/Admin/Settings_Screens/Connection.php:329
518
  msgid "Log plugin events for debugging"
519
  msgstr ""
520
 
521
+ #: includes/Admin/Settings_Screens/Connection.php:330
522
  msgid "Only enable this if you are experiencing problems with the plugin."
523
  msgstr ""
524
 
525
+ #: includes/Admin/Settings_Screens/Messenger.php:121
526
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
527
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
528
  msgstr ""
529
 
530
+ #: includes/Admin/Settings_Screens/Messenger.php:153
531
  msgid "Enable Messenger"
532
  msgstr ""
533
 
534
+ #: includes/Admin/Settings_Screens/Messenger.php:155
535
  msgid "Enable and customize Facebook Messenger on your store"
536
  msgstr ""
537
 
538
+ #: includes/Admin/Settings_Screens/Messenger.php:165
539
  msgid "Language"
540
  msgstr ""
541
 
542
+ #: includes/Admin/Settings_Screens/Messenger.php:170
543
  msgid "Greeting & Colors"
544
  msgstr ""
545
 
546
+ #: includes/Admin/Settings_Screens/Messenger.php:192
547
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
548
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger."
549
  msgstr ""
550
 
551
+ #: includes/Admin/Settings_Screens/Messenger.php:245
552
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
553
  msgid ""
554
  "There was an error communicating with the Facebook Business Extension. "
555
  "%1$sClick here%2$s to manage your Messenger settings."
556
  msgstr ""
557
 
558
+ #: includes/Admin/Settings_Screens/Messenger.php:317
559
  msgid "Please try again."
560
  msgstr ""
561
 
564
  msgid "Product sets"
565
  msgstr ""
566
 
567
+ #: includes/Admin/Settings_Screens/Product_Sync.php:81
568
  #. translators: Placeholders: {count} number of remaining items
569
  msgid "{count} item remaining."
570
  msgid_plural "{count} items remaining."
571
  msgstr[0] ""
572
  msgstr[1] ""
573
 
574
+ #: includes/Admin/Settings_Screens/Product_Sync.php:96
575
  #. translators: Placeholders %s - html code for a spinner icon
576
  msgid "Your products will now be resynced to Facebook, this may take some time."
577
  msgstr ""
578
 
579
+ #: includes/Admin/Settings_Screens/Product_Sync.php:97
580
  msgid ""
581
  "Facebook for WooCommerce automatically syncs your products on "
582
  "create/update. Are you sure you want to force product resync?\n"
586
  "did not sync."
587
  msgstr ""
588
 
589
+ #: includes/Admin/Settings_Screens/Product_Sync.php:98
590
  msgid "Your products are syncing - you may safely leave this page %s"
591
  msgstr ""
592
 
593
+ #: includes/Admin/Settings_Screens/Product_Sync.php:101
594
  msgid "There was an error trying to sync the products to Facebook."
595
  msgstr ""
596
 
597
+ #: includes/Admin/Settings_Screens/Product_Sync.php:102
598
  msgid ""
599
  "Something went wrong while uploading the product information, please try "
600
  "again."
601
  msgstr ""
602
 
603
+ #: includes/Admin/Settings_Screens/Product_Sync.php:135
604
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
605
  msgid ""
606
  "Products and categories that inherit this global setting (they do not have "
610
  "Are you sure you want to proceed?"
611
  msgstr ""
612
 
613
+ #: includes/Admin/Settings_Screens/Product_Sync.php:190
614
  msgid "Sync products"
615
  msgstr ""
616
 
617
+ #: includes/Admin/Settings_Screens/Product_Sync.php:290
618
  msgid "Enable product sync"
619
  msgstr ""
620
 
621
+ #: includes/Admin/Settings_Screens/Product_Sync.php:298
622
  msgid "Exclude categories from sync"
623
  msgstr ""
624
 
625
+ #: includes/Admin/Settings_Screens/Product_Sync.php:302
626
  msgid "Products in one or more of these categories will not sync to Facebook."
627
  msgstr ""
628
 
629
+ #: includes/Admin/Settings_Screens/Product_Sync.php:306
630
  msgid "Search for a product category&hellip;"
631
  msgstr ""
632
 
633
+ #: includes/Admin/Settings_Screens/Product_Sync.php:312
634
  msgid "Exclude tags from sync"
635
  msgstr ""
636
 
637
+ #: includes/Admin/Settings_Screens/Product_Sync.php:316
638
  msgid "Products with one or more of these tags will not sync to Facebook."
639
  msgstr ""
640
 
641
+ #: includes/Admin/Settings_Screens/Product_Sync.php:320
642
  msgid "Search for a product tag&hellip;"
643
  msgstr ""
644
 
645
+ #: includes/Admin/Settings_Screens/Product_Sync.php:326
646
  msgid "Product description sync"
647
  msgstr ""
648
 
649
+ #: includes/Admin/Settings_Screens/Product_Sync.php:329
650
  msgid "Choose which product description to display in the Facebook catalog."
651
  msgstr ""
652
 
653
+ #: includes/Admin/Settings_Screens/Product_Sync.php:332
654
  msgid "Standard description"
655
  msgstr ""
656
 
657
+ #: includes/Admin/Settings_Screens/Product_Sync.php:333
658
  msgid "Short description"
659
  msgstr ""
660
 
661
+ #: includes/Admin/Settings_Screens/Product_Sync.php:340
662
  msgid ""
663
  "Choose a default Google product category for your products. Defaults can "
664
  "also be set for product categories. Products need at least two category "
665
  "levels defined for tax to be correctly applied."
666
  msgstr ""
667
 
668
+ #: includes/Admin/Settings_Screens/Product_Sync.php:386
669
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
670
  msgid "Please %1$sconnect to Facebook%2$s to enable and manage product sync."
671
  msgstr ""
674
  msgid "The name is how it appears on Facebook Catalog."
675
  msgstr ""
676
 
677
+ #: includes/Admin.php:212
678
  msgid ""
679
  "Please enter a Google product category and at least one sub-category to "
680
  "sell this product on Instagram."
681
  msgstr ""
682
 
683
+ #: includes/Admin.php:238
684
  msgid "Search main categories..."
685
  msgstr ""
686
 
687
+ #: includes/Admin.php:239
688
  msgid "Choose a main category"
689
  msgstr ""
690
 
691
+ #: includes/Admin.php:240
692
  msgid "Choose a category"
693
  msgstr ""
694
 
695
+ #: includes/Admin.php:279
696
  msgid ""
697
  "To sell this product on Instagram, please ensure it meets the following "
698
  "requirements:"
699
  msgstr ""
700
 
701
+ #: includes/Admin.php:282
702
  msgid "Has a price defined"
703
  msgstr ""
704
 
705
+ #: includes/Admin.php:288
706
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s -
707
  #. </strong> closing HTML tag
708
  msgid "Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab"
709
  msgstr ""
710
 
711
+ #: includes/Admin.php:300
712
  #. translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s -
713
  #. </strong> closing HTML tag
714
  msgid ""
716
  "and hide\""
717
  msgstr ""
718
 
719
+ #: includes/Admin.php:329
720
  msgid "Close"
721
  msgstr ""
722
 
723
+ #: includes/Admin.php:354
724
  #. translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a>
725
  #. link tag
726
  msgid ""
729
  "the Facebook catalog as well?"
730
  msgstr ""
731
 
732
+ #: includes/Admin.php:383
733
  msgid "Remove from sync only"
734
  msgstr ""
735
 
736
+ #: includes/Admin.php:387
737
  msgid "Remove from sync and delete"
738
  msgstr ""
739
 
740
+ #: includes/Admin.php:423 includes/Admin.php:1266 includes/Admin.php:1408
741
  msgid "Facebook sync"
742
  msgstr ""
743
 
744
+ #: includes/Admin.php:450 includes/Admin.php:481
745
  msgid "Sync and show"
746
  msgstr ""
747
 
748
+ #: includes/Admin.php:452 includes/Admin.php:482
749
  msgid "Sync and hide"
750
  msgstr ""
751
 
752
+ #: includes/Admin.php:456 includes/Admin.php:483 includes/Admin.php:1270
753
+ #: includes/Admin.php:1412
754
  msgid "Do not sync"
755
  msgstr ""
756
 
757
+ #: includes/Admin.php:480
758
  msgid "Filter by Facebook sync setting"
759
  msgstr ""
760
 
761
+ #: includes/Admin.php:893
762
  msgid "Include in Facebook sync"
763
  msgstr ""
764
 
765
+ #: includes/Admin.php:894
766
  msgid "Exclude from Facebook sync"
767
  msgstr ""
768
 
769
+ #: includes/Admin.php:1087
770
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
771
  #. <a> tag, %4$s - <a> tag
772
  msgid ""
780
  msgstr[0] ""
781
  msgstr[1] ""
782
 
783
+ #: includes/Admin.php:1094
784
  msgid "Don't show this notice again"
785
  msgstr ""
786
 
787
+ #: includes/Admin.php:1128
788
  #. translators: Placeholders: %1$s - number of affected products, %2$s opening
789
  #. HTML <a> tag, %3$s - closing HTML </a> tag, %4$s - opening HTML <a> tag,
790
  #. %5$s - closing HTML </a> tag
801
  msgstr[0] ""
802
  msgstr[1] ""
803
 
804
+ #: includes/Admin.php:1194
805
  #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing
806
  #. HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a>
807
  #. tag
811
  "your Facebook catalog. You may still advertise Virtual products on Facebook."
812
  msgstr ""
813
 
814
+ #: includes/Admin.php:1268 includes/Admin.php:1410
815
  msgid "Sync and show in catalog"
816
  msgstr ""
817
 
818
+ #: includes/Admin.php:1269 includes/Admin.php:1411
819
  msgid "Sync and hide in catalog"
820
  msgstr ""
821
 
822
+ #: includes/Admin.php:1279 includes/Admin.php:1424
823
  msgid "Facebook Description"
824
  msgstr ""
825
 
826
+ #: includes/Admin.php:1281 includes/Admin.php:1426
827
  msgid ""
828
  "Custom (plain-text only) description for product on Facebook. If blank, "
829
  "product description will be used. If product description is blank, "
830
  "shortname will be used."
831
  msgstr ""
832
 
833
+ #: includes/Admin.php:1292 includes/Admin.php:1439
834
  msgid "Facebook Product Image"
835
  msgstr ""
836
 
837
+ #: includes/Admin.php:1294 includes/Admin.php:1441
838
  msgid ""
839
  "Choose the product image that should be synced to the Facebook catalog for "
840
  "this product. If using a custom image, please enter an absolute URL (e.g. "
841
  "https://domain.com/image.jpg)."
842
  msgstr ""
843
 
844
+ #: includes/Admin.php:1296
845
  msgid "Use WooCommerce image"
846
  msgstr ""
847
 
848
+ #: includes/Admin.php:1297 includes/Admin.php:1445
849
  msgid "Use custom image"
850
  msgstr ""
851
 
852
+ #: includes/Admin.php:1308 includes/Admin.php:1457
853
  msgid "Custom Image URL"
854
  msgstr ""
855
 
856
+ #: includes/Admin.php:1319 includes/Admin.php:1470
857
  #. translators: Placeholders %1$s - WC currency symbol
858
  msgid "Facebook Price (%1$s)"
859
  msgstr ""
860
 
861
+ #: includes/Admin.php:1323 includes/Admin.php:1474
862
  msgid ""
863
  "Custom price for product on Facebook. Please enter in monetary decimal (.) "
864
  "format without thousand separators and currency symbols. If blank, product "
865
  "price will be used."
866
  msgstr ""
867
 
868
+ #: includes/Admin.php:1443
869
  msgid "Use variation image"
870
  msgstr ""
871
 
872
+ #: includes/Admin.php:1444
873
  msgid "Use parent image"
874
  msgstr ""
875
 
876
+ #: includes/Admin.php:1598
877
  msgid "Close modal panel"
878
  msgstr ""
879
 
880
+ #: includes/Commerce/Orders.php:432
881
  #. translators: Placeholders: %1$s - order remote id, %2$s - order created by
882
  msgid "Order %1$s paid in %2$s"
883
  msgstr ""
884
 
885
+ #: includes/Commerce/Orders.php:584 includes/Commerce/Orders.php:819
886
  msgid "Remote ID not found."
887
  msgstr ""
888
 
889
+ #: includes/Commerce/Orders.php:591
890
  msgid "%s is not a valid shipping carrier code."
891
  msgstr ""
892
 
893
+ #: includes/Commerce/Orders.php:609 includes/Commerce/Orders.php:786
894
  msgid "No valid Facebook products were found."
895
  msgstr ""
896
 
897
+ #: includes/Commerce/Orders.php:627
898
  #. translators: Placeholder: %s - sales channel name, like Facebook or
899
  #. Instagram
900
  msgid "%s order fulfilled."
901
  msgstr ""
902
 
903
+ #: includes/Commerce/Orders.php:637
904
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
905
  #. Instagram, %2$s - error message
906
  msgid "%1$s order could not be fulfilled. %2$s"
907
  msgstr ""
908
 
909
+ #: includes/Commerce/Orders.php:681
910
  msgid "Parent order not found."
911
  msgstr ""
912
 
913
+ #: includes/Commerce/Orders.php:687
914
  msgid "Remote ID for parent order not found."
915
  msgstr ""
916
 
917
+ #: includes/Commerce/Orders.php:718
918
  #. translators: Placeholder: %s - sales channel name, like Facebook or
919
  #. Instagram
920
  msgid "Order refunded on %s."
921
  msgstr ""
922
 
923
+ #: includes/Commerce/Orders.php:730
924
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
925
  #. Instagram, %2$s - error message
926
  msgid "Could not refund %1$s order: %2$s"
927
  msgstr ""
928
 
929
+ #: includes/Commerce/Orders.php:827
930
  #. translators: Placeholder: %s - sales channel name, like Facebook or
931
  #. Instagram
932
  msgid "%s order cancelled."
933
  msgstr ""
934
 
935
+ #: includes/Commerce/Orders.php:837
936
  #. translators: Placeholders: %1$s - sales channel name, like Facebook or
937
  #. Instagram, %2$s - error message
938
  msgid "%1$s order could not be cancelled. %2$s"
939
  msgstr ""
940
 
941
+ #: includes/Commerce/Orders.php:859
942
  msgid "Customer requested cancellation"
943
  msgstr ""
944
 
945
+ #: includes/Commerce/Orders.php:860
946
  msgid "Product(s) are out of stock"
947
  msgstr ""
948
 
949
+ #: includes/Commerce/Orders.php:861
950
  msgid "Customer address is invalid"
951
  msgstr ""
952
 
953
+ #: includes/Commerce/Orders.php:862
954
  msgid "Suspicious order"
955
  msgstr ""
956
 
957
+ #: includes/Commerce/Orders.php:863
958
  msgid "Other"
959
  msgstr ""
960
 
961
+ #: includes/Handlers/Connection.php:316
962
  msgid "Connection successful!"
963
  msgstr ""
964
 
965
+ #: includes/Handlers/Connection.php:347
966
  msgid "You do not have permission to uninstall Facebook Business Extension."
967
  msgstr ""
968
 
969
+ #: includes/Handlers/Connection.php:357
970
  msgid "Disconnection successful."
971
  msgstr ""
972
 
973
+ #: includes/Handlers/Connection.php:422
974
  #. translators: Placeholders: %s - API error message
975
  msgid "Could not retrieve page access data. %s"
976
  msgstr ""
977
 
978
+ #: includes/Handlers/Connection.php:436
979
  #. translators: Placeholders: %s - Facebook page ID
980
  msgid "Page %s not authorized."
981
  msgstr ""
982
 
983
+ #: includes/Handlers/Connection.php:1385
984
  msgid "You do not have permission to finish App Store login."
985
  msgstr ""
986
 
992
  msgid "Job data key \"%s\" is not an array"
993
  msgstr ""
994
 
995
+ #: includes/Products/Sync/Background.php:157
996
  msgid ""
997
  "There was an error trying sync products using the Catalog Batch API for job "
998
+ "%1$s: %2$s"
999
  msgstr ""
1000
 
1001
  #: includes/fbgraph.php:105
1002
+ msgid "HTTP %1$s: %2$s"
1003
  msgstr ""
1004
 
1005
  #: includes/fbinfobanner.php:211
1014
  msgid "Dismiss"
1015
  msgstr ""
1016
 
1017
+ #: includes/fbproductfeed.php:465
1018
  msgid "Could not create product catalog feed directory"
1019
  msgstr ""
1020
 
1021
+ #: includes/fbproductfeed.php:530
1022
  msgid "Could not open the product catalog temporary feed file for writing"
1023
  msgstr ""
1024
 
1025
+ #: includes/fbproductfeed.php:537
1026
  msgid "Could not open the product catalog feed file for writing"
1027
  msgstr ""
1028
 
1029
+ #: includes/fbproductfeed.php:580
1030
  msgid "Could not rename the product catalog feed file"
1031
  msgstr ""
1032
 
1067
  msgid "https://www.facebook.com/"
1068
  msgstr ""
1069
 
1070
+ #: includes/Locale.php:180
1071
  msgctxt "language"
1072
  msgid "English (United States)"
1073
  msgstr ""
includes/AJAX.php CHANGED
@@ -41,26 +41,26 @@ class AJAX {
41
  public function __construct() {
42
 
43
  // maybe output a modal prompt when toggling product sync in bulk or individual product actions
44
- add_action( 'wp_ajax_facebook_for_woocommerce_set_product_sync_prompt', [ $this, 'handle_set_product_sync_prompt' ] );
45
- add_action( 'wp_ajax_facebook_for_woocommerce_set_product_sync_bulk_action_prompt', [ $this, 'handle_set_product_sync_bulk_action_prompt' ] );
46
 
47
  // maybe output a modal prompt when setting excluded terms
48
- add_action( 'wp_ajax_facebook_for_woocommerce_set_excluded_terms_prompt', [ $this, 'handle_set_excluded_terms_prompt' ] );
49
 
50
  // sync all products via AJAX
51
- add_action( 'wp_ajax_wc_facebook_sync_products', [ $this, 'sync_products' ] );
52
 
53
  // get the current sync status
54
- add_action( 'wp_ajax_wc_facebook_get_sync_status', [ $this, 'get_sync_status' ] );
55
 
56
  // search a product's attributes for the given term
57
- add_action( 'wp_ajax_' . self::ACTION_SEARCH_PRODUCT_ATTRIBUTES, [ $this, 'admin_search_product_attributes' ] );
58
 
59
  // complete a Facebook order for the given order ID
60
- add_action( 'wp_ajax_' . self::ACTION_COMPLETE_ORDER, [ $this, 'admin_complete_order' ] );
61
 
62
  // cancel facebook order by the given order ID
63
- add_action( 'wp_ajax_' . self::ACTION_CANCEL_ORDER, [ $this, 'admin_cancel_order' ] );
64
  }
65
 
66
 
@@ -139,11 +139,15 @@ class AJAX {
139
  $attributes = Admin\Products::get_available_product_attribute_names( $product );
140
 
141
  // filter out any attributes whose slug or proper name don't at least partially match the search term
142
- $results = array_filter( $attributes, function( $name, $slug ) use ( $term ) {
 
 
143
 
144
- return false !== stripos( $name, $term ) || false !== stripos( $slug, $term );
145
 
146
- }, ARRAY_FILTER_USE_BOTH );
 
 
147
 
148
  wp_send_json( $results );
149
 
@@ -232,9 +236,11 @@ class AJAX {
232
 
233
  $remaining_products = 0;
234
 
235
- $jobs = facebook_for_woocommerce()->get_products_sync_background_handler()->get_jobs( [
236
- 'status' => 'processing',
237
- ] );
 
 
238
 
239
  if ( ! empty( $jobs ) ) {
240
 
@@ -246,7 +252,6 @@ class AJAX {
246
  if ( ! empty( $job->progress ) ) {
247
  $remaining_products -= $job->progress;
248
  }
249
-
250
  }
251
 
252
  wp_send_json_success( $remaining_products );
@@ -265,17 +270,17 @@ class AJAX {
265
  check_ajax_referer( 'set-product-sync-prompt', 'security' );
266
 
267
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
268
- $product_id = isset( $_POST['product'] ) ? (int) $_POST['product'] : 0;
269
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
270
- $sync_enabled = isset( $_POST['sync_enabled'] ) ? (string) $_POST['sync_enabled'] : '';
271
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
272
  $var_sync_enabled = isset( $_POST['var_sync_enabled'] ) ? (string) $_POST['var_sync_enabled'] : '';
273
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
274
- $product_cats = isset( $_POST['categories'] ) ? (array) $_POST['categories'] : [];
275
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
276
- $product_tags = isset( $_POST['tags'] ) ? (array) $_POST['tags'] : [];
277
 
278
- if ( $product_id > 0 && in_array( $var_sync_enabled, [ 'enabled', 'disabled' ], true ) && in_array( $sync_enabled, [ 'enabled', 'disabled' ], true ) ) {
279
 
280
  $product = wc_get_product( $product_id );
281
 
@@ -296,7 +301,7 @@ class AJAX {
296
  // try next with tags, but WordPress only gives us tag names
297
  if ( ! $has_excluded_terms && ! empty( $product_tags ) ) {
298
 
299
- $product_tag_ids = [];
300
 
301
  foreach ( $product_tags as $product_tag_name_or_id ) {
302
 
@@ -339,14 +344,16 @@ class AJAX {
339
 
340
  $buttons = ob_get_clean();
341
 
342
- wp_send_json_error( [
343
- 'message' => sprintf(
344
- /* translators: Placeholder %s - <br/> tag */
345
- __( '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.', 'facebook-for-woocommerce' ),
346
- '<br/><br/>'
347
- ),
348
- 'buttons' => $buttons,
349
- ] );
 
 
350
  }
351
  }
352
  }
@@ -368,9 +375,9 @@ class AJAX {
368
  check_ajax_referer( 'set-product-sync-bulk-action-prompt', 'security' );
369
 
370
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
371
- $product_ids = isset( $_POST['products'] ) ? (array) $_POST['products'] : [];
372
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
373
- $toggle = isset( $_POST['toggle'] ) ? (string) $_POST['toggle'] : '';
374
 
375
  if ( ! empty( $product_ids ) && ! empty( $toggle ) && 'facebook_include' === $toggle ) {
376
 
@@ -389,7 +396,7 @@ class AJAX {
389
  }
390
 
391
  // show modal if there's at least one product that belongs to an excluded term
392
- if ( $has_excluded_term ) {
393
 
394
  ob_start();
395
 
@@ -408,10 +415,12 @@ class AJAX {
408
 
409
  $buttons = ob_get_clean();
410
 
411
- wp_send_json_error( [
412
- 'message' => __( '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.', 'facebook-for-woocommerce' ),
413
- 'buttons' => $buttons,
414
- ] );
 
 
415
  }
416
  }
417
 
@@ -431,12 +440,12 @@ class AJAX {
431
  check_ajax_referer( 'set-excluded-terms-prompt', 'security' );
432
 
433
  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
434
- $posted_categories = isset( $_POST['categories'] ) ? wp_unslash( $_POST['categories'] ) : [];
435
  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
436
- $posted_tags = isset( $_POST['tags'] ) ? wp_unslash( $_POST['tags'] ) : [];
437
 
438
- $new_category_ids = [];
439
- $new_tag_ids = [];
440
 
441
  if ( ! empty( $posted_categories ) ) {
442
  foreach ( $posted_categories as $posted_category_id ) {
@@ -472,14 +481,16 @@ class AJAX {
472
 
473
  $buttons = ob_get_clean();
474
 
475
- wp_send_json_error( [
476
- 'message' => sprintf(
477
- /* translators: Placeholder %s - <br/> tags */
478
- __( '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.', 'facebook-for-woocommerce' ),
479
- '<br/><br/>'
480
- ),
481
- 'buttons' => $buttons,
482
- ] );
 
 
483
 
484
  } else {
485
 
@@ -501,34 +512,34 @@ class AJAX {
501
  * @param string[] $new_excluded_tags
502
  * @return int[]
503
  */
504
- private function get_products_to_be_excluded( $new_excluded_categories = [], $new_excluded_tags = [] ) {
505
 
506
  // products with sync enabled
507
- $sync_enabled_meta_query = [
508
  'relation' => 'OR',
509
- [
510
  'key' => Products::SYNC_ENABLED_META_KEY,
511
  'value' => 'yes',
512
- ],
513
- [
514
  'key' => Products::SYNC_ENABLED_META_KEY,
515
  'compare' => 'NOT EXISTS',
516
- ],
517
- ];
518
 
519
- $products_query_vars = [
520
  'post_type' => 'product',
521
  'fields' => 'ids',
522
  'meta_query' => $sync_enabled_meta_query,
523
- ];
524
 
525
  if ( ! empty( $new_excluded_categories ) ) {
526
 
527
  // products that belong to the new excluded categories
528
- $categories_tax_query = [
529
  'taxonomy' => 'product_cat',
530
  'terms' => $new_excluded_categories,
531
- ];
532
 
533
  if ( $integration = facebook_for_woocommerce()->get_integration() ) {
534
 
@@ -537,15 +548,15 @@ class AJAX {
537
 
538
  if ( ! empty( $saved_excluded_categories ) ) {
539
 
540
- $categories_tax_query = [
541
  'relation' => 'AND',
542
  $categories_tax_query,
543
- [
544
  'taxonomy' => 'product_cat',
545
  'terms' => $saved_excluded_categories,
546
  'operator' => 'NOT IN',
547
- ],
548
- ];
549
  }
550
  }
551
 
@@ -555,10 +566,10 @@ class AJAX {
555
  if ( ! empty( $new_excluded_tags ) ) {
556
 
557
  // products that belong to the new excluded tags
558
- $tags_tax_query = [
559
  'taxonomy' => 'product_tag',
560
  'terms' => $new_excluded_tags,
561
- ];
562
 
563
  if ( $integration = facebook_for_woocommerce()->get_integration() ) {
564
 
@@ -567,15 +578,15 @@ class AJAX {
567
  if ( ! empty( $save_excluded_tags ) ) {
568
 
569
  // products that do not belong to the saved excluded tags
570
- $tags_tax_query = [
571
  'relation' => 'AND',
572
  $tags_tax_query,
573
- [
574
  'taxonomy' => 'product_tag',
575
  'terms' => $save_excluded_tags,
576
  'operator' => 'NOT IN',
577
- ],
578
- ];
579
  }
580
  }
581
 
@@ -585,11 +596,11 @@ class AJAX {
585
 
586
  } else {
587
 
588
- $products_query_vars['tax_query'] = [
589
  'relation' => 'OR',
590
  $products_query_vars,
591
  $tags_tax_query,
592
- ];
593
  }
594
  }
595
 
41
  public function __construct() {
42
 
43
  // maybe output a modal prompt when toggling product sync in bulk or individual product actions
44
+ add_action( 'wp_ajax_facebook_for_woocommerce_set_product_sync_prompt', array( $this, 'handle_set_product_sync_prompt' ) );
45
+ add_action( 'wp_ajax_facebook_for_woocommerce_set_product_sync_bulk_action_prompt', array( $this, 'handle_set_product_sync_bulk_action_prompt' ) );
46
 
47
  // maybe output a modal prompt when setting excluded terms
48
+ add_action( 'wp_ajax_facebook_for_woocommerce_set_excluded_terms_prompt', array( $this, 'handle_set_excluded_terms_prompt' ) );
49
 
50
  // sync all products via AJAX
51
+ add_action( 'wp_ajax_wc_facebook_sync_products', array( $this, 'sync_products' ) );
52
 
53
  // get the current sync status
54
+ add_action( 'wp_ajax_wc_facebook_get_sync_status', array( $this, 'get_sync_status' ) );
55
 
56
  // search a product's attributes for the given term
57
+ add_action( 'wp_ajax_' . self::ACTION_SEARCH_PRODUCT_ATTRIBUTES, array( $this, 'admin_search_product_attributes' ) );
58
 
59
  // complete a Facebook order for the given order ID
60
+ add_action( 'wp_ajax_' . self::ACTION_COMPLETE_ORDER, array( $this, 'admin_complete_order' ) );
61
 
62
  // cancel facebook order by the given order ID
63
+ add_action( 'wp_ajax_' . self::ACTION_CANCEL_ORDER, array( $this, 'admin_cancel_order' ) );
64
  }
65
 
66
 
139
  $attributes = Admin\Products::get_available_product_attribute_names( $product );
140
 
141
  // filter out any attributes whose slug or proper name don't at least partially match the search term
142
+ $results = array_filter(
143
+ $attributes,
144
+ function( $name, $slug ) use ( $term ) {
145
 
146
+ return false !== stripos( $name, $term ) || false !== stripos( $slug, $term );
147
 
148
+ },
149
+ ARRAY_FILTER_USE_BOTH
150
+ );
151
 
152
  wp_send_json( $results );
153
 
236
 
237
  $remaining_products = 0;
238
 
239
+ $jobs = facebook_for_woocommerce()->get_products_sync_background_handler()->get_jobs(
240
+ array(
241
+ 'status' => 'processing',
242
+ )
243
+ );
244
 
245
  if ( ! empty( $jobs ) ) {
246
 
252
  if ( ! empty( $job->progress ) ) {
253
  $remaining_products -= $job->progress;
254
  }
 
255
  }
256
 
257
  wp_send_json_success( $remaining_products );
270
  check_ajax_referer( 'set-product-sync-prompt', 'security' );
271
 
272
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
273
+ $product_id = isset( $_POST['product'] ) ? (int) $_POST['product'] : 0;
274
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
275
+ $sync_enabled = isset( $_POST['sync_enabled'] ) ? (string) $_POST['sync_enabled'] : '';
276
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
277
  $var_sync_enabled = isset( $_POST['var_sync_enabled'] ) ? (string) $_POST['var_sync_enabled'] : '';
278
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
279
+ $product_cats = isset( $_POST['categories'] ) ? (array) $_POST['categories'] : array();
280
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
281
+ $product_tags = isset( $_POST['tags'] ) ? (array) $_POST['tags'] : array();
282
 
283
+ if ( $product_id > 0 && in_array( $var_sync_enabled, array( 'enabled', 'disabled' ), true ) && in_array( $sync_enabled, array( 'enabled', 'disabled' ), true ) ) {
284
 
285
  $product = wc_get_product( $product_id );
286
 
301
  // try next with tags, but WordPress only gives us tag names
302
  if ( ! $has_excluded_terms && ! empty( $product_tags ) ) {
303
 
304
+ $product_tag_ids = array();
305
 
306
  foreach ( $product_tags as $product_tag_name_or_id ) {
307
 
344
 
345
  $buttons = ob_get_clean();
346
 
347
+ wp_send_json_error(
348
+ array(
349
+ 'message' => sprintf(
350
+ /* translators: Placeholder %s - <br/> tag */
351
+ __( '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.', 'facebook-for-woocommerce' ),
352
+ '<br/><br/>'
353
+ ),
354
+ 'buttons' => $buttons,
355
+ )
356
+ );
357
  }
358
  }
359
  }
375
  check_ajax_referer( 'set-product-sync-bulk-action-prompt', 'security' );
376
 
377
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
378
+ $product_ids = isset( $_POST['products'] ) ? (array) $_POST['products'] : array();
379
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
380
+ $toggle = isset( $_POST['toggle'] ) ? (string) $_POST['toggle'] : '';
381
 
382
  if ( ! empty( $product_ids ) && ! empty( $toggle ) && 'facebook_include' === $toggle ) {
383
 
396
  }
397
 
398
  // show modal if there's at least one product that belongs to an excluded term
399
+ if ( $has_excluded_term ) {
400
 
401
  ob_start();
402
 
415
 
416
  $buttons = ob_get_clean();
417
 
418
+ wp_send_json_error(
419
+ array(
420
+ 'message' => __( '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.', 'facebook-for-woocommerce' ),
421
+ 'buttons' => $buttons,
422
+ )
423
+ );
424
  }
425
  }
426
 
440
  check_ajax_referer( 'set-excluded-terms-prompt', 'security' );
441
 
442
  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
443
+ $posted_categories = isset( $_POST['categories'] ) ? wp_unslash( $_POST['categories'] ) : array();
444
  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
445
+ $posted_tags = isset( $_POST['tags'] ) ? wp_unslash( $_POST['tags'] ) : array();
446
 
447
+ $new_category_ids = array();
448
+ $new_tag_ids = array();
449
 
450
  if ( ! empty( $posted_categories ) ) {
451
  foreach ( $posted_categories as $posted_category_id ) {
481
 
482
  $buttons = ob_get_clean();
483
 
484
+ wp_send_json_error(
485
+ array(
486
+ 'message' => sprintf(
487
+ /* translators: Placeholder %s - <br/> tags */
488
+ __( '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.', 'facebook-for-woocommerce' ),
489
+ '<br/><br/>'
490
+ ),
491
+ 'buttons' => $buttons,
492
+ )
493
+ );
494
 
495
  } else {
496
 
512
  * @param string[] $new_excluded_tags
513
  * @return int[]
514
  */
515
+ private function get_products_to_be_excluded( $new_excluded_categories = array(), $new_excluded_tags = array() ) {
516
 
517
  // products with sync enabled
518
+ $sync_enabled_meta_query = array(
519
  'relation' => 'OR',
520
+ array(
521
  'key' => Products::SYNC_ENABLED_META_KEY,
522
  'value' => 'yes',
523
+ ),
524
+ array(
525
  'key' => Products::SYNC_ENABLED_META_KEY,
526
  'compare' => 'NOT EXISTS',
527
+ ),
528
+ );
529
 
530
+ $products_query_vars = array(
531
  'post_type' => 'product',
532
  'fields' => 'ids',
533
  'meta_query' => $sync_enabled_meta_query,
534
+ );
535
 
536
  if ( ! empty( $new_excluded_categories ) ) {
537
 
538
  // products that belong to the new excluded categories
539
+ $categories_tax_query = array(
540
  'taxonomy' => 'product_cat',
541
  'terms' => $new_excluded_categories,
542
+ );
543
 
544
  if ( $integration = facebook_for_woocommerce()->get_integration() ) {
545
 
548
 
549
  if ( ! empty( $saved_excluded_categories ) ) {
550
 
551
+ $categories_tax_query = array(
552
  'relation' => 'AND',
553
  $categories_tax_query,
554
+ array(
555
  'taxonomy' => 'product_cat',
556
  'terms' => $saved_excluded_categories,
557
  'operator' => 'NOT IN',
558
+ ),
559
+ );
560
  }
561
  }
562
 
566
  if ( ! empty( $new_excluded_tags ) ) {
567
 
568
  // products that belong to the new excluded tags
569
+ $tags_tax_query = array(
570
  'taxonomy' => 'product_tag',
571
  'terms' => $new_excluded_tags,
572
+ );
573
 
574
  if ( $integration = facebook_for_woocommerce()->get_integration() ) {
575
 
578
  if ( ! empty( $save_excluded_tags ) ) {
579
 
580
  // products that do not belong to the saved excluded tags
581
+ $tags_tax_query = array(
582
  'relation' => 'AND',
583
  $tags_tax_query,
584
+ array(
585
  'taxonomy' => 'product_tag',
586
  'terms' => $save_excluded_tags,
587
  'operator' => 'NOT IN',
588
+ ),
589
+ );
590
  }
591
  }
592
 
596
 
597
  } else {
598
 
599
+ $products_query_vars['tax_query'] = array(
600
  'relation' => 'OR',
601
  $products_query_vars,
602
  $tags_tax_query,
603
+ );
604
  }
605
  }
606
 
includes/API.php CHANGED
@@ -49,9 +49,9 @@ class API extends Framework\SV_WC_API_Base {
49
 
50
  $this->access_token = $access_token;
51
 
52
- $this->request_headers = [
53
  'Authorization' => "Bearer {$access_token}",
54
- ];
55
 
56
  $this->set_request_content_type_header( 'application/json' );
57
  $this->set_request_accept_header( 'application/json' );
@@ -148,7 +148,7 @@ class API extends Framework\SV_WC_API_Base {
148
  * @link https://developers.facebook.com/docs/graph-api/using-graph-api/error-handling#rate-limiting-error-codes
149
  * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/batch/#validation-rules
150
  */
151
- if ( in_array( $code, [ 4, 17, 32, 613, 80001, 80004 ], true ) ) {
152
 
153
  $delay_in_seconds = $this->calculate_rate_limit_delay( $response, $this->get_response_headers() );
154
 
@@ -172,7 +172,7 @@ class API extends Framework\SV_WC_API_Base {
172
  *
173
  * @link https://developers.facebook.com/docs/graph-api/using-graph-api/error-handling#errorcodes
174
  */
175
- if ( ( $code >= 200 && $code < 300 ) || in_array( $code, [ 10, 102, 190 ], false ) ) {
176
  set_transient( 'wc_facebook_connection_invalid', time(), DAY_IN_SECONDS );
177
  } else {
178
  // this was an unrelated error, so the OAuth connection may still be valid
@@ -203,7 +203,7 @@ class API extends Framework\SV_WC_API_Base {
203
  * @since 2.1.0
204
  *
205
  * @param string $rate_limit_id ID for the API request
206
- * @param int $timestamp timestamp until the delay is over
207
  * @throws API\Exceptions\Request_Limit_Reached
208
  */
209
  private function handle_throttled_request( $rate_limit_id, $timestamp ) {
@@ -345,12 +345,12 @@ class API extends Framework\SV_WC_API_Base {
345
  *
346
  * @since 2.0.0
347
  *
348
- * @param string $external_business_id external business ID
349
  * @param API\FBE\Configuration\Messenger $configuration messenger configuration
350
  * @return Response
351
  * @throws Framework\SV_WC_API_Exception
352
  */
353
- public function update_messenger_configuration( $external_business_id, API\FBE\Configuration\Messenger $configuration ) {
354
 
355
  $request = new API\FBE\Configuration\Update\Request( $external_business_id );
356
 
@@ -370,8 +370,8 @@ class API extends Framework\SV_WC_API_Base {
370
  * @since 2.0.0
371
  *
372
  * @param string $catalog_id catalog ID
373
- * @param array $requests array of prefixed product IDs to create, update or remove
374
- * @param bool $allow_upsert whether to allow updates to insert new items
375
  * @return \SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates\Response
376
  * @throws Framework\SV_WC_API_Exception
377
  */
@@ -394,16 +394,18 @@ class API extends Framework\SV_WC_API_Base {
394
  * @since 2.0.0
395
  *
396
  * @param string $catalog_id catalog ID
397
- * @param array $data product group data
398
  * @return Response
399
  * @throws Framework\SV_WC_API_Exception
400
  */
401
  public function create_product_group( $catalog_id, $data ) {
402
 
403
- $request = $this->get_new_request( [
404
- 'path' => "/{$catalog_id}/product_groups",
405
- 'method' => 'POST',
406
- ] );
 
 
407
 
408
  $request->set_data( $data );
409
 
@@ -419,16 +421,18 @@ class API extends Framework\SV_WC_API_Base {
419
  * @since 2.0.0
420
  *
421
  * @param string $product_group_id product group ID
422
- * @param array $data product group data
423
  * @return Response
424
  * @throws Framework\SV_WC_API_Exception
425
  */
426
  public function update_product_group( $product_group_id, $data ) {
427
 
428
- $request = $this->get_new_request( [
429
- 'path' => "/{$product_group_id}",
430
- 'method' => 'POST',
431
- ] );
 
 
432
 
433
  $request->set_data( $data );
434
 
@@ -449,10 +453,12 @@ class API extends Framework\SV_WC_API_Base {
449
  */
450
  public function delete_product_group( $product_group_id ) {
451
 
452
- $request = $this->get_new_request( [
453
- 'path' => "/{$product_group_id}",
454
- 'method' => 'DELETE',
455
- ] );
 
 
456
 
457
  $this->set_response_handler( Response::class );
458
 
@@ -466,7 +472,7 @@ class API extends Framework\SV_WC_API_Base {
466
  * @since 2.0.0
467
  *
468
  * @param string $product_group_id product group ID
469
- * @param int $limit max number of results returned per page of data
470
  * @return API\Catalog\Product_Group\Products\Read\Response
471
  * @throws Framework\SV_WC_API_Exception
472
  */
@@ -506,16 +512,18 @@ class API extends Framework\SV_WC_API_Base {
506
  * @since 2.0.0
507
  *
508
  * @param string $product_group_id parent product ID
509
- * @param array $data product data
510
  * @return Response
511
  * @throws Framework\SV_WC_API_Exception
512
  */
513
  public function create_product_item( $product_group_id, $data ) {
514
 
515
- $request = $this->get_new_request( [
516
- 'path' => "/{$product_group_id}/products",
517
- 'method' => 'POST',
518
- ] );
 
 
519
 
520
  $request->set_data( $data );
521
 
@@ -531,16 +539,18 @@ class API extends Framework\SV_WC_API_Base {
531
  * @since 2.0.0
532
  *
533
  * @param string $product_item_id product item ID
534
- * @param array $data product data
535
  * @return Response
536
  * @throws Framework\SV_WC_API_Exception
537
  */
538
  public function update_product_item( $product_item_id, $data ) {
539
 
540
- $request = $this->get_new_request( [
541
- 'path' => "/{$product_item_id}",
542
- 'method' => 'POST',
543
- ] );
 
 
544
 
545
  $request->set_data( $data );
546
 
@@ -561,10 +571,12 @@ class API extends Framework\SV_WC_API_Base {
561
  */
562
  public function delete_product_item( $product_item_id ) {
563
 
564
- $request = $this->get_new_request( [
565
- 'path' => "/{$product_item_id}",
566
- 'method' => 'DELETE',
567
- ] );
 
 
568
 
569
  $this->set_response_handler( Response::class );
570
 
@@ -577,7 +589,7 @@ class API extends Framework\SV_WC_API_Base {
577
  *
578
  * @since 2.0.0
579
  *
580
- * @param string $pixel_id pixel ID
581
  * @param Event[] $events events to send
582
  * @return Response
583
  * @throws Framework\SV_WC_API_Exception
@@ -598,7 +610,7 @@ class API extends Framework\SV_WC_API_Base {
598
  * @since 2.0.0
599
  *
600
  * @param API\Response $response previous response object
601
- * @param int $additional_pages number of additional pages of results to retrieve
602
  * @return API\Response|null
603
  * @throws Framework\SV_WC_API_Exception
604
  */
@@ -611,11 +623,13 @@ class API extends Framework\SV_WC_API_Base {
611
 
612
  $components = parse_url( str_replace( $this->request_uri, '', $response->get_next_page_endpoint() ) );
613
 
614
- $request = $this->get_new_request( [
615
- 'path' => isset( $components['path'] ) ? $components['path'] : '',
616
- 'method' => 'GET',
617
- 'params' => isset( $components['query'] ) ? wp_parse_args( $components['query'] ) : [],
618
- ] );
 
 
619
 
620
  $this->set_response_handler( get_class( $response ) );
621
 
@@ -640,12 +654,12 @@ class API extends Framework\SV_WC_API_Base {
640
  */
641
  public function get_new_orders( $page_id ) {
642
 
643
- $request_args = [
644
- 'state' => [
645
  Order::STATUS_PROCESSING,
646
  Order::STATUS_CREATED,
647
- ]
648
- ];
649
 
650
  $request = new API\Orders\Request( $page_id, $request_args );
651
 
@@ -666,13 +680,13 @@ class API extends Framework\SV_WC_API_Base {
666
  */
667
  public function get_cancelled_orders( $page_id ) {
668
 
669
- $request_args = [
670
- 'state' => [
671
  Order::STATUS_COMPLETED,
672
- ],
673
  'updated_after' => time() - facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->get_order_update_interval(),
674
  'filters' => 'has_cancellations',
675
- ];
676
 
677
  $request = new API\Orders\Request( $page_id, $request_args );
678
 
@@ -729,7 +743,7 @@ class API extends Framework\SV_WC_API_Base {
729
  * @since 2.1.0
730
  *
731
  * @param string $remote_id remote order ID
732
- * @param array $fulfillment_data fulfillment data to be sent on the request
733
  * @return API\Response
734
  * @throws Framework\SV_WC_API_Exception
735
  */
@@ -750,7 +764,7 @@ class API extends Framework\SV_WC_API_Base {
750
  *
751
  * @param string $remote_id remote order ID
752
  * @param string $reason cancellation reason
753
- * @param bool $restock_items whether to restock items remotely
754
  * @return API\Response
755
  * @throws Framework\SV_WC_API_Exception
756
  */
@@ -772,7 +786,7 @@ class API extends Framework\SV_WC_API_Base {
772
  * @since 2.1.0
773
  *
774
  * @param string $remote_id remote order ID
775
- * @param array $refund_data refund data to be sent on the request
776
  * @return API\Response
777
  * @throws Framework\SV_WC_API_Exception
778
  */
@@ -800,13 +814,13 @@ class API extends Framework\SV_WC_API_Base {
800
  * }
801
  * @return Request
802
  */
803
- protected function get_new_request( $args = [] ) {
804
 
805
- $defaults = [
806
  'path' => '/',
807
  'method' => 'GET',
808
- 'params' => [],
809
- ];
810
 
811
  $args = wp_parse_args( $args, $defaults );
812
  $request = new Request( $args['path'], $args['method'] );
49
 
50
  $this->access_token = $access_token;
51
 
52
+ $this->request_headers = array(
53
  'Authorization' => "Bearer {$access_token}",
54
+ );
55
 
56
  $this->set_request_content_type_header( 'application/json' );
57
  $this->set_request_accept_header( 'application/json' );
148
  * @link https://developers.facebook.com/docs/graph-api/using-graph-api/error-handling#rate-limiting-error-codes
149
  * @link https://developers.facebook.com/docs/marketing-api/reference/product-catalog/batch/#validation-rules
150
  */
151
+ if ( in_array( $code, array( 4, 17, 32, 613, 80001, 80004 ), true ) ) {
152
 
153
  $delay_in_seconds = $this->calculate_rate_limit_delay( $response, $this->get_response_headers() );
154
 
172
  *
173
  * @link https://developers.facebook.com/docs/graph-api/using-graph-api/error-handling#errorcodes
174
  */
175
+ if ( ( $code >= 200 && $code < 300 ) || in_array( $code, array( 10, 102, 190 ), false ) ) {
176
  set_transient( 'wc_facebook_connection_invalid', time(), DAY_IN_SECONDS );
177
  } else {
178
  // this was an unrelated error, so the OAuth connection may still be valid
203
  * @since 2.1.0
204
  *
205
  * @param string $rate_limit_id ID for the API request
206
+ * @param int $timestamp timestamp until the delay is over
207
  * @throws API\Exceptions\Request_Limit_Reached
208
  */
209
  private function handle_throttled_request( $rate_limit_id, $timestamp ) {
345
  *
346
  * @since 2.0.0
347
  *
348
+ * @param string $external_business_id external business ID
349
  * @param API\FBE\Configuration\Messenger $configuration messenger configuration
350
  * @return Response
351
  * @throws Framework\SV_WC_API_Exception
352
  */
353
+ public function update_messenger_configuration( $external_business_id, API\FBE\Configuration\Messenger $configuration ) {
354
 
355
  $request = new API\FBE\Configuration\Update\Request( $external_business_id );
356
 
370
  * @since 2.0.0
371
  *
372
  * @param string $catalog_id catalog ID
373
+ * @param array $requests array of prefixed product IDs to create, update or remove
374
+ * @param bool $allow_upsert whether to allow updates to insert new items
375
  * @return \SkyVerge\WooCommerce\Facebook\API\Catalog\Send_Item_Updates\Response
376
  * @throws Framework\SV_WC_API_Exception
377
  */
394
  * @since 2.0.0
395
  *
396
  * @param string $catalog_id catalog ID
397
+ * @param array $data product group data
398
  * @return Response
399
  * @throws Framework\SV_WC_API_Exception
400
  */
401
  public function create_product_group( $catalog_id, $data ) {
402
 
403
+ $request = $this->get_new_request(
404
+ array(
405
+ 'path' => "/{$catalog_id}/product_groups",
406
+ 'method' => 'POST',
407
+ )
408
+ );
409
 
410
  $request->set_data( $data );
411
 
421
  * @since 2.0.0
422
  *
423
  * @param string $product_group_id product group ID
424
+ * @param array $data product group data
425
  * @return Response
426
  * @throws Framework\SV_WC_API_Exception
427
  */
428
  public function update_product_group( $product_group_id, $data ) {
429
 
430
+ $request = $this->get_new_request(
431
+ array(
432
+ 'path' => "/{$product_group_id}",
433
+ 'method' => 'POST',
434
+ )
435
+ );
436
 
437
  $request->set_data( $data );
438
 
453
  */
454
  public function delete_product_group( $product_group_id ) {
455
 
456
+ $request = $this->get_new_request(
457
+ array(
458
+ 'path' => "/{$product_group_id}",
459
+ 'method' => 'DELETE',
460
+ )
461
+ );
462
 
463
  $this->set_response_handler( Response::class );
464
 
472
  * @since 2.0.0
473
  *
474
  * @param string $product_group_id product group ID
475
+ * @param int $limit max number of results returned per page of data
476
  * @return API\Catalog\Product_Group\Products\Read\Response
477
  * @throws Framework\SV_WC_API_Exception
478
  */
512
  * @since 2.0.0
513
  *
514
  * @param string $product_group_id parent product ID
515
+ * @param array $data product data
516
  * @return Response
517
  * @throws Framework\SV_WC_API_Exception
518
  */
519
  public function create_product_item( $product_group_id, $data ) {
520
 
521
+ $request = $this->get_new_request(
522
+ array(
523
+ 'path' => "/{$product_group_id}/products",
524
+ 'method' => 'POST',
525
+ )
526
+ );
527
 
528
  $request->set_data( $data );
529
 
539
  * @since 2.0.0
540
  *
541
  * @param string $product_item_id product item ID
542
+ * @param array $data product data
543
  * @return Response
544
  * @throws Framework\SV_WC_API_Exception
545
  */
546
  public function update_product_item( $product_item_id, $data ) {
547
 
548
+ $request = $this->get_new_request(
549
+ array(
550
+ 'path' => "/{$product_item_id}",
551
+ 'method' => 'POST',
552
+ )
553
+ );
554
 
555
  $request->set_data( $data );
556
 
571
  */
572
  public function delete_product_item( $product_item_id ) {
573
 
574
+ $request = $this->get_new_request(
575
+ array(
576
+ 'path' => "/{$product_item_id}",
577
+ 'method' => 'DELETE',
578
+ )
579
+ );
580
 
581
  $this->set_response_handler( Response::class );
582
 
589
  *
590
  * @since 2.0.0
591
  *
592
+ * @param string $pixel_id pixel ID
593
  * @param Event[] $events events to send
594
  * @return Response
595
  * @throws Framework\SV_WC_API_Exception
610
  * @since 2.0.0
611
  *
612
  * @param API\Response $response previous response object
613
+ * @param int $additional_pages number of additional pages of results to retrieve
614
  * @return API\Response|null
615
  * @throws Framework\SV_WC_API_Exception
616
  */
623
 
624
  $components = parse_url( str_replace( $this->request_uri, '', $response->get_next_page_endpoint() ) );
625
 
626
+ $request = $this->get_new_request(
627
+ array(
628
+ 'path' => isset( $components['path'] ) ? $components['path'] : '',
629
+ 'method' => 'GET',
630
+ 'params' => isset( $components['query'] ) ? wp_parse_args( $components['query'] ) : array(),
631
+ )
632
+ );
633
 
634
  $this->set_response_handler( get_class( $response ) );
635
 
654
  */
655
  public function get_new_orders( $page_id ) {
656
 
657
+ $request_args = array(
658
+ 'state' => array(
659
  Order::STATUS_PROCESSING,
660
  Order::STATUS_CREATED,
661
+ ),
662
+ );
663
 
664
  $request = new API\Orders\Request( $page_id, $request_args );
665
 
680
  */
681
  public function get_cancelled_orders( $page_id ) {
682
 
683
+ $request_args = array(
684
+ 'state' => array(
685
  Order::STATUS_COMPLETED,
686
+ ),
687
  'updated_after' => time() - facebook_for_woocommerce()->get_commerce_handler()->get_orders_handler()->get_order_update_interval(),
688
  'filters' => 'has_cancellations',
689
+ );
690
 
691
  $request = new API\Orders\Request( $page_id, $request_args );
692
 
743
  * @since 2.1.0
744
  *
745
  * @param string $remote_id remote order ID
746
+ * @param array $fulfillment_data fulfillment data to be sent on the request
747
  * @return API\Response
748
  * @throws Framework\SV_WC_API_Exception
749
  */
764
  *
765
  * @param string $remote_id remote order ID
766
  * @param string $reason cancellation reason
767
+ * @param bool $restock_items whether to restock items remotely
768
  * @return API\Response
769
  * @throws Framework\SV_WC_API_Exception
770
  */
786
  * @since 2.1.0
787
  *
788
  * @param string $remote_id remote order ID
789
+ * @param array $refund_data refund data to be sent on the request
790
  * @return API\Response
791
  * @throws Framework\SV_WC_API_Exception
792
  */
814
  * }
815
  * @return Request
816
  */
817
+ protected function get_new_request( $args = array() ) {
818
 
819
+ $defaults = array(
820
  'path' => '/',
821
  'method' => 'GET',
822
+ 'params' => array(),
823
+ );
824
 
825
  $args = wp_parse_args( $args, $defaults );
826
  $request = new Request( $args['path'], $args['method'] );
includes/API/Catalog/Product_Group/Products/Read/Request.php CHANGED
@@ -28,16 +28,18 @@ class Request extends API\Request {
28
  * @since 2.0.0
29
  *
30
  * @param string $product_group_id product group ID
31
- * @param int $limit max number of results returned
32
  */
33
  public function __construct( $product_group_id, $limit ) {
34
 
35
  parent::__construct( "/{$product_group_id}/products", 'GET' );
36
 
37
- $this->set_params( [
38
- 'fields' => 'id,retailer_id',
39
- 'limit' => $limit,
40
- ] );
 
 
41
  }
42
 
43
 
28
  * @since 2.0.0
29
  *
30
  * @param string $product_group_id product group ID
31
+ * @param int $limit max number of results returned
32
  */
33
  public function __construct( $product_group_id, $limit ) {
34
 
35
  parent::__construct( "/{$product_group_id}/products", 'GET' );
36
 
37
+ $this->set_params(
38
+ array(
39
+ 'fields' => 'id,retailer_id',
40
+ 'limit' => $limit,
41
+ )
42
+ );
43
  }
44
 
45
 
includes/API/Catalog/Product_Group/Products/Read/Response.php CHANGED
@@ -34,7 +34,7 @@ class Response extends API\Response {
34
  */
35
  public function get_ids() {
36
 
37
- $product_item_ids = [];
38
 
39
  foreach ( $this->get_data() as $entry ) {
40
  $product_item_ids[ $entry->retailer_id ] = $entry->id;
34
  */
35
  public function get_ids() {
36
 
37
+ $product_item_ids = array();
38
 
39
  foreach ( $this->get_data() as $entry ) {
40
  $product_item_ids[ $entry->retailer_id ] = $entry->id;
includes/API/Catalog/Product_Item/Find/Request.php CHANGED
@@ -58,7 +58,7 @@ class Request extends API\Request {
58
  */
59
  public function get_params() {
60
 
61
- return [ 'fields' => 'id,product_group{id}' ];
62
  }
63
 
64
 
58
  */
59
  public function get_params() {
60
 
61
+ return array( 'fields' => 'id,product_group{id}' );
62
  }
63
 
64
 
includes/API/Catalog/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
@@ -57,7 +57,7 @@ class Request extends API\Request {
57
  */
58
  public function get_params() {
59
 
60
- return [ 'fields' => 'name' ];
61
  }
62
 
63
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
57
  */
58
  public function get_params() {
59
 
60
+ return array( 'fields' => 'name' );
61
  }
62
 
63
 
includes/API/Catalog/Send_Item_Updates/Request.php CHANGED
@@ -19,11 +19,11 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /** @var array an array of item update requests */
26
- protected $requests = [];
27
 
28
  /** @var bool determines whether updates for products that are not currently in the catalog should create new items */
29
  protected $allow_upsert = true;
@@ -115,13 +115,13 @@ class Request extends API\Request {
115
  * @since 2.0.0
116
  */
117
  public function get_data() {
118
- # TODO: Make it so the item type is based on the actual item type
119
 
120
- return [
121
  'allow_upsert' => $this->get_allow_upsert(),
122
  'requests' => $this->get_requests(),
123
  'item_type' => 'PRODUCT_ITEM',
124
- ];
125
  }
126
 
127
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /** @var array an array of item update requests */
26
+ protected $requests = array();
27
 
28
  /** @var bool determines whether updates for products that are not currently in the catalog should create new items */
29
  protected $allow_upsert = true;
115
  * @since 2.0.0
116
  */
117
  public function get_data() {
118
+ // TODO: Make it so the item type is based on the actual item type
119
 
120
+ return array(
121
  'allow_upsert' => $this->get_allow_upsert(),
122
  'requests' => $this->get_requests(),
123
  'item_type' => 'PRODUCT_ITEM',
124
+ );
125
  }
126
 
127
 
includes/API/FBE/Configuration/Messenger.php CHANGED
@@ -27,7 +27,7 @@ class Messenger {
27
  private $default_locale;
28
 
29
  /** @var string[] approved domains */
30
- private $domains = [];
31
 
32
 
33
  /**
@@ -35,13 +35,16 @@ class Messenger {
35
  *
36
  * @param array $data configuration data
37
  */
38
- public function __construct( array $data = [] ) {
39
 
40
- $data = wp_parse_args( $data, [
41
- 'enabled' => false,
42
- 'default_locale' => '',
43
- 'domains' => [],
44
- ] );
 
 
 
45
 
46
  $this->set_enabled( $data['enabled'] );
47
  $this->set_default_locale( $data['default_locale'] );
@@ -87,7 +90,7 @@ class Messenger {
87
  if ( is_array( $this->domains ) ) {
88
  $domains = array_map( 'trailingslashit', $this->domains );
89
  } else {
90
- $domains = [];
91
  }
92
 
93
  return $domains;
@@ -129,7 +132,7 @@ class Messenger {
129
  */
130
  public function add_domain( $domain ) {
131
 
132
- $domains = is_array( $this->domains ) ? $this->domains : [];
133
 
134
  $domains[] = $domain;
135
 
27
  private $default_locale;
28
 
29
  /** @var string[] approved domains */
30
+ private $domains = array();
31
 
32
 
33
  /**
35
  *
36
  * @param array $data configuration data
37
  */
38
+ public function __construct( array $data = array() ) {
39
 
40
+ $data = wp_parse_args(
41
+ $data,
42
+ array(
43
+ 'enabled' => false,
44
+ 'default_locale' => '',
45
+ 'domains' => array(),
46
+ )
47
+ );
48
 
49
  $this->set_enabled( $data['enabled'] );
50
  $this->set_default_locale( $data['default_locale'] );
90
  if ( is_array( $this->domains ) ) {
91
  $domains = array_map( 'trailingslashit', $this->domains );
92
  } else {
93
+ $domains = array();
94
  }
95
 
96
  return $domains;
132
  */
133
  public function add_domain( $domain ) {
134
 
135
+ $domains = is_array( $this->domains ) ? $this->domains : array();
136
 
137
  $domains[] = $domain;
138
 
includes/API/FBE/Configuration/Read/Response.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Response extends API\Response {
23
 
24
 
25
  /**
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Response extends API\Response {
23
 
24
 
25
  /**
includes/API/FBE/Configuration/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
@@ -34,9 +34,9 @@ class Request extends API\Request {
34
 
35
  parent::__construct( '/fbe_business', $method );
36
 
37
- $this->params = [
38
  'fbe_external_business_id' => $external_business_id,
39
- ];
40
  }
41
 
42
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
34
 
35
  parent::__construct( '/fbe_business', $method );
36
 
37
+ $this->params = array(
38
  'fbe_external_business_id' => $external_business_id,
39
+ );
40
  }
41
 
42
 
includes/API/FBE/Configuration/Update/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API\FBE\Configuration;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends Configuration\Request {
23
 
24
 
25
  /**
@@ -49,10 +49,10 @@ class Request extends Configuration\Request {
49
  */
50
  public function set_messenger_configuration( Configuration\Messenger $configuration ) {
51
 
52
- $this->data['messenger_chat'] = [
53
  'enabled' => $configuration->is_enabled(),
54
  'domains' => $configuration->get_domains(),
55
- ];
56
  }
57
 
58
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends Configuration\Request {
23
 
24
 
25
  /**
49
  */
50
  public function set_messenger_configuration( Configuration\Messenger $configuration ) {
51
 
52
+ $this->data['messenger_chat'] = array(
53
  'enabled' => $configuration->is_enabled(),
54
  'domains' => $configuration->get_domains(),
55
+ );
56
  }
57
 
58
 
includes/API/FBE/Installation/Read/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API\FBE\Installation;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends Installation\Request {
23
 
24
 
25
  /**
@@ -33,9 +33,9 @@ class Request extends Installation\Request {
33
 
34
  parent::__construct( 'fbe_installs', 'GET' );
35
 
36
- $this->params = [
37
  'fbe_external_business_id' => $external_business_id,
38
- ];
39
  }
40
 
41
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends Installation\Request {
23
 
24
 
25
  /**
33
 
34
  parent::__construct( 'fbe_installs', 'GET' );
35
 
36
+ $this->params = array(
37
  'fbe_external_business_id' => $external_business_id,
38
+ );
39
  }
40
 
41
 
includes/API/FBE/Installation/Read/Response.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Response extends API\Response {
23
 
24
 
25
  /**
@@ -130,7 +130,7 @@ class Response extends API\Response {
130
  */
131
  public function get_profiles() {
132
 
133
- return ! empty( $this->get_data()->profiles ) ? $this->get_data()->profiles : [];
134
  }
135
 
136
 
@@ -143,7 +143,7 @@ class Response extends API\Response {
143
  */
144
  public function get_data() {
145
 
146
- $data = ! empty( $this->response_data->data ) && is_array( $this->response_data->data ) ? $this->response_data->data : [];
147
 
148
  return is_object( $data[0] ) ? $data[0] : new \stdClass();
149
  }
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Response extends API\Response {
23
 
24
 
25
  /**
130
  */
131
  public function get_profiles() {
132
 
133
+ return ! empty( $this->get_data()->profiles ) ? $this->get_data()->profiles : array();
134
  }
135
 
136
 
143
  */
144
  public function get_data() {
145
 
146
+ $data = ! empty( $this->response_data->data ) && is_array( $this->response_data->data ) ? $this->response_data->data : array();
147
 
148
  return is_object( $data[0] ) ? $data[0] : new \stdClass();
149
  }
includes/API/FBE/Installation/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
includes/API/Orders/Acknowledge/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.1.0
21
  */
22
- class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
@@ -37,10 +37,12 @@ class Request extends API\Orders\Abstract_Request {
37
 
38
  parent::__construct( "/{$remote_id}/acknowledge_order", 'POST' );
39
 
40
- $this->set_data( [
41
- 'merchant_order_reference' => $merchant_order_reference,
42
- 'idempotency_key' => $this->get_idempotency_key(),
43
- ] );
 
 
44
  }
45
 
46
 
19
  *
20
  * @since 2.1.0
21
  */
22
+ class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
37
 
38
  parent::__construct( "/{$remote_id}/acknowledge_order", 'POST' );
39
 
40
+ $this->set_data(
41
+ array(
42
+ 'merchant_order_reference' => $merchant_order_reference,
43
+ 'idempotency_key' => $this->get_idempotency_key(),
44
+ )
45
+ );
46
  }
47
 
48
 
includes/API/Orders/Cancel/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.1.0
21
  */
22
- class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
@@ -32,19 +32,21 @@ class Request extends API\Orders\Abstract_Request {
32
  *
33
  * @param string $remote_id remote order ID
34
  * @param string $reason cancellation reason code
35
- * @param bool $restock_items whether or not the items were restocked
36
  */
37
  public function __construct( $remote_id, $reason, $restock_items = true ) {
38
 
39
  parent::__construct( "/{$remote_id}/cancellations", 'POST' );
40
 
41
- $this->set_data( [
42
- 'cancel_reason' => [
43
- 'reason_code' => $reason,
44
- ],
45
- 'restock_items' => $restock_items,
46
- 'idempotency_key' => $this->get_idempotency_key(),
47
- ] );
 
 
48
  }
49
 
50
 
19
  *
20
  * @since 2.1.0
21
  */
22
+ class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
32
  *
33
  * @param string $remote_id remote order ID
34
  * @param string $reason cancellation reason code
35
+ * @param bool $restock_items whether or not the items were restocked
36
  */
37
  public function __construct( $remote_id, $reason, $restock_items = true ) {
38
 
39
  parent::__construct( "/{$remote_id}/cancellations", 'POST' );
40
 
41
+ $this->set_data(
42
+ array(
43
+ 'cancel_reason' => array(
44
+ 'reason_code' => $reason,
45
+ ),
46
+ 'restock_items' => $restock_items,
47
+ 'idempotency_key' => $this->get_idempotency_key(),
48
+ )
49
+ );
50
  }
51
 
52
 
includes/API/Orders/Fulfillment/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.1.0
21
  */
22
- class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
@@ -31,7 +31,7 @@ class Request extends API\Orders\Abstract_Request {
31
  * @since 2.1.0
32
  *
33
  * @param string $remote_id remote order ID
34
- * @param array $fulfillment_data fulfillment data
35
  */
36
  public function __construct( $remote_id, $fulfillment_data ) {
37
 
19
  *
20
  * @since 2.1.0
21
  */
22
+ class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
31
  * @since 2.1.0
32
  *
33
  * @param string $remote_id remote order ID
34
+ * @param array $fulfillment_data fulfillment data
35
  */
36
  public function __construct( $remote_id, $fulfillment_data ) {
37
 
includes/API/Orders/Order.php CHANGED
@@ -87,7 +87,7 @@ class Order {
87
  */
88
  public function get_items() {
89
 
90
- return ! empty( $this->data['items']['data'] ) ? $this->data['items']['data'] : [];
91
  }
92
 
93
 
@@ -115,7 +115,7 @@ class Order {
115
  */
116
  public function get_selected_shipping_option() {
117
 
118
- return isset( $this->data['selected_shipping_option'] ) ? $this->data['selected_shipping_option'] : [];
119
  }
120
 
121
 
@@ -130,7 +130,7 @@ class Order {
130
  */
131
  public function get_shipping_address() {
132
 
133
- return isset( $this->data['shipping_address'] ) ? $this->data['shipping_address'] : [];
134
  }
135
 
136
 
@@ -145,7 +145,7 @@ class Order {
145
  */
146
  public function get_estimated_payment_details() {
147
 
148
- return isset( $this->data['estimated_payment_details'] ) ? $this->data['estimated_payment_details'] : [];
149
  }
150
 
151
 
@@ -160,7 +160,7 @@ class Order {
160
  */
161
  public function get_buyer_details() {
162
 
163
- return isset( $this->data['buyer_details'] ) ? $this->data['buyer_details'] : [];
164
  }
165
 
166
 
87
  */
88
  public function get_items() {
89
 
90
+ return ! empty( $this->data['items']['data'] ) ? $this->data['items']['data'] : array();
91
  }
92
 
93
 
115
  */
116
  public function get_selected_shipping_option() {
117
 
118
+ return isset( $this->data['selected_shipping_option'] ) ? $this->data['selected_shipping_option'] : array();
119
  }
120
 
121
 
130
  */
131
  public function get_shipping_address() {
132
 
133
+ return isset( $this->data['shipping_address'] ) ? $this->data['shipping_address'] : array();
134
  }
135
 
136
 
145
  */
146
  public function get_estimated_payment_details() {
147
 
148
+ return isset( $this->data['estimated_payment_details'] ) ? $this->data['estimated_payment_details'] : array();
149
  }
150
 
151
 
160
  */
161
  public function get_buyer_details() {
162
 
163
+ return isset( $this->data['buyer_details'] ) ? $this->data['buyer_details'] : array();
164
  }
165
 
166
 
includes/API/Orders/Read/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.1.0
21
  */
22
- class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  /**
@@ -28,36 +28,39 @@ class Request extends API\Orders\Abstract_Request {
28
  * @since 2.1.0
29
  *
30
  * @param string $remote_id remote order ID
31
- * @param array $fields fields to be returned
32
  */
33
- public function __construct( $remote_id, $fields = [] ) {
34
 
35
  parent::__construct( "/{$remote_id}", 'GET' );
36
 
37
  if ( empty( $fields ) ) {
38
 
39
  // request all top-level fields
40
- $fields = implode( ',', [
41
- 'id',
42
- 'order_status',
43
- 'created',
44
- 'last_updated',
45
- 'items{id,retailer_id,product_id,quantity,price_per_unit,tax_details}',
46
- 'ship_by_date',
47
- 'merchant_order_id',
48
- 'channel',
49
- 'selected_shipping_option',
50
- 'shipping_address',
51
- 'estimated_payment_details',
52
- 'buyer_details',
53
- ] );
 
 
 
54
 
55
  } elseif ( is_array( $fields ) ) {
56
 
57
  $fields = implode( ',', $fields );
58
  }
59
 
60
- $this->set_params( [ 'fields' => $fields ] );
61
  }
62
 
63
 
19
  *
20
  * @since 2.1.0
21
  */
22
+ class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  /**
28
  * @since 2.1.0
29
  *
30
  * @param string $remote_id remote order ID
31
+ * @param array $fields fields to be returned
32
  */
33
+ public function __construct( $remote_id, $fields = array() ) {
34
 
35
  parent::__construct( "/{$remote_id}", 'GET' );
36
 
37
  if ( empty( $fields ) ) {
38
 
39
  // request all top-level fields
40
+ $fields = implode(
41
+ ',',
42
+ array(
43
+ 'id',
44
+ 'order_status',
45
+ 'created',
46
+ 'last_updated',
47
+ 'items{id,retailer_id,product_id,quantity,price_per_unit,tax_details}',
48
+ 'ship_by_date',
49
+ 'merchant_order_id',
50
+ 'channel',
51
+ 'selected_shipping_option',
52
+ 'shipping_address',
53
+ 'estimated_payment_details',
54
+ 'buyer_details',
55
+ )
56
+ );
57
 
58
  } elseif ( is_array( $fields ) ) {
59
 
60
  $fields = implode( ',', $fields );
61
  }
62
 
63
+ $this->set_params( array( 'fields' => $fields ) );
64
  }
65
 
66
 
includes/API/Orders/Refund/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.1.0
21
  */
22
- class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
@@ -31,7 +31,7 @@ class Request extends API\Orders\Abstract_Request {
31
  * @since 2.1.0
32
  *
33
  * @param string $remote_id remote order ID
34
- * @param array $refund_data refund data
35
  */
36
  public function __construct( $remote_id, $refund_data ) {
37
 
19
  *
20
  * @since 2.1.0
21
  */
22
+ class Request extends API\Orders\Abstract_Request {
23
 
24
 
25
  use API\Traits\Idempotent_Request;
31
  * @since 2.1.0
32
  *
33
  * @param string $remote_id remote order ID
34
+ * @param array $refund_data refund data
35
  */
36
  public function __construct( $remote_id, $refund_data ) {
37
 
includes/API/Orders/Request.php CHANGED
@@ -17,7 +17,7 @@ defined( 'ABSPATH' ) or exit;
17
  *
18
  * @since 2.1.0
19
  */
20
- class Request extends Abstract_Request {
21
 
22
 
23
  /**
@@ -43,13 +43,13 @@ class Request extends Abstract_Request {
43
  * @since 2.1.0
44
  *
45
  * @param string $page_id page ID
46
- * @param array $args optional additional arguments
47
  */
48
- public function __construct( $page_id, $args = [] ) {
49
 
50
  parent::__construct( "/{$page_id}/commerce_orders", 'GET' );
51
 
52
- $params = [];
53
 
54
  if ( isset( $args['updated_before'] ) ) {
55
 
@@ -78,20 +78,23 @@ class Request extends Abstract_Request {
78
  } else {
79
 
80
  // request all top-level fields
81
- $params['fields'] = implode( ',', [
82
- 'id',
83
- 'order_status',
84
- 'created',
85
- 'last_updated',
86
- 'items{id,retailer_id,product_id,quantity,price_per_unit,tax_details}',
87
- 'ship_by_date',
88
- 'merchant_order_id',
89
- 'channel',
90
- 'selected_shipping_option',
91
- 'shipping_address',
92
- 'estimated_payment_details',
93
- 'buyer_details',
94
- ] );
 
 
 
95
  }
96
 
97
  $this->set_params( $params );
17
  *
18
  * @since 2.1.0
19
  */
20
+ class Request extends Abstract_Request {
21
 
22
 
23
  /**
43
  * @since 2.1.0
44
  *
45
  * @param string $page_id page ID
46
+ * @param array $args optional additional arguments
47
  */
48
+ public function __construct( $page_id, $args = array() ) {
49
 
50
  parent::__construct( "/{$page_id}/commerce_orders", 'GET' );
51
 
52
+ $params = array();
53
 
54
  if ( isset( $args['updated_before'] ) ) {
55
 
78
  } else {
79
 
80
  // request all top-level fields
81
+ $params['fields'] = implode(
82
+ ',',
83
+ array(
84
+ 'id',
85
+ 'order_status',
86
+ 'created',
87
+ 'last_updated',
88
+ 'items{id,retailer_id,product_id,quantity,price_per_unit,tax_details}',
89
+ 'ship_by_date',
90
+ 'merchant_order_id',
91
+ 'channel',
92
+ 'selected_shipping_option',
93
+ 'shipping_address',
94
+ 'estimated_payment_details',
95
+ 'buyer_details',
96
+ )
97
+ );
98
  }
99
 
100
  $this->set_params( $params );
includes/API/Orders/Response.php CHANGED
@@ -34,7 +34,7 @@ class Response extends API\Response {
34
  */
35
  public function get_orders() {
36
 
37
- $orders = [];
38
 
39
  foreach ( $this->get_data() as $order_data ) {
40
  $orders[] = new Order( json_decode( json_encode( $order_data ), true ) );
34
  */
35
  public function get_orders() {
36
 
37
+ $orders = array();
38
 
39
  foreach ( $this->get_data() as $order_data ) {
40
  $orders[] = new Order( json_decode( json_encode( $order_data ), true ) );
includes/API/Pages/Read/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
@@ -44,7 +44,7 @@ class Request extends API\Request {
44
  */
45
  public function get_params() {
46
 
47
- return [ 'fields' => 'name,link' ];
48
  }
49
 
50
 
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
44
  */
45
  public function get_params() {
46
 
47
+ return array( 'fields' => 'name,link' );
48
  }
49
 
50
 
includes/API/Pages/Read/Response.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Response extends API\Response {
23
 
24
 
25
  /**
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Response extends API\Response {
23
 
24
 
25
  /**
includes/API/Pixel/Events/Request.php CHANGED
@@ -30,7 +30,7 @@ class Request extends API\Request {
30
  /**
31
  * Request constructor.
32
  *
33
- * @param string $pixel_id
34
  * @param Event[] $events events to send
35
  */
36
  public function __construct( $pixel_id, array $events ) {
@@ -50,10 +50,10 @@ class Request extends API\Request {
50
  */
51
  public function get_data() {
52
 
53
- $data = [
54
- 'data' => [],
55
  'partner_agent' => Event::get_platform_identifier(),
56
- ];
57
 
58
  foreach ( $this->events as $event ) {
59
 
@@ -88,7 +88,7 @@ class Request extends API\Request {
88
  * @param array $data request data
89
  * @param Request $request request object
90
  */
91
- return apply_filters( 'wc_facebook_api_pixel_event_request_data', $data, $this);
92
  }
93
 
94
 
30
  /**
31
  * Request constructor.
32
  *
33
+ * @param string $pixel_id
34
  * @param Event[] $events events to send
35
  */
36
  public function __construct( $pixel_id, array $events ) {
50
  */
51
  public function get_data() {
52
 
53
+ $data = array(
54
+ 'data' => array(),
55
  'partner_agent' => Event::get_platform_identifier(),
56
+ );
57
 
58
  foreach ( $this->events as $event ) {
59
 
88
  * @param array $data request data
89
  * @param Request $request request object
90
  */
91
+ return apply_filters( 'wc_facebook_api_pixel_event_request_data', $data, $this );
92
  }
93
 
94
 
includes/API/Request.php CHANGED
@@ -32,7 +32,7 @@ class Request extends Framework\SV_WC_API_JSON_Request {
32
  protected $retry_count = 0;
33
 
34
  /** @var int[] the response codes that should trigger a retry */
35
- protected $retry_codes = [];
36
 
37
 
38
  /**
32
  protected $retry_count = 0;
33
 
34
  /** @var int[] the response codes that should trigger a retry */
35
+ protected $retry_codes = array();
36
 
37
 
38
  /**
includes/API/Traits/Paginated_Response.php CHANGED
@@ -65,7 +65,7 @@ trait Paginated_Response {
65
  */
66
  public function get_data() {
67
 
68
- return ! empty( $this->response_data->data ) ? $this->response_data->data : [];
69
  }
70
 
71
 
65
  */
66
  public function get_data() {
67
 
68
+ return ! empty( $this->response_data->data ) ? $this->response_data->data : array();
69
  }
70
 
71
 
includes/API/Traits/Rate_Limited_API.php CHANGED
@@ -30,7 +30,7 @@ trait Rate_Limited_API {
30
  * @since 2.0.0
31
  *
32
  * @param string $rate_limit_id request ID for rate limiting
33
- * @param int $delay delay in seconds
34
  */
35
  public function set_rate_limit_delay( $rate_limit_id, $delay ) {
36
 
@@ -68,7 +68,7 @@ trait Rate_Limited_API {
68
  * @since 2.0.0
69
  *
70
  * @param Rate_Limited_Response $response API response object
71
- * @param array $headers API response headers
72
  * @return int delay in seconds
73
  */
74
  protected function calculate_rate_limit_delay( $response, $headers ) {
30
  * @since 2.0.0
31
  *
32
  * @param string $rate_limit_id request ID for rate limiting
33
+ * @param int $delay delay in seconds
34
  */
35
  public function set_rate_limit_delay( $rate_limit_id, $delay ) {
36
 
68
  * @since 2.0.0
69
  *
70
  * @param Rate_Limited_Response $response API response object
71
+ * @param array $headers API response headers
72
  * @return int delay in seconds
73
  */
74
  protected function calculate_rate_limit_delay( $response, $headers ) {
includes/API/Traits/Rate_Limited_Response.php CHANGED
@@ -33,7 +33,7 @@ trait Rate_Limited_Response {
33
  */
34
  private function get_usage_data( $headers ) {
35
 
36
- $usage_data = [];
37
 
38
  if ( ! empty( $headers['X-Business-Use-Case-Usage'] ) ) {
39
 
33
  */
34
  private function get_usage_data( $headers ) {
35
 
36
+ $usage_data = array();
37
 
38
  if ( ! empty( $headers['X-Business-Use-Case-Usage'] ) ) {
39
 
includes/API/User/Permissions/Delete/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
includes/API/User/Request.php CHANGED
@@ -19,7 +19,7 @@ use SkyVerge\WooCommerce\Facebook\API;
19
  *
20
  * @since 2.0.0
21
  */
22
- class Request extends API\Request {
23
 
24
 
25
  /**
19
  *
20
  * @since 2.0.0
21
  */
22
+ class Request extends API\Request {
23
 
24
 
25
  /**
includes/Admin.php CHANGED
@@ -43,7 +43,7 @@ class Admin {
43
  public function __construct() {
44
 
45
  // enqueue admin scripts
46
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
47
 
48
  $plugin = facebook_for_woocommerce();
49
 
@@ -57,7 +57,7 @@ class Admin {
57
  require_once __DIR__ . '/Admin/Product_Sets.php';
58
 
59
  $this->product_categories = new Admin\Product_Categories();
60
- $this->product_sets = new Admin\Product_Sets();
61
 
62
  // add a modal in admin product pages
63
  add_action( 'admin_footer', array( $this, 'render_modal_template' ) );
@@ -141,46 +141,77 @@ class Admin {
141
  if ( in_array( $current_screen->id, $modal_screens, true ) || facebook_for_woocommerce()->is_plugin_settings() ) {
142
 
143
  // enqueue modal functions
144
- wp_enqueue_script( 'facebook-for-woocommerce-modal', facebook_for_woocommerce()->get_plugin_url() . '/assets/js/facebook-for-woocommerce-modal.min.js', array( 'jquery', 'wc-backbone-modal', 'jquery-blockui' ), \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
 
145
  }
146
 
147
  if ( 'edit-fb_product_set' === $current_screen->id ) {
148
 
149
  // enqueue WooCommerce Admin Styles because of Select2
150
- wp_enqueue_style( 'woocommerce_admin_styles', WC()->plugin_url() . '/assets/css/admin.css', array(), \WC_Facebookcommerce::PLUGIN_VERSION );
151
- wp_enqueue_style( 'facebook-for-woocommerce-product-sets-admin', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-product-sets-admin.css', array(), \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
 
 
 
 
 
 
152
 
153
- wp_enqueue_script( 'facebook-for-woocommerce-product-sets', facebook_for_woocommerce()->get_plugin_url() . '/assets/js/admin/facebook-for-woocommerce-product-sets-admin.js', array( 'jquery', 'select2' ), \WC_Facebookcommerce::PLUGIN_VERSION, true );
 
 
 
 
 
 
154
  }
155
 
156
  if ( 'product' === $current_screen->id || 'edit-product' === $current_screen->id ) {
157
 
158
- wp_enqueue_style( 'facebook-for-woocommerce-products-admin', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-products-admin.css', array(), \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
 
159
 
160
- wp_enqueue_script( 'facebook-for-woocommerce-products-admin', facebook_for_woocommerce()->get_plugin_url() . '/assets/js/admin/facebook-for-woocommerce-products-admin.min.js', [ 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'facebook-for-woocommerce-modal' ], \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
 
161
 
162
  wp_localize_script(
163
  'facebook-for-woocommerce-products-admin',
164
  'facebook_for_woocommerce_products_admin',
165
- [
166
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
167
- '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,
168
- 'enhanced_attribute_page_type_edit_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
169
- 'enhanced_attribute_page_type_add_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
170
- 'enhanced_attribute_page_type_edit_product' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
171
- 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(),
172
- 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ),
173
- 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ),
174
- 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ),
175
- 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(),
176
- 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(),
177
  'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(),
178
  'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(),
179
- 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
180
- 'i18n' => [
181
  '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' ),
182
- ],
183
- ]
184
  );
185
 
186
  }//end if
@@ -192,8 +223,12 @@ class Admin {
192
  }
193
  }//end if
194
 
195
- // wp_enqueue_script( 'wc-facebook-google-product-category-fields', facebook_for_woocommerce()->get_plugin_url() . '/assets/js/admin/google-product-category-fields.min.js', [ 'jquery' ], \WC_Facebookcommerce::PLUGIN_VERSION );
196
- wp_enqueue_script( 'wc-facebook-google-product-category-fields', facebook_for_woocommerce()->get_plugin_url() . '/assets/js/admin/google-product-category-fields.js', array( 'jquery' ), \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
197
 
198
  wp_localize_script(
199
  'wc-facebook-google-product-category-fields',
@@ -245,18 +280,30 @@ class Admin {
245
 
246
  <ul class="ul-disc">
247
  <li><?php esc_html_e( 'Has a price defined', 'facebook-for-woocommerce' ); ?></li>
248
- <li><?php echo esc_html( sprintf(
 
 
 
249
  /* translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag */
250
- __( 'Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab', 'facebook-for-woocommerce' ),
251
- '<strong>',
252
- '</strong>'
253
- ) ); ?></li>
254
- <li><?php echo esc_html( sprintf(
 
 
 
 
 
 
255
  /* translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag */
256
- __( 'Has the %1$sFacebook Sync%2$s setting set to "Sync and show" or "Sync and hide"', 'facebook-for-woocommerce' ),
257
- '<strong>',
258
- '</strong>'
259
- ) ); ?></li>
 
 
 
260
  </ul>
261
  <?php
262
 
@@ -300,12 +347,16 @@ class Admin {
300
  ob_start();
301
 
302
  ?>
303
- <p><?php printf(
 
 
304
  /* translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a> link tag */
305
- __( '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?', 'facebook-for-woocommerce' ),
306
- '<a href="https://www.facebook.com/products" target="_blank">',
307
- '</a>'
308
- ); ?></p>
 
 
309
  <?php
310
 
311
  return ob_get_clean();
@@ -400,7 +451,6 @@ class Admin {
400
  } else {
401
  esc_html_e( 'Sync and hide', 'facebook-for-woocommerce' );
402
  }
403
-
404
  } else {
405
 
406
  esc_html_e( 'Do not sync', 'facebook-for-woocommerce' );
@@ -1278,10 +1328,12 @@ class Admin {
1278
  )
1279
  );
1280
 
1281
- woocommerce_wp_hidden_input( [
1282
- 'id' => \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
1283
- 'value' => '',
1284
- ] );
 
 
1285
 
1286
  ?>
1287
  </div>
43
  public function __construct() {
44
 
45
  // enqueue admin scripts
46
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
47
 
48
  $plugin = facebook_for_woocommerce();
49
 
57
  require_once __DIR__ . '/Admin/Product_Sets.php';
58
 
59
  $this->product_categories = new Admin\Product_Categories();
60
+ $this->product_sets = new Admin\Product_Sets();
61
 
62
  // add a modal in admin product pages
63
  add_action( 'admin_footer', array( $this, 'render_modal_template' ) );
141
  if ( in_array( $current_screen->id, $modal_screens, true ) || facebook_for_woocommerce()->is_plugin_settings() ) {
142
 
143
  // enqueue modal functions
144
+ wp_enqueue_script(
145
+ 'facebook-for-woocommerce-modal',
146
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/modal.js',
147
+ array( 'jquery', 'wc-backbone-modal', 'jquery-blockui' ),
148
+ \WC_Facebookcommerce::PLUGIN_VERSION
149
+ );
150
  }
151
 
152
  if ( 'edit-fb_product_set' === $current_screen->id ) {
153
 
154
  // enqueue WooCommerce Admin Styles because of Select2
155
+ wp_enqueue_style(
156
+ 'woocommerce_admin_styles',
157
+ WC()->plugin_url() . '/assets/css/admin.css',
158
+ array(),
159
+ \WC_Facebookcommerce::PLUGIN_VERSION
160
+ );
161
+ wp_enqueue_style(
162
+ 'facebook-for-woocommerce-product-sets-admin',
163
+ facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-product-sets-admin.css',
164
+ array(),
165
+ \WC_Facebookcommerce::PLUGIN_VERSION
166
+ );
167
 
168
+ wp_enqueue_script(
169
+ 'facebook-for-woocommerce-product-sets',
170
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/product-sets-admin.js',
171
+ array( 'jquery', 'select2' ),
172
+ \WC_Facebookcommerce::PLUGIN_VERSION,
173
+ true
174
+ );
175
  }
176
 
177
  if ( 'product' === $current_screen->id || 'edit-product' === $current_screen->id ) {
178
 
179
+ wp_enqueue_style(
180
+ 'facebook-for-woocommerce-products-admin',
181
+ facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-products-admin.css',
182
+ array(),
183
+ \WC_Facebookcommerce::PLUGIN_VERSION
184
+ );
185
 
186
+ wp_enqueue_script(
187
+ 'facebook-for-woocommerce-products-admin',
188
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/products-admin.js',
189
+ array( 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'facebook-for-woocommerce-modal' ),
190
+ \WC_Facebookcommerce::PLUGIN_VERSION
191
+ );
192
 
193
  wp_localize_script(
194
  'facebook-for-woocommerce-products-admin',
195
  'facebook_for_woocommerce_products_admin',
196
+ array(
197
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
198
+ '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,
199
+ 'enhanced_attribute_page_type_edit_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_CATEGORY,
200
+ 'enhanced_attribute_page_type_add_category' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_ADD_CATEGORY,
201
+ 'enhanced_attribute_page_type_edit_product' => \SkyVerge\WooCommerce\Facebook\Admin\Enhanced_Catalog_Attribute_Fields::PAGE_TYPE_EDIT_PRODUCT,
202
+ 'is_sync_enabled_for_product' => $this->is_sync_enabled_for_current_product(),
203
+ 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ),
204
+ 'set_product_sync_prompt_nonce' => wp_create_nonce( 'set-product-sync-prompt' ),
205
+ 'set_product_sync_bulk_action_prompt_nonce' => wp_create_nonce( 'set-product-sync-bulk-action-prompt' ),
206
+ 'product_not_ready_modal_message' => $this->get_product_not_ready_modal_message(),
207
+ 'product_not_ready_modal_buttons' => $this->get_product_not_ready_modal_buttons(),
208
  'product_removed_from_sync_confirm_modal_message' => $this->get_product_removed_from_sync_confirm_modal_message(),
209
  'product_removed_from_sync_confirm_modal_buttons' => $this->get_product_removed_from_sync_confirm_modal_buttons(),
210
+ 'product_removed_from_sync_field_id' => '#' . \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
211
+ 'i18n' => array(
212
  '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' ),
213
+ ),
214
+ )
215
  );
216
 
217
  }//end if
223
  }
224
  }//end if
225
 
226
+ wp_enqueue_script(
227
+ 'wc-facebook-google-product-category-fields',
228
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/google-product-category-fields.js',
229
+ array( 'jquery' ),
230
+ \WC_Facebookcommerce::PLUGIN_VERSION
231
+ );
232
 
233
  wp_localize_script(
234
  'wc-facebook-google-product-category-fields',
280
 
281
  <ul class="ul-disc">
282
  <li><?php esc_html_e( 'Has a price defined', 'facebook-for-woocommerce' ); ?></li>
283
+ <li>
284
+ <?php
285
+ echo esc_html(
286
+ sprintf(
287
  /* translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag */
288
+ __( 'Has %1$sManage Stock%2$s enabled on the %1$sInventory%2$s tab', 'facebook-for-woocommerce' ),
289
+ '<strong>',
290
+ '</strong>'
291
+ )
292
+ );
293
+ ?>
294
+ </li>
295
+ <li>
296
+ <?php
297
+ echo esc_html(
298
+ sprintf(
299
  /* translators: Placeholders: %1$s - <strong> opening HTML tag, %2$s - </strong> closing HTML tag */
300
+ __( 'Has the %1$sFacebook Sync%2$s setting set to "Sync and show" or "Sync and hide"', 'facebook-for-woocommerce' ),
301
+ '<strong>',
302
+ '</strong>'
303
+ )
304
+ );
305
+ ?>
306
+ </li>
307
  </ul>
308
  <?php
309
 
347
  ob_start();
348
 
349
  ?>
350
+ <p>
351
+ <?php
352
+ printf(
353
  /* translators: Placeholders: %1$s - opening <a> link tag, %2$s - closing </a> link tag */
354
+ __( '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?', 'facebook-for-woocommerce' ),
355
+ '<a href="https://www.facebook.com/products" target="_blank">',
356
+ '</a>'
357
+ );
358
+ ?>
359
+ </p>
360
  <?php
361
 
362
  return ob_get_clean();
451
  } else {
452
  esc_html_e( 'Sync and hide', 'facebook-for-woocommerce' );
453
  }
 
454
  } else {
455
 
456
  esc_html_e( 'Do not sync', 'facebook-for-woocommerce' );
1328
  )
1329
  );
1330
 
1331
+ woocommerce_wp_hidden_input(
1332
+ array(
1333
+ 'id' => \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
1334
+ 'value' => '',
1335
+ )
1336
+ );
1337
 
1338
  ?>
1339
  </div>
includes/Admin/Abstract_Settings_Screen.php CHANGED
@@ -118,6 +118,7 @@ abstract class Abstract_Settings_Screen {
118
  * Gets the settings.
119
  *
120
  * Should return a multi-dimensional array of settings in the format expected by \WC_Admin_Settings
 
121
  * @return array
122
  */
123
  abstract public function get_settings();
118
  * Gets the settings.
119
  *
120
  * Should return a multi-dimensional array of settings in the format expected by \WC_Admin_Settings
121
+ *
122
  * @return array
123
  */
124
  abstract public function get_settings();
includes/Admin/Enhanced_Catalog_Attribute_Fields.php CHANGED
@@ -43,7 +43,7 @@ class Enhanced_Catalog_Attribute_Fields {
43
  }
44
 
45
  private function extract_attribute( &$attributes, $key ) {
46
- $index = array_search($key, array_column( $attributes, 'key' ));
47
  $extracted = false === $index ? array() : array_splice( $attributes, $index, 1 );
48
  return empty( $extracted ) ? null : array_shift( $extracted );
49
  }
43
  }
44
 
45
  private function extract_attribute( &$attributes, $key ) {
46
+ $index = array_search( $key, array_column( $attributes, 'key' ) );
47
  $extracted = false === $index ? array() : array_splice( $attributes, $index, 1 );
48
  return empty( $extracted ) ? null : array_shift( $extracted );
49
  }
includes/Admin/Product_Categories.php CHANGED
@@ -59,7 +59,7 @@ class Product_Categories {
59
 
60
  wp_enqueue_script(
61
  'wc-facebook-product-categories',
62
- facebook_for_woocommerce()->get_plugin_url() . '/assets/js/admin/product-categories.min.js',
63
  array(
64
  'jquery',
65
  'wc-backbone-modal',
@@ -269,7 +269,7 @@ class Product_Categories {
269
  * @since 2.1.0
270
  *
271
  * @param \WP_Term $term current taxonomy term object.
272
- * @param string $category_id passed in category id
273
  */
274
  public function render_edit_enhanced_catalog_attributes_field( \WP_Term $term, $category_id = null ) {
275
  if ( empty( $category_id ) ) {
59
 
60
  wp_enqueue_script(
61
  'wc-facebook-product-categories',
62
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/product-categories.js',
63
  array(
64
  'jquery',
65
  'wc-backbone-modal',
269
  * @since 2.1.0
270
  *
271
  * @param \WP_Term $term current taxonomy term object.
272
+ * @param string $category_id passed in category id
273
  */
274
  public function render_edit_enhanced_catalog_attributes_field( \WP_Term $term, $category_id = null ) {
275
  if ( empty( $category_id ) ) {
includes/Admin/Settings.php CHANGED
@@ -76,7 +76,7 @@ class Settings {
76
  if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
77
 
78
  $is_marketing_enabled = is_callable( '\Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
79
- && \Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
80
 
81
  if ( $is_marketing_enabled ) {
82
 
@@ -88,8 +88,9 @@ class Settings {
88
  $root_menu_item,
89
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
90
  __( 'Facebook', 'facebook-for-woocommerce' ),
91
- 'manage_woocommerce', self::PAGE_ID,
92
- [ $this, 'render' ],
 
93
  5
94
  );
95
 
@@ -109,33 +110,35 @@ class Settings {
109
 
110
  if ( is_callable( 'wc_admin_connect_page' ) ) {
111
 
112
- $crumbs = [
113
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
114
- ];
115
 
116
  if ( ! empty( $_GET['tab'] ) ) {
117
  switch ( $_GET['tab'] ) {
118
- case Settings_Screens\Connection::ID :
119
  $crumbs[] = __( 'Connection', 'facebook-for-woocommerce' );
120
- break;
121
- case Settings_Screens\Messenger::ID :
122
  $crumbs[] = __( 'Messenger', 'facebook-for-woocommerce' );
123
- break;
124
- case Settings_Screens\Product_Sync::ID :
125
  $crumbs[] = __( 'Product sync', 'facebook-for-woocommerce' );
126
- break;
127
- case Settings_Screens\Advertise::ID :
128
  $crumbs[] = __( 'Advertise', 'facebook-for-woocommerce' );
129
- break;
130
  }
131
  }
132
 
133
- wc_admin_connect_page( [
134
- 'id' => self::PAGE_ID,
135
- 'screen_id' => $screen_id,
136
- 'path' => add_query_arg( 'page', self::PAGE_ID, 'admin.php' ),
137
- 'title' => $crumbs
138
- ] );
 
 
139
  }
140
  }
141
 
@@ -160,7 +163,7 @@ class Settings {
160
 
161
  <div class="wrap woocommerce">
162
 
163
- <?php if ( ! $this->use_woo_nav ): ?>
164
  <nav class="nav-tab-wrapper woo-nav-tab-wrapper">
165
 
166
  <?php foreach ( $tabs as $id => $label ) : ?>
@@ -325,14 +328,14 @@ class Settings {
325
  );
326
 
327
  $order = 1;
328
- foreach( $this->get_screens() as $screen_id => $screen ) {
329
  $url = $screen instanceof Settings_Screens\Product_Sets
330
  ? 'edit-tags.php?taxonomy=fb_product_set&post_type=product'
331
  : 'wc-facebook&tab=' . $screen->get_id();
332
 
333
  WooAdminMenu::add_plugin_item(
334
  array(
335
- 'id' => 'facebook-for-woocommerce-'. $screen->get_id(),
336
  'parent' => 'facebook-for-woocommerce',
337
  'title' => $screen->get_label(),
338
  'url' => $url,
76
  if ( Framework\SV_WC_Plugin_Compatibility::is_enhanced_admin_available() ) {
77
 
78
  $is_marketing_enabled = is_callable( '\Automattic\WooCommerce\Admin\Loader::is_feature_enabled' )
79
+ && \Automattic\WooCommerce\Admin\Loader::is_feature_enabled( 'marketing' );
80
 
81
  if ( $is_marketing_enabled ) {
82
 
88
  $root_menu_item,
89
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
90
  __( 'Facebook', 'facebook-for-woocommerce' ),
91
+ 'manage_woocommerce',
92
+ self::PAGE_ID,
93
+ array( $this, 'render' ),
94
  5
95
  );
96
 
110
 
111
  if ( is_callable( 'wc_admin_connect_page' ) ) {
112
 
113
+ $crumbs = array(
114
  __( 'Facebook for WooCommerce', 'facebook-for-woocommerce' ),
115
+ );
116
 
117
  if ( ! empty( $_GET['tab'] ) ) {
118
  switch ( $_GET['tab'] ) {
119
+ case Settings_Screens\Connection::ID:
120
  $crumbs[] = __( 'Connection', 'facebook-for-woocommerce' );
121
+ break;
122
+ case Settings_Screens\Messenger::ID:
123
  $crumbs[] = __( 'Messenger', 'facebook-for-woocommerce' );
124
+ break;
125
+ case Settings_Screens\Product_Sync::ID:
126
  $crumbs[] = __( 'Product sync', 'facebook-for-woocommerce' );
127
+ break;
128
+ case Settings_Screens\Advertise::ID:
129
  $crumbs[] = __( 'Advertise', 'facebook-for-woocommerce' );
130
+ break;
131
  }
132
  }
133
 
134
+ wc_admin_connect_page(
135
+ array(
136
+ 'id' => self::PAGE_ID,
137
+ 'screen_id' => $screen_id,
138
+ 'path' => add_query_arg( 'page', self::PAGE_ID, 'admin.php' ),
139
+ 'title' => $crumbs,
140
+ )
141
+ );
142
  }
143
  }
144
 
163
 
164
  <div class="wrap woocommerce">
165
 
166
+ <?php if ( ! $this->use_woo_nav ) : ?>
167
  <nav class="nav-tab-wrapper woo-nav-tab-wrapper">
168
 
169
  <?php foreach ( $tabs as $id => $label ) : ?>
328
  );
329
 
330
  $order = 1;
331
+ foreach ( $this->get_screens() as $screen_id => $screen ) {
332
  $url = $screen instanceof Settings_Screens\Product_Sets
333
  ? 'edit-tags.php?taxonomy=fb_product_set&post_type=product'
334
  : 'wc-facebook&tab=' . $screen->get_id();
335
 
336
  WooAdminMenu::add_plugin_item(
337
  array(
338
+ 'id' => 'facebook-for-woocommerce-' . $screen->get_id(),
339
  'parent' => 'facebook-for-woocommerce',
340
  'title' => $screen->get_label(),
341
  'url' => $url,
includes/Admin/Settings_Screens/Advertise.php CHANGED
@@ -48,9 +48,9 @@ class Advertise extends Admin\Abstract_Settings_Screen {
48
  */
49
  private function add_hooks() {
50
 
51
- add_action( 'admin_head', [ $this, 'output_scripts' ] );
52
 
53
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
54
  }
55
 
56
 
@@ -67,7 +67,7 @@ class Advertise extends Admin\Abstract_Settings_Screen {
67
  return;
68
  }
69
 
70
- wp_enqueue_style( 'wc-facebook-admin-advertise-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-advertise.css', [], \WC_Facebookcommerce::VERSION );
71
  }
72
 
73
 
@@ -113,23 +113,23 @@ class Advertise extends Admin\Abstract_Settings_Screen {
113
  $connection_handler = facebook_for_woocommerce()->get_connection_handler();
114
 
115
  if ( ! $connection_handler || ! $connection_handler->is_connected() ) {
116
- return [];
117
  }
118
 
119
- return [
120
- 'business_config' => [
121
- 'business' => [
122
  'name' => $connection_handler->get_business_name(),
123
- ],
124
- ],
125
- 'setup' => [
126
  'external_business_id' => $connection_handler->get_external_business_id(),
127
  'timezone' => $this->parse_timezone( wc_timezone_string(), wc_timezone_offset() ),
128
  'currency' => get_woocommerce_currency(),
129
  'business_vertical' => 'ECOMMERCE',
130
- ],
131
  'repeat' => false,
132
- ];
133
  }
134
 
135
 
@@ -200,7 +200,7 @@ class Advertise extends Admin\Abstract_Settings_Screen {
200
  printf(
201
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
202
  esc_html__( 'Please %1$sconnect your store%2$s to Facebook to create ads.', 'facebook-for-woocommerce' ),
203
- '<a href="' . esc_url( add_query_arg( [ 'tab' => Connection::ID ], facebook_for_woocommerce()->get_settings_url() ) ) . '">',
204
  '</a>'
205
  );
206
 
@@ -238,7 +238,7 @@ class Advertise extends Admin\Abstract_Settings_Screen {
238
  */
239
  public function get_settings() {
240
 
241
- return [];
242
  }
243
 
244
 
48
  */
49
  private function add_hooks() {
50
 
51
+ add_action( 'admin_head', array( $this, 'output_scripts' ) );
52
 
53
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
54
  }
55
 
56
 
67
  return;
68
  }
69
 
70
+ 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 );
71
  }
72
 
73
 
113
  $connection_handler = facebook_for_woocommerce()->get_connection_handler();
114
 
115
  if ( ! $connection_handler || ! $connection_handler->is_connected() ) {
116
+ return array();
117
  }
118
 
119
+ return array(
120
+ 'business_config' => array(
121
+ 'business' => array(
122
  'name' => $connection_handler->get_business_name(),
123
+ ),
124
+ ),
125
+ 'setup' => array(
126
  'external_business_id' => $connection_handler->get_external_business_id(),
127
  'timezone' => $this->parse_timezone( wc_timezone_string(), wc_timezone_offset() ),
128
  'currency' => get_woocommerce_currency(),
129
  'business_vertical' => 'ECOMMERCE',
130
+ ),
131
  'repeat' => false,
132
+ );
133
  }
134
 
135
 
200
  printf(
201
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
202
  esc_html__( 'Please %1$sconnect your store%2$s to Facebook to create ads.', 'facebook-for-woocommerce' ),
203
+ '<a href="' . esc_url( add_query_arg( array( 'tab' => Connection::ID ), facebook_for_woocommerce()->get_settings_url() ) ) . '">',
204
  '</a>'
205
  );
206
 
238
  */
239
  public function get_settings() {
240
 
241
+ return array();
242
  }
243
 
244
 
includes/Admin/Settings_Screens/Connection.php CHANGED
@@ -34,9 +34,9 @@ class Connection extends Admin\Abstract_Settings_Screen {
34
  $this->label = __( 'Connection', 'facebook-for-woocommerce' );
35
  $this->title = __( 'Connection', 'facebook-for-woocommerce' );
36
 
37
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
38
 
39
- add_action( 'admin_notices', [ $this, 'add_notices' ] );
40
  }
41
 
42
 
@@ -55,14 +55,21 @@ class Connection extends Admin\Abstract_Settings_Screen {
55
  $message = sprintf(
56
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag */
57
  __( '%1$sHeads up!%2$s It looks like there was a problem with reconnecting your site to Facebook. Please %3$sclick here%4$s to try again, or %5$sget in touch with our support team%6$s for assistance.', 'facebook-for-woocommerce' ),
58
- '<strong>', '</strong>',
59
- '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">', '</a>',
60
- '<a href="' . esc_url( facebook_for_woocommerce()->get_support_url() ) . '" target="_blank">', '</a>'
 
 
 
61
  );
62
 
63
- facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice( $message, 'wc_facebook_connection_failed', [
64
- 'notice_class' => 'error',
65
- ] );
 
 
 
 
66
 
67
  delete_transient( 'wc_facebook_connection_failed' );
68
  }
@@ -82,7 +89,7 @@ class Connection extends Admin\Abstract_Settings_Screen {
82
  return;
83
  }
84
 
85
- wp_enqueue_style( 'wc-facebook-admin-connection-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-connection.css', [], \WC_Facebookcommerce::VERSION );
86
  }
87
 
88
 
@@ -118,37 +125,37 @@ class Connection extends Admin\Abstract_Settings_Screen {
118
  * TODO: add pixel & ad account API retrieval when we gain the ads_management permission
119
  * TODO: add the page name and link when we gain the manage_pages permission
120
  */
121
- $static_items = [
122
- 'page' => [
123
  'label' => __( 'Page', 'facebook-for-woocommerce' ),
124
  'value' => facebook_for_woocommerce()->get_integration()->get_facebook_page_id(),
125
- ],
126
- 'pixel' => [
127
  'label' => __( 'Pixel', 'facebook-for-woocommerce' ),
128
  'value' => facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(),
129
- ],
130
- 'catalog' => [
131
  'label' => __( 'Catalog', 'facebook-for-woocommerce' ),
132
  'value' => facebook_for_woocommerce()->get_integration()->get_product_catalog_id(),
133
  'url' => 'https://facebook.com/products',
134
- ],
135
- 'business-manager' => [
136
  'label' => __( 'Business Manager account', 'facebook-for-woocommerce' ),
137
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_business_manager_id(),
138
- ],
139
- 'ad-account' => [
140
  'label' => __( 'Ad Manager account', 'facebook-for-woocommerce' ),
141
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_ad_account_id(),
142
- ],
143
- 'instagram-business-id' => [
144
  'label' => __( 'Instagram Business ID', 'facebook-for-woocommerce' ),
145
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_instagram_business_id(),
146
- ],
147
- 'commerce-merchant-settings-id' => [
148
  'label' => __( 'Commerce Merchant Settings ID', 'facebook-for-woocommerce' ),
149
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_commerce_merchant_settings_id(),
150
- ],
151
- ];
152
 
153
  // if the catalog ID is set, update the URL and try to get its name for display
154
  if ( $catalog_id = $static_items['catalog']['value'] ) {
@@ -162,8 +169,8 @@ class Connection extends Admin\Abstract_Settings_Screen {
162
  if ( $name = $response->get_name() ) {
163
  $static_items['catalog']['value'] = $name;
164
  }
165
-
166
- } catch ( Framework\SV_WC_API_Exception $exception ) {}
167
  }
168
 
169
  ?>
@@ -171,13 +178,17 @@ class Connection extends Admin\Abstract_Settings_Screen {
171
  <table class="form-table">
172
  <tbody>
173
 
174
- <?php foreach ( $static_items as $id => $item ) :
 
175
 
176
- $item = wp_parse_args( $item, [
177
- 'label' => '',
178
- 'value' => '',
179
- 'url' => '',
180
- ] );
 
 
 
181
 
182
  ?>
183
 
@@ -212,7 +223,7 @@ class Connection extends Admin\Abstract_Settings_Screen {
212
 
213
  <?php else : ?>
214
 
215
- <?php echo '-' ?>
216
 
217
  <?php endif; ?>
218
 
@@ -246,11 +257,11 @@ class Connection extends Admin\Abstract_Settings_Screen {
246
  }
247
 
248
  $subtitle = __( 'Use this WooCommerce and Facebook integration to:', 'facebook-for-woocommerce' );
249
- $benefits = [
250
- __( 'Create an ad in a few steps', 'facebook-for-woocommerce'),
251
- __( 'Use built-in best practices for online sales', 'facebook-for-woocommerce'),
252
- __( 'Get reporting on sales and revenue', 'facebook-for-woocommerce'),
253
- ];
254
 
255
  ?>
256
 
@@ -304,25 +315,25 @@ class Connection extends Admin\Abstract_Settings_Screen {
304
  */
305
  public function get_settings() {
306
 
307
- return [
308
 
309
- [
310
  'title' => __( 'Debug', 'facebook-for-woocommerce' ),
311
  'type' => 'title',
312
- ],
313
 
314
- [
315
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
316
  'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ),
317
  'type' => 'checkbox',
318
  'desc' => __( 'Log plugin events for debugging', 'facebook-for-woocommerce' ),
319
  'desc_tip' => __( 'Only enable this if you are experiencing problems with the plugin.', 'facebook-for-woocommerce' ),
320
  'default' => 'no',
321
- ],
322
 
323
- [ 'type' => 'sectionend' ],
324
 
325
- ];
326
  }
327
 
328
 
34
  $this->label = __( 'Connection', 'facebook-for-woocommerce' );
35
  $this->title = __( 'Connection', 'facebook-for-woocommerce' );
36
 
37
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
38
 
39
+ add_action( 'admin_notices', array( $this, 'add_notices' ) );
40
  }
41
 
42
 
55
  $message = sprintf(
56
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag */
57
  __( '%1$sHeads up!%2$s It looks like there was a problem with reconnecting your site to Facebook. Please %3$sclick here%4$s to try again, or %5$sget in touch with our support team%6$s for assistance.', 'facebook-for-woocommerce' ),
58
+ '<strong>',
59
+ '</strong>',
60
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">',
61
+ '</a>',
62
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_support_url() ) . '" target="_blank">',
63
+ '</a>'
64
  );
65
 
66
+ facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice(
67
+ $message,
68
+ 'wc_facebook_connection_failed',
69
+ array(
70
+ 'notice_class' => 'error',
71
+ )
72
+ );
73
 
74
  delete_transient( 'wc_facebook_connection_failed' );
75
  }
89
  return;
90
  }
91
 
92
+ wp_enqueue_style( 'wc-facebook-admin-connection-settings', facebook_for_woocommerce()->get_plugin_url() . '/assets/css/admin/facebook-for-woocommerce-connection.css', array(), \WC_Facebookcommerce::VERSION );
93
  }
94
 
95
 
125
  * TODO: add pixel & ad account API retrieval when we gain the ads_management permission
126
  * TODO: add the page name and link when we gain the manage_pages permission
127
  */
128
+ $static_items = array(
129
+ 'page' => array(
130
  'label' => __( 'Page', 'facebook-for-woocommerce' ),
131
  'value' => facebook_for_woocommerce()->get_integration()->get_facebook_page_id(),
132
+ ),
133
+ 'pixel' => array(
134
  'label' => __( 'Pixel', 'facebook-for-woocommerce' ),
135
  'value' => facebook_for_woocommerce()->get_integration()->get_facebook_pixel_id(),
136
+ ),
137
+ 'catalog' => array(
138
  'label' => __( 'Catalog', 'facebook-for-woocommerce' ),
139
  'value' => facebook_for_woocommerce()->get_integration()->get_product_catalog_id(),
140
  'url' => 'https://facebook.com/products',
141
+ ),
142
+ 'business-manager' => array(
143
  'label' => __( 'Business Manager account', 'facebook-for-woocommerce' ),
144
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_business_manager_id(),
145
+ ),
146
+ 'ad-account' => array(
147
  'label' => __( 'Ad Manager account', 'facebook-for-woocommerce' ),
148
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_ad_account_id(),
149
+ ),
150
+ 'instagram-business-id' => array(
151
  'label' => __( 'Instagram Business ID', 'facebook-for-woocommerce' ),
152
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_instagram_business_id(),
153
+ ),
154
+ 'commerce-merchant-settings-id' => array(
155
  'label' => __( 'Commerce Merchant Settings ID', 'facebook-for-woocommerce' ),
156
  'value' => facebook_for_woocommerce()->get_connection_handler()->get_commerce_merchant_settings_id(),
157
+ ),
158
+ );
159
 
160
  // if the catalog ID is set, update the URL and try to get its name for display
161
  if ( $catalog_id = $static_items['catalog']['value'] ) {
169
  if ( $name = $response->get_name() ) {
170
  $static_items['catalog']['value'] = $name;
171
  }
172
+ } catch ( Framework\SV_WC_API_Exception $exception ) {
173
+ }
174
  }
175
 
176
  ?>
178
  <table class="form-table">
179
  <tbody>
180
 
181
+ <?php
182
+ foreach ( $static_items as $id => $item ) :
183
 
184
+ $item = wp_parse_args(
185
+ $item,
186
+ array(
187
+ 'label' => '',
188
+ 'value' => '',
189
+ 'url' => '',
190
+ )
191
+ );
192
 
193
  ?>
194
 
223
 
224
  <?php else : ?>
225
 
226
+ <?php echo '-'; ?>
227
 
228
  <?php endif; ?>
229
 
257
  }
258
 
259
  $subtitle = __( 'Use this WooCommerce and Facebook integration to:', 'facebook-for-woocommerce' );
260
+ $benefits = array(
261
+ __( 'Create an ad in a few steps', 'facebook-for-woocommerce' ),
262
+ __( 'Use built-in best practices for online sales', 'facebook-for-woocommerce' ),
263
+ __( 'Get reporting on sales and revenue', 'facebook-for-woocommerce' ),
264
+ );
265
 
266
  ?>
267
 
315
  */
316
  public function get_settings() {
317
 
318
+ return array(
319
 
320
+ array(
321
  'title' => __( 'Debug', 'facebook-for-woocommerce' ),
322
  'type' => 'title',
323
+ ),
324
 
325
+ array(
326
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
327
  'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ),
328
  'type' => 'checkbox',
329
  'desc' => __( 'Log plugin events for debugging', 'facebook-for-woocommerce' ),
330
  'desc_tip' => __( 'Only enable this if you are experiencing problems with the plugin.', 'facebook-for-woocommerce' ),
331
  'default' => 'no',
332
+ ),
333
 
334
+ array( 'type' => 'sectionend' ),
335
 
336
+ );
337
  }
338
 
339
 
includes/Admin/Settings_Screens/Messenger.php CHANGED
@@ -40,10 +40,10 @@ class Messenger extends Admin\Abstract_Settings_Screen {
40
  $this->label = __( 'Messenger', 'facebook-for-woocommerce' );
41
  $this->title = __( 'Messenger', 'facebook-for-woocommerce' );
42
 
43
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
44
 
45
- add_action( 'woocommerce_admin_field_messenger_locale', [ $this, 'render_locale_field' ] );
46
- add_action( 'woocommerce_admin_field_messenger_greeting', [ $this, 'render_greeting_field' ] );
47
  }
48
 
49
 
@@ -115,11 +115,14 @@ class Messenger extends Admin\Abstract_Settings_Screen {
115
  </th>
116
  <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $field['type'] ) ); ?>">
117
  <p>
118
- <?php printf(
 
119
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
120
  esc_html__( '%1$sClick here%2$s to manage your Messenger greeting and colors.', 'facebook-for-woocommerce' ),
121
- '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_manage_url() ) . '" target="_blank">', '</a>'
122
- ); ?>
 
 
123
  </p>
124
  </td>
125
  </tr>
@@ -138,38 +141,38 @@ class Messenger extends Admin\Abstract_Settings_Screen {
138
 
139
  $is_enabled = $this->remote_configuration && $this->remote_configuration->is_enabled();
140
 
141
- $settings = [
142
 
143
- [
144
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
145
  'type' => 'title',
146
- ],
147
 
148
- [
149
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER,
150
  'title' => __( 'Enable Messenger', 'facebook-for-woocommerce' ),
151
  'type' => 'checkbox',
152
  'desc' => __( 'Enable and customize Facebook Messenger on your store', 'facebook-for-woocommerce' ),
153
  'default' => 'no',
154
  'value' => $is_enabled ? 'yes' : 'no',
155
- ],
156
- ];
157
 
158
  // only add the static configuration display if messenger is enabled
159
  if ( $is_enabled ) {
160
 
161
- $settings[] = [
162
  'title' => __( 'Language', 'facebook-for-woocommerce' ),
163
  'type' => 'messenger_locale',
164
- ];
165
 
166
- $settings[] = [
167
  'title' => __( 'Greeting & Colors', 'facebook-for-woocommerce' ),
168
  'type' => 'messenger_greeting',
169
- ];
170
  }
171
 
172
- $settings[] = [ 'type' => 'sectionend' ];
173
 
174
  return $settings;
175
  }
@@ -187,7 +190,8 @@ class Messenger extends Admin\Abstract_Settings_Screen {
187
  return sprintf(
188
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
189
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger.', 'facebook-for-woocommerce' ),
190
- '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">', '</a>'
 
191
  );
192
  }
193
 
@@ -235,11 +239,14 @@ class Messenger extends Admin\Abstract_Settings_Screen {
235
  ?>
236
  <div class="notice notice-error">
237
  <p>
238
- <?php printf(
 
239
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
240
  esc_html__( 'There was an error communicating with the Facebook Business Extension. %1$sClick here%2$s to manage your Messenger settings.', 'facebook-for-woocommerce' ),
241
- '<a href="' . esc_url( $plugin->get_connection_handler()->get_manage_url() ) . '" target="_blank">', '</a>'
242
- ); ?>
 
 
243
  </p>
244
  </div>
245
  <?php
40
  $this->label = __( 'Messenger', 'facebook-for-woocommerce' );
41
  $this->title = __( 'Messenger', 'facebook-for-woocommerce' );
42
 
43
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
44
 
45
+ add_action( 'woocommerce_admin_field_messenger_locale', array( $this, 'render_locale_field' ) );
46
+ add_action( 'woocommerce_admin_field_messenger_greeting', array( $this, 'render_greeting_field' ) );
47
  }
48
 
49
 
115
  </th>
116
  <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $field['type'] ) ); ?>">
117
  <p>
118
+ <?php
119
+ printf(
120
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
121
  esc_html__( '%1$sClick here%2$s to manage your Messenger greeting and colors.', 'facebook-for-woocommerce' ),
122
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_manage_url() ) . '" target="_blank">',
123
+ '</a>'
124
+ );
125
+ ?>
126
  </p>
127
  </td>
128
  </tr>
141
 
142
  $is_enabled = $this->remote_configuration && $this->remote_configuration->is_enabled();
143
 
144
+ $settings = array(
145
 
146
+ array(
147
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
148
  'type' => 'title',
149
+ ),
150
 
151
+ array(
152
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_MESSENGER,
153
  'title' => __( 'Enable Messenger', 'facebook-for-woocommerce' ),
154
  'type' => 'checkbox',
155
  'desc' => __( 'Enable and customize Facebook Messenger on your store', 'facebook-for-woocommerce' ),
156
  'default' => 'no',
157
  'value' => $is_enabled ? 'yes' : 'no',
158
+ ),
159
+ );
160
 
161
  // only add the static configuration display if messenger is enabled
162
  if ( $is_enabled ) {
163
 
164
+ $settings[] = array(
165
  'title' => __( 'Language', 'facebook-for-woocommerce' ),
166
  'type' => 'messenger_locale',
167
+ );
168
 
169
+ $settings[] = array(
170
  'title' => __( 'Greeting & Colors', 'facebook-for-woocommerce' ),
171
  'type' => 'messenger_greeting',
172
+ );
173
  }
174
 
175
+ $settings[] = array( 'type' => 'sectionend' );
176
 
177
  return $settings;
178
  }
190
  return sprintf(
191
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
192
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage Facebook Messenger.', 'facebook-for-woocommerce' ),
193
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">',
194
+ '</a>'
195
  );
196
  }
197
 
239
  ?>
240
  <div class="notice notice-error">
241
  <p>
242
+ <?php
243
+ printf(
244
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
245
  esc_html__( 'There was an error communicating with the Facebook Business Extension. %1$sClick here%2$s to manage your Messenger settings.', 'facebook-for-woocommerce' ),
246
+ '<a href="' . esc_url( $plugin->get_connection_handler()->get_manage_url() ) . '" target="_blank">',
247
+ '</a>'
248
+ );
249
+ ?>
250
  </p>
251
  </div>
252
  <?php
includes/Admin/Settings_Screens/Product_Sync.php CHANGED
@@ -42,11 +42,11 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
42
  $this->label = __( 'Product sync', 'facebook-for-woocommerce' );
43
  $this->title = __( 'Product sync', 'facebook-for-woocommerce' );
44
 
45
- add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
46
 
47
- add_action( 'woocommerce_admin_field_product_sync_title', [ $this, 'render_title' ] );
48
 
49
- add_action( 'woocommerce_admin_field_product_sync_google_product_categories', [ $this, 'render_google_product_category_field' ] );
50
  }
51
 
52
 
@@ -63,35 +63,49 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
63
  return;
64
  }
65
 
66
- wp_enqueue_script( 'wc-backbone-modal', null, [ 'backbone' ] );
67
- wp_enqueue_script( 'facebook-for-woocommerce-modal', plugins_url( '/facebook-for-woocommerce/assets/js/facebook-for-woocommerce-modal.min.js' ), [ 'jquery', 'wc-backbone-modal', 'jquery-blockui' ], \WC_Facebookcommerce::PLUGIN_VERSION );
68
- wp_enqueue_script( 'facebook-for-woocommerce-settings-sync', plugins_url( '/facebook-for-woocommerce/assets/js/admin/facebook-for-woocommerce-settings-sync.min.js' ), [ 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'jquery-tiptip', 'facebook-for-woocommerce-modal', 'wc-enhanced-select' ], \WC_Facebookcommerce::PLUGIN_VERSION );
 
 
 
 
 
 
 
 
 
 
69
 
70
  /* translators: Placeholders: {count} number of remaining items */
71
  $sync_remaining_items_string = _n_noop( '{count} item remaining.', '{count} items remaining.', 'facebook-for-woocommerce' );
72
 
73
- wp_localize_script( 'facebook-for-woocommerce-settings-sync', 'facebook_for_woocommerce_settings_sync', [
74
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
75
- 'set_excluded_terms_prompt_nonce' => wp_create_nonce( 'set-excluded-terms-prompt' ),
76
- 'sync_products_nonce' => wp_create_nonce( self::ACTION_SYNC_PRODUCTS ),
77
- 'sync_status_nonce' => wp_create_nonce( self::ACTION_GET_SYNC_STATUS ),
78
- 'sync_in_progress' => Sync::is_sync_in_progress(),
79
- 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(),
80
- 'excluded_tag_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_tag_ids(),
81
- 'i18n' => [
82
- /* translators: Placeholders %s - html code for a spinner icon */
83
- 'confirm_resync' => esc_html__( 'Your products will now be resynced to Facebook, this may take some time.', 'facebook-for-woocommerce' ),
84
- 'confirm_sync' => esc_html__( "Facebook for WooCommerce automatically syncs your products on create/update. Are you sure you want to force product resync?\n\nThis 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.", 'facebook-for-woocommerce' ),
85
- 'sync_in_progress' => sprintf( esc_html__( 'Your products are syncing - you may safely leave this page %s', 'facebook-for-woocommerce' ), '<span class="spinner is-active"></span>' ),
86
- 'sync_remaining_items_singular' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 1 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
87
- 'sync_remaining_items_plural' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 2 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
88
- 'general_error' => esc_html__( 'There was an error trying to sync the products to Facebook.', 'facebook-for-woocommerce' ),
89
- 'feed_upload_error' => esc_html__( 'Something went wrong while uploading the product information, please try again.', 'facebook-for-woocommerce' ),
90
- ],
91
- 'default_google_product_category_modal_message' => $this->get_default_google_product_category_modal_message(),
92
- 'default_google_product_category_modal_message_empty' => $this->get_default_google_product_category_modal_message_empty(),
93
- 'default_google_product_category_modal_buttons' => $this->get_default_google_product_category_modal_buttons(),
94
- ] );
 
 
 
 
95
  }
96
 
97
  /**
@@ -119,7 +133,8 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
119
  return sprintf(
120
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
121
  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' ),
122
- '<strong>', '</strong>'
 
123
  );
124
  }
125
 
@@ -216,16 +231,20 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
216
  private function disable_sync_for_excluded_products( $product_cat_ids, $product_tag_ids ) {
217
 
218
  // disable sync for all products belonging to excluded categories
219
- Products::disable_sync_for_products_with_terms( [
220
- 'taxonomy' => 'product_cat',
221
- 'include' => $product_cat_ids,
222
- ] );
 
 
223
 
224
  // disable sync for all products belonging to excluded tags
225
- Products::disable_sync_for_products_with_terms( [
226
- 'taxonomy' => 'product_tag',
227
- 'include' => $product_tag_ids,
228
- ] );
 
 
229
  }
230
 
231
 
@@ -238,87 +257,91 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
238
  */
239
  public function get_settings() {
240
 
241
- $term_query = new \WP_Term_Query( [
242
- 'taxonomy' => 'product_cat',
243
- 'hide_empty' => false,
244
- 'fields' => 'id=>name',
245
- ] );
 
 
246
 
247
  $product_categories = $term_query->get_terms();
248
 
249
- $term_query = new \WP_Term_Query( [
250
- 'taxonomy' => 'product_tag',
251
- 'hide_empty' => false,
252
- 'hierarchical' => false,
253
- 'fields' => 'id=>name',
254
- ] );
 
 
255
 
256
  $product_tags = $term_query->get_terms();
257
 
258
- return [
259
 
260
- [
261
  'type' => 'product_sync_title',
262
  'title' => __( 'Product sync', 'facebook-for-woocommerce' ),
263
- ],
264
 
265
- [
266
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
267
  'title' => __( 'Enable product sync', 'facebook-for-woocommerce' ),
268
  'type' => 'checkbox',
269
  'label' => ' ',
270
  'default' => 'yes',
271
- ],
272
 
273
- [
274
  'id' => \WC_Facebookcommerce_Integration::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS,
275
  'title' => __( 'Exclude categories from sync', 'facebook-for-woocommerce' ),
276
  'type' => 'multiselect',
277
  'class' => 'wc-enhanced-select product-sync-field',
278
  'css' => 'min-width: 300px;',
279
  'desc_tip' => __( 'Products in one or more of these categories will not sync to Facebook.', 'facebook-for-woocommerce' ),
280
- 'default' => [],
281
- 'options' => is_array( $product_categories ) ? $product_categories : [],
282
- 'custom_attributes' => [
283
  'data-placeholder' => __( 'Search for a product category&hellip;', 'facebook-for-woocommerce' ),
284
- ],
285
- ],
286
 
287
- [
288
  'id' => \WC_Facebookcommerce_Integration::SETTING_EXCLUDED_PRODUCT_TAG_IDS,
289
  'title' => __( 'Exclude tags from sync', 'facebook-for-woocommerce' ),
290
  'type' => 'multiselect',
291
  'class' => 'wc-enhanced-select product-sync-field',
292
  'css' => 'min-width: 300px;',
293
  'desc_tip' => __( 'Products with one or more of these tags will not sync to Facebook.', 'facebook-for-woocommerce' ),
294
- 'default' => [],
295
- 'options' => is_array( $product_tags ) ? $product_tags : [],
296
- 'custom_attributes' => [
297
  'data-placeholder' => __( 'Search for a product tag&hellip;', 'facebook-for-woocommerce' ),
298
- ],
299
- ],
300
 
301
- [
302
  'id' => \WC_Facebookcommerce_Integration::SETTING_PRODUCT_DESCRIPTION_MODE,
303
  'title' => __( 'Product description sync', 'facebook-for-woocommerce' ),
304
  'type' => 'select',
305
  'class' => 'product-sync-field',
306
  'desc_tip' => __( 'Choose which product description to display in the Facebook catalog.', 'facebook-for-woocommerce' ),
307
  'default' => \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_STANDARD,
308
- 'options' => [
309
  \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_STANDARD => __( 'Standard description', 'facebook-for-woocommerce' ),
310
  \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT => __( 'Short description', 'facebook-for-woocommerce' ),
311
- ],
312
- ],
313
- [
314
  'id' => \SkyVerge\WooCommerce\Facebook\Commerce::OPTION_GOOGLE_PRODUCT_CATEGORY_ID,
315
  'type' => 'product_sync_google_product_categories',
316
  'title' => __( 'Default Google product category', 'facebook-for-woocommerce' ),
317
  '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' ),
318
- ],
319
- [ 'type' => 'sectionend' ],
320
 
321
- ];
322
  }
323
 
324
  /**
@@ -361,7 +384,8 @@ class Product_Sync extends Admin\Abstract_Settings_Screen {
361
  return sprintf(
362
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
363
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage product sync.', 'facebook-for-woocommerce' ),
364
- '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">', '</a>'
 
365
  );
366
  }
367
 
42
  $this->label = __( 'Product sync', 'facebook-for-woocommerce' );
43
  $this->title = __( 'Product sync', 'facebook-for-woocommerce' );
44
 
45
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
46
 
47
+ add_action( 'woocommerce_admin_field_product_sync_title', array( $this, 'render_title' ) );
48
 
49
+ add_action( 'woocommerce_admin_field_product_sync_google_product_categories', array( $this, 'render_google_product_category_field' ) );
50
  }
51
 
52
 
63
  return;
64
  }
65
 
66
+ wp_enqueue_script( 'wc-backbone-modal', null, array( 'backbone' ) );
67
+ wp_enqueue_script(
68
+ 'facebook-for-woocommerce-modal',
69
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/modal.js',
70
+ array( 'jquery', 'wc-backbone-modal', 'jquery-blockui' ),
71
+ \WC_Facebookcommerce::PLUGIN_VERSION
72
+ );
73
+ wp_enqueue_script(
74
+ 'facebook-for-woocommerce-settings-sync',
75
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/settings-sync.js',
76
+ array( 'jquery', 'wc-backbone-modal', 'jquery-blockui', 'jquery-tiptip', 'facebook-for-woocommerce-modal', 'wc-enhanced-select' ),
77
+ \WC_Facebookcommerce::PLUGIN_VERSION
78
+ );
79
 
80
  /* translators: Placeholders: {count} number of remaining items */
81
  $sync_remaining_items_string = _n_noop( '{count} item remaining.', '{count} items remaining.', 'facebook-for-woocommerce' );
82
 
83
+ wp_localize_script(
84
+ 'facebook-for-woocommerce-settings-sync',
85
+ 'facebook_for_woocommerce_settings_sync',
86
+ array(
87
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
88
+ 'set_excluded_terms_prompt_nonce' => wp_create_nonce( 'set-excluded-terms-prompt' ),
89
+ 'sync_products_nonce' => wp_create_nonce( self::ACTION_SYNC_PRODUCTS ),
90
+ 'sync_status_nonce' => wp_create_nonce( self::ACTION_GET_SYNC_STATUS ),
91
+ 'sync_in_progress' => Sync::is_sync_in_progress(),
92
+ 'excluded_category_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_category_ids(),
93
+ 'excluded_tag_ids' => facebook_for_woocommerce()->get_integration()->get_excluded_product_tag_ids(),
94
+ 'i18n' => array(
95
+ /* translators: Placeholders %s - html code for a spinner icon */
96
+ 'confirm_resync' => esc_html__( 'Your products will now be resynced to Facebook, this may take some time.', 'facebook-for-woocommerce' ),
97
+ 'confirm_sync' => esc_html__( "Facebook for WooCommerce automatically syncs your products on create/update. Are you sure you want to force product resync?\n\nThis 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.", 'facebook-for-woocommerce' ),
98
+ 'sync_in_progress' => sprintf( esc_html__( 'Your products are syncing - you may safely leave this page %s', 'facebook-for-woocommerce' ), '<span class="spinner is-active"></span>' ),
99
+ 'sync_remaining_items_singular' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 1 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
100
+ 'sync_remaining_items_plural' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 2 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
101
+ 'general_error' => esc_html__( 'There was an error trying to sync the products to Facebook.', 'facebook-for-woocommerce' ),
102
+ 'feed_upload_error' => esc_html__( 'Something went wrong while uploading the product information, please try again.', 'facebook-for-woocommerce' ),
103
+ ),
104
+ 'default_google_product_category_modal_message' => $this->get_default_google_product_category_modal_message(),
105
+ 'default_google_product_category_modal_message_empty' => $this->get_default_google_product_category_modal_message_empty(),
106
+ 'default_google_product_category_modal_buttons' => $this->get_default_google_product_category_modal_buttons(),
107
+ )
108
+ );
109
  }
110
 
111
  /**
133
  return sprintf(
134
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
135
  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' ),
136
+ '<strong>',
137
+ '</strong>'
138
  );
139
  }
140
 
231
  private function disable_sync_for_excluded_products( $product_cat_ids, $product_tag_ids ) {
232
 
233
  // disable sync for all products belonging to excluded categories
234
+ Products::disable_sync_for_products_with_terms(
235
+ array(
236
+ 'taxonomy' => 'product_cat',
237
+ 'include' => $product_cat_ids,
238
+ )
239
+ );
240
 
241
  // disable sync for all products belonging to excluded tags
242
+ Products::disable_sync_for_products_with_terms(
243
+ array(
244
+ 'taxonomy' => 'product_tag',
245
+ 'include' => $product_tag_ids,
246
+ )
247
+ );
248
  }
249
 
250
 
257
  */
258
  public function get_settings() {
259
 
260
+ $term_query = new \WP_Term_Query(
261
+ array(
262
+ 'taxonomy' => 'product_cat',
263
+ 'hide_empty' => false,
264
+ 'fields' => 'id=>name',
265
+ )
266
+ );
267
 
268
  $product_categories = $term_query->get_terms();
269
 
270
+ $term_query = new \WP_Term_Query(
271
+ array(
272
+ 'taxonomy' => 'product_tag',
273
+ 'hide_empty' => false,
274
+ 'hierarchical' => false,
275
+ 'fields' => 'id=>name',
276
+ )
277
+ );
278
 
279
  $product_tags = $term_query->get_terms();
280
 
281
+ return array(
282
 
283
+ array(
284
  'type' => 'product_sync_title',
285
  'title' => __( 'Product sync', 'facebook-for-woocommerce' ),
286
+ ),
287
 
288
+ array(
289
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
290
  'title' => __( 'Enable product sync', 'facebook-for-woocommerce' ),
291
  'type' => 'checkbox',
292
  'label' => ' ',
293
  'default' => 'yes',
294
+ ),
295
 
296
+ array(
297
  'id' => \WC_Facebookcommerce_Integration::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS,
298
  'title' => __( 'Exclude categories from sync', 'facebook-for-woocommerce' ),
299
  'type' => 'multiselect',
300
  'class' => 'wc-enhanced-select product-sync-field',
301
  'css' => 'min-width: 300px;',
302
  'desc_tip' => __( 'Products in one or more of these categories will not sync to Facebook.', 'facebook-for-woocommerce' ),
303
+ 'default' => array(),
304
+ 'options' => is_array( $product_categories ) ? $product_categories : array(),
305
+ 'custom_attributes' => array(
306
  'data-placeholder' => __( 'Search for a product category&hellip;', 'facebook-for-woocommerce' ),
307
+ ),
308
+ ),
309
 
310
+ array(
311
  'id' => \WC_Facebookcommerce_Integration::SETTING_EXCLUDED_PRODUCT_TAG_IDS,
312
  'title' => __( 'Exclude tags from sync', 'facebook-for-woocommerce' ),
313
  'type' => 'multiselect',
314
  'class' => 'wc-enhanced-select product-sync-field',
315
  'css' => 'min-width: 300px;',
316
  'desc_tip' => __( 'Products with one or more of these tags will not sync to Facebook.', 'facebook-for-woocommerce' ),
317
+ 'default' => array(),
318
+ 'options' => is_array( $product_tags ) ? $product_tags : array(),
319
+ 'custom_attributes' => array(
320
  'data-placeholder' => __( 'Search for a product tag&hellip;', 'facebook-for-woocommerce' ),
321
+ ),
322
+ ),
323
 
324
+ array(
325
  'id' => \WC_Facebookcommerce_Integration::SETTING_PRODUCT_DESCRIPTION_MODE,
326
  'title' => __( 'Product description sync', 'facebook-for-woocommerce' ),
327
  'type' => 'select',
328
  'class' => 'product-sync-field',
329
  'desc_tip' => __( 'Choose which product description to display in the Facebook catalog.', 'facebook-for-woocommerce' ),
330
  'default' => \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_STANDARD,
331
+ 'options' => array(
332
  \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_STANDARD => __( 'Standard description', 'facebook-for-woocommerce' ),
333
  \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT => __( 'Short description', 'facebook-for-woocommerce' ),
334
+ ),
335
+ ),
336
+ array(
337
  'id' => \SkyVerge\WooCommerce\Facebook\Commerce::OPTION_GOOGLE_PRODUCT_CATEGORY_ID,
338
  'type' => 'product_sync_google_product_categories',
339
  'title' => __( 'Default Google product category', 'facebook-for-woocommerce' ),
340
  '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' ),
341
+ ),
342
+ array( 'type' => 'sectionend' ),
343
 
344
+ );
345
  }
346
 
347
  /**
384
  return sprintf(
385
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
386
  __( 'Please %1$sconnect to Facebook%2$s to enable and manage product sync.', 'facebook-for-woocommerce' ),
387
+ '<a href="' . esc_url( facebook_for_woocommerce()->get_connection_handler()->get_connect_url() ) . '">',
388
+ '</a>'
389
  );
390
  }
391
 
includes/Commerce/Orders.php CHANGED
@@ -106,7 +106,7 @@ class Orders {
106
  */
107
  public static function is_commerce_order( \WC_Order $order ) {
108
 
109
- return in_array( $order->get_created_via(), [ 'instagram', 'facebook' ], true );
110
  }
111
 
112
 
@@ -120,12 +120,14 @@ class Orders {
120
  */
121
  public function find_local_order( $remote_id ) {
122
 
123
- $orders = wc_get_orders( [
124
- 'limit' => 1,
125
- 'status' => 'any',
126
- 'meta_key' => self::REMOTE_ID_META_KEY,
127
- 'meta_value' => $remote_id,
128
- ] );
 
 
129
 
130
  return ! empty( $orders ) ? current( $orders ) : null;
131
  }
@@ -158,7 +160,7 @@ class Orders {
158
  *
159
  * @since 2.1.0
160
  *
161
- * @param Order $remote_order Orders API order object
162
  * @param \WC_Order $local_order local order object
163
  * @return \WC_Order
164
  * @throws SV_WC_Plugin_Exception|\WC_Data_Exception
@@ -212,10 +214,12 @@ class Orders {
212
  $matching_wc_order_item->set_subtotal( $item['quantity'] * $item['price_per_unit']['amount'] );
213
  $matching_wc_order_item->set_total( $item['quantity'] * $item['price_per_unit']['amount'] );
214
  // we use the estimated_tax because the captured_tax represents the tax after the order/item has been shipped and we don't fulfill order at the line-item level
215
- $matching_wc_order_item->set_taxes( [
216
- 'subtotal' => [ $item['tax_details']['estimated_tax']['amount'] ],
217
- 'total' => [ $item['tax_details']['estimated_tax']['amount'] ],
218
- ] );
 
 
219
  $matching_wc_order_item->save();
220
 
221
  if ( ! empty( $item['tax_details']['estimated_tax']['amount'] ) ) {
@@ -250,9 +254,11 @@ class Orders {
250
  }
251
 
252
  $matching_shipping_order_item->set_total( $selected_shipping_option['price']['amount'] );
253
- $matching_shipping_order_item->set_taxes( [
254
- 'total' => [ $selected_shipping_option['calculated_tax']['amount'] ],
255
- ] );
 
 
256
  $matching_shipping_order_item->save();
257
 
258
  // add tax item
@@ -399,7 +405,6 @@ class Orders {
399
  } else {
400
  $local_order = $this->update_local_order( $remote_order, $local_order );
401
  }
402
-
403
  } catch ( \Exception $exception ) {
404
 
405
  if ( ! empty( $local_order ) ) {
@@ -422,10 +427,13 @@ class Orders {
422
  $local_order->set_status( 'processing' );
423
 
424
  /* translators: Placeholders: %1$s - order remote id, %2$s - order created by */
425
- $local_order->add_order_note( sprintf( __( 'Order %1$s paid in %2$s', 'facebook-for-woocommerce' ),
426
- $remote_order->get_id(),
427
- ucfirst( $remote_order->get_channel() )
428
- ) );
 
 
 
429
 
430
  } catch ( SV_WC_API_Exception $exception ) {
431
 
@@ -521,11 +529,11 @@ class Orders {
521
  * @since 2.1.0
522
  */
523
  public function schedule_local_orders_update() {
524
- if ( facebook_for_woocommerce()->get_commerce_handler()->is_connected() && false === as_next_scheduled_action( self::ACTION_FETCH_ORDERS, [], \WC_Facebookcommerce::PLUGIN_ID ) ) {
525
 
526
  $interval = $this->get_order_update_interval();
527
 
528
- as_schedule_recurring_action( time() + $interval, $interval, self::ACTION_FETCH_ORDERS, [], \WC_Facebookcommerce::PLUGIN_ID );
529
  }
530
  }
531
 
@@ -538,15 +546,15 @@ class Orders {
538
  public function add_hooks() {
539
 
540
  // schedule a recurring ACTION_FETCH_ORDERS action, if not already scheduled
541
- add_action( 'init', [ $this, 'schedule_local_orders_update' ] );
542
 
543
- add_action( self::ACTION_FETCH_ORDERS, [ $this, 'update_local_orders' ] );
544
 
545
  // prevent sending emails for Commerce orders
546
- add_action( 'woocommerce_email_enabled_customer_completed_order', [ $this, 'maybe_stop_order_email' ], 10, 2 );
547
- add_action( 'woocommerce_email_enabled_customer_processing_order', [ $this, 'maybe_stop_order_email' ], 10, 2 );
548
- add_action( 'woocommerce_email_enabled_customer_refunded_order', [ $this, 'maybe_stop_order_email' ], 10, 2 );
549
- add_action( 'woocommerce_email_enabled_customer_partially_refunded_order', [ $this, 'maybe_stop_order_email' ], 10, 2 );
550
  }
551
 
552
 
@@ -562,8 +570,8 @@ class Orders {
562
  * @since 2.1.0
563
  *
564
  * @param \WC_Order $order order object
565
- * @param string $tracking_number shipping tracking number
566
- * @param string $carrier shipping carrier
567
  * @throws SV_WC_Plugin_Exception
568
  */
569
  public function fulfill_order( \WC_Order $order, $tracking_number, $carrier ) {
@@ -583,17 +591,17 @@ class Orders {
583
  throw new SV_WC_Plugin_Exception( sprintf( __( '%s is not a valid shipping carrier code.', 'facebook-for-woocommerce' ), $carrier ) );
584
  }
585
 
586
- $items = [];
587
 
588
  /** @var \WC_Order_Item_Product $item */
589
  foreach ( $order->get_items() as $item ) {
590
 
591
  if ( $product = $item->get_product() ) {
592
 
593
- $items[] = [
594
  'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
595
  'quantity' => $item->get_quantity(),
596
- ];
597
  }
598
  }
599
 
@@ -601,32 +609,36 @@ class Orders {
601
  throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
602
  }
603
 
604
- $fulfillment_data = [
605
  'items' => $items,
606
- 'tracking_info' => [
607
  'carrier' => $carrier,
608
  'tracking_number' => $tracking_number,
609
- ],
610
- ];
611
 
612
  $plugin = facebook_for_woocommerce();
613
 
614
  $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() )->fulfill_order( $remote_id, $fulfillment_data );
615
 
616
- $order->add_order_note( sprintf(
 
617
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
618
- __( '%s order fulfilled.', 'facebook-for-woocommerce' ),
619
- ucfirst( $order->get_created_via() )
620
- ) );
 
621
 
622
  } catch ( SV_WC_Plugin_Exception $exception ) {
623
 
624
- $order->add_order_note( sprintf(
 
625
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
626
- __( '%1$s order could not be fulfilled. %2$s', 'facebook-for-woocommerce' ),
627
- ucfirst( $order->get_created_via() ),
628
- $exception->getMessage()
629
- ) );
 
630
 
631
  throw $exception;
632
  }
@@ -639,7 +651,7 @@ class Orders {
639
  * @since 2.1.0
640
  *
641
  * @param \WC_Order_Refund $refund order refund object
642
- * @param string $reason_code refund reason code
643
  * @throws SV_WC_Plugin_Exception
644
  */
645
  public function add_order_refund( \WC_Order_Refund $refund, $reason_code ) {
@@ -648,14 +660,14 @@ class Orders {
648
 
649
  $api = $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() );
650
 
651
- $valid_reason_codes = [
652
  self::REFUND_REASON_BUYERS_REMORSE,
653
  self::REFUND_REASON_DAMAGED_GOODS,
654
  self::REFUND_REASON_NOT_AS_DESCRIBED,
655
  self::REFUND_REASON_QUALITY_ISSUE,
656
  self::REFUND_REASON_OTHER,
657
  self::REFUND_REASON_WRONG_ITEM,
658
- ];
659
 
660
  if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
661
  $reason_code = self::REFUND_REASON_OTHER;
@@ -675,9 +687,9 @@ class Orders {
675
  throw new SV_WC_Plugin_Exception( __( 'Remote ID for parent order not found.', 'facebook-for-woocommerce' ) );
676
  }
677
 
678
- $refund_data = [
679
  'reason_code' => $reason_code,
680
- ];
681
 
682
  if ( ! empty( $reason_text = $refund->get_reason() ) ) {
683
  $refund_data['reason_text'] = $reason_text;
@@ -690,32 +702,36 @@ class Orders {
690
 
691
  if ( ! empty( $refund->get_shipping_total() ) ) {
692
 
693
- $refund_data['shipping'] = [
694
- 'shipping_refund' => [
695
  'amount' => abs( $refund->get_shipping_total() ),
696
  'currency' => $refund->get_currency(),
697
- ],
698
- ];
699
  }
700
 
701
  $api->add_order_refund( $remote_id, $refund_data );
702
 
703
- $parent_order->add_order_note( sprintf(
 
704
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
705
- __( 'Order refunded on %s.', 'facebook-for-woocommerce' ),
706
- ucfirst( $parent_order->get_created_via() )
707
- ) );
 
708
 
709
  } catch ( SV_WC_Plugin_Exception $exception ) {
710
 
711
  if ( ! empty( $parent_order ) && $parent_order instanceof \WC_Order ) {
712
 
713
- $parent_order->add_order_note( sprintf(
 
714
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
715
- __( 'Could not refund %1$s order: %2$s', 'facebook-for-woocommerce' ),
716
- ucfirst( $parent_order->get_created_via() ),
717
- $exception->getMessage()
718
- ) );
 
719
 
720
  } else {
721
 
@@ -739,16 +755,16 @@ class Orders {
739
  */
740
  private function get_refund_items( \WC_Order_Refund $refund ) {
741
 
742
- $items = [];
743
 
744
  /** @var \WC_Order_Item_Product $item */
745
  foreach ( $refund->get_items() as $item ) {
746
 
747
  if ( $product = $item->get_product() ) {
748
 
749
- $refund_item = [
750
  'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
751
- ];
752
 
753
  if ( ! empty( $item->get_quantity() ) ) {
754
 
@@ -756,10 +772,10 @@ class Orders {
756
 
757
  } else {
758
 
759
- $refund_item['item_refund_amount'] = [
760
  'amount' => abs( $item->get_total() ),
761
  'currency' => $refund->get_currency(),
762
- ];
763
  }
764
 
765
  $items[] = $refund_item;
@@ -780,7 +796,7 @@ class Orders {
780
  * @since 2.1.0
781
  *
782
  * @param \WC_Order $order order object
783
- * @param string $reason_code cancellation reason code
784
  * @throws SV_WC_Plugin_Exception
785
  */
786
  public function cancel_order( \WC_Order $order, $reason_code ) {
@@ -792,7 +808,7 @@ class Orders {
792
  $valid_reason_codes = array_keys( $this->get_cancellation_reasons() );
793
 
794
  if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
795
- $reason_code = Orders::CANCEL_REASON_OTHER;
796
  }
797
 
798
  try {
@@ -805,20 +821,24 @@ class Orders {
805
 
806
  $api->cancel_order( $remote_id, $reason_code );
807
 
808
- $order->add_order_note( sprintf(
 
809
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
810
- __( '%s order cancelled.', 'facebook-for-woocommerce' ),
811
- ucfirst( $order->get_created_via() )
812
- ) );
 
813
 
814
  } catch ( SV_WC_Plugin_Exception $exception ) {
815
 
816
- $order->add_order_note( sprintf(
 
817
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
818
- __( '%1$s order could not be cancelled. %2$s', 'facebook-for-woocommerce' ),
819
- ucfirst( $order->get_created_via() ),
820
- $exception->getMessage()
821
- ) );
 
822
 
823
  throw $exception;
824
  }
@@ -834,14 +854,14 @@ class Orders {
834
  */
835
  public function get_cancellation_reasons() {
836
 
837
- return [
838
 
839
  self::CANCEL_REASON_CUSTOMER_REQUESTED => __( 'Customer requested cancellation', 'facebook-for-woocommerce' ),
840
  self::CANCEL_REASON_OUT_OF_STOCK => __( 'Product(s) are out of stock', 'facebook-for-woocommerce' ),
841
  self::CANCEL_REASON_INVALID_ADDRESS => __( 'Customer address is invalid', 'facebook-for-woocommerce' ),
842
  self::CANCEL_REASON_SUSPICIOUS_ORDER => __( 'Suspicious order', 'facebook-for-woocommerce' ),
843
  self::CANCEL_REASON_OTHER => __( 'Other', 'facebook-for-woocommerce' ),
844
- ];
845
  }
846
 
847
 
@@ -852,7 +872,7 @@ class Orders {
852
  *
853
  * @since 2.1.0
854
  *
855
- * @param bool $is_enabled whether the email is enabled in the first place
856
  * @param \WC_Order $order order object
857
  * @return bool
858
  */
@@ -877,7 +897,6 @@ class Orders {
877
  * @param bool $is_enabled whether the email is enabled
878
  * @param \WC_Order $order order object
879
  * @param Orders $this admin orders instance
880
- *
881
  */
882
  $is_enabled = (bool) apply_filters( 'wc_facebook_commerce_send_woocommerce_emails', $is_enabled, $order, $this );
883
  }
106
  */
107
  public static function is_commerce_order( \WC_Order $order ) {
108
 
109
+ return in_array( $order->get_created_via(), array( 'instagram', 'facebook' ), true );
110
  }
111
 
112
 
120
  */
121
  public function find_local_order( $remote_id ) {
122
 
123
+ $orders = wc_get_orders(
124
+ array(
125
+ 'limit' => 1,
126
+ 'status' => 'any',
127
+ 'meta_key' => self::REMOTE_ID_META_KEY,
128
+ 'meta_value' => $remote_id,
129
+ )
130
+ );
131
 
132
  return ! empty( $orders ) ? current( $orders ) : null;
133
  }
160
  *
161
  * @since 2.1.0
162
  *
163
+ * @param Order $remote_order Orders API order object
164
  * @param \WC_Order $local_order local order object
165
  * @return \WC_Order
166
  * @throws SV_WC_Plugin_Exception|\WC_Data_Exception
214
  $matching_wc_order_item->set_subtotal( $item['quantity'] * $item['price_per_unit']['amount'] );
215
  $matching_wc_order_item->set_total( $item['quantity'] * $item['price_per_unit']['amount'] );
216
  // we use the estimated_tax because the captured_tax represents the tax after the order/item has been shipped and we don't fulfill order at the line-item level
217
+ $matching_wc_order_item->set_taxes(
218
+ array(
219
+ 'subtotal' => array( $item['tax_details']['estimated_tax']['amount'] ),
220
+ 'total' => array( $item['tax_details']['estimated_tax']['amount'] ),
221
+ )
222
+ );
223
  $matching_wc_order_item->save();
224
 
225
  if ( ! empty( $item['tax_details']['estimated_tax']['amount'] ) ) {
254
  }
255
 
256
  $matching_shipping_order_item->set_total( $selected_shipping_option['price']['amount'] );
257
+ $matching_shipping_order_item->set_taxes(
258
+ array(
259
+ 'total' => array( $selected_shipping_option['calculated_tax']['amount'] ),
260
+ )
261
+ );
262
  $matching_shipping_order_item->save();
263
 
264
  // add tax item
405
  } else {
406
  $local_order = $this->update_local_order( $remote_order, $local_order );
407
  }
 
408
  } catch ( \Exception $exception ) {
409
 
410
  if ( ! empty( $local_order ) ) {
427
  $local_order->set_status( 'processing' );
428
 
429
  /* translators: Placeholders: %1$s - order remote id, %2$s - order created by */
430
+ $local_order->add_order_note(
431
+ sprintf(
432
+ __( 'Order %1$s paid in %2$s', 'facebook-for-woocommerce' ),
433
+ $remote_order->get_id(),
434
+ ucfirst( $remote_order->get_channel() )
435
+ )
436
+ );
437
 
438
  } catch ( SV_WC_API_Exception $exception ) {
439
 
529
  * @since 2.1.0
530
  */
531
  public function schedule_local_orders_update() {
532
+ if ( facebook_for_woocommerce()->get_commerce_handler()->is_connected() && false === as_next_scheduled_action( self::ACTION_FETCH_ORDERS, array(), \WC_Facebookcommerce::PLUGIN_ID ) ) {
533
 
534
  $interval = $this->get_order_update_interval();
535
 
536
+ as_schedule_recurring_action( time() + $interval, $interval, self::ACTION_FETCH_ORDERS, array(), \WC_Facebookcommerce::PLUGIN_ID );
537
  }
538
  }
539
 
546
  public function add_hooks() {
547
 
548
  // schedule a recurring ACTION_FETCH_ORDERS action, if not already scheduled
549
+ add_action( 'init', array( $this, 'schedule_local_orders_update' ) );
550
 
551
+ add_action( self::ACTION_FETCH_ORDERS, array( $this, 'update_local_orders' ) );
552
 
553
  // prevent sending emails for Commerce orders
554
+ add_action( 'woocommerce_email_enabled_customer_completed_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
555
+ add_action( 'woocommerce_email_enabled_customer_processing_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
556
+ add_action( 'woocommerce_email_enabled_customer_refunded_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
557
+ add_action( 'woocommerce_email_enabled_customer_partially_refunded_order', array( $this, 'maybe_stop_order_email' ), 10, 2 );
558
  }
559
 
560
 
570
  * @since 2.1.0
571
  *
572
  * @param \WC_Order $order order object
573
+ * @param string $tracking_number shipping tracking number
574
+ * @param string $carrier shipping carrier
575
  * @throws SV_WC_Plugin_Exception
576
  */
577
  public function fulfill_order( \WC_Order $order, $tracking_number, $carrier ) {
591
  throw new SV_WC_Plugin_Exception( sprintf( __( '%s is not a valid shipping carrier code.', 'facebook-for-woocommerce' ), $carrier ) );
592
  }
593
 
594
+ $items = array();
595
 
596
  /** @var \WC_Order_Item_Product $item */
597
  foreach ( $order->get_items() as $item ) {
598
 
599
  if ( $product = $item->get_product() ) {
600
 
601
+ $items[] = array(
602
  'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
603
  'quantity' => $item->get_quantity(),
604
+ );
605
  }
606
  }
607
 
609
  throw new SV_WC_Plugin_Exception( __( 'No valid Facebook products were found.', 'facebook-for-woocommerce' ) );
610
  }
611
 
612
+ $fulfillment_data = array(
613
  'items' => $items,
614
+ 'tracking_info' => array(
615
  'carrier' => $carrier,
616
  'tracking_number' => $tracking_number,
617
+ ),
618
+ );
619
 
620
  $plugin = facebook_for_woocommerce();
621
 
622
  $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() )->fulfill_order( $remote_id, $fulfillment_data );
623
 
624
+ $order->add_order_note(
625
+ sprintf(
626
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
627
+ __( '%s order fulfilled.', 'facebook-for-woocommerce' ),
628
+ ucfirst( $order->get_created_via() )
629
+ )
630
+ );
631
 
632
  } catch ( SV_WC_Plugin_Exception $exception ) {
633
 
634
+ $order->add_order_note(
635
+ sprintf(
636
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
637
+ __( '%1$s order could not be fulfilled. %2$s', 'facebook-for-woocommerce' ),
638
+ ucfirst( $order->get_created_via() ),
639
+ $exception->getMessage()
640
+ )
641
+ );
642
 
643
  throw $exception;
644
  }
651
  * @since 2.1.0
652
  *
653
  * @param \WC_Order_Refund $refund order refund object
654
+ * @param string $reason_code refund reason code
655
  * @throws SV_WC_Plugin_Exception
656
  */
657
  public function add_order_refund( \WC_Order_Refund $refund, $reason_code ) {
660
 
661
  $api = $plugin->get_api( $plugin->get_connection_handler()->get_page_access_token() );
662
 
663
+ $valid_reason_codes = array(
664
  self::REFUND_REASON_BUYERS_REMORSE,
665
  self::REFUND_REASON_DAMAGED_GOODS,
666
  self::REFUND_REASON_NOT_AS_DESCRIBED,
667
  self::REFUND_REASON_QUALITY_ISSUE,
668
  self::REFUND_REASON_OTHER,
669
  self::REFUND_REASON_WRONG_ITEM,
670
+ );
671
 
672
  if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
673
  $reason_code = self::REFUND_REASON_OTHER;
687
  throw new SV_WC_Plugin_Exception( __( 'Remote ID for parent order not found.', 'facebook-for-woocommerce' ) );
688
  }
689
 
690
+ $refund_data = array(
691
  'reason_code' => $reason_code,
692
+ );
693
 
694
  if ( ! empty( $reason_text = $refund->get_reason() ) ) {
695
  $refund_data['reason_text'] = $reason_text;
702
 
703
  if ( ! empty( $refund->get_shipping_total() ) ) {
704
 
705
+ $refund_data['shipping'] = array(
706
+ 'shipping_refund' => array(
707
  'amount' => abs( $refund->get_shipping_total() ),
708
  'currency' => $refund->get_currency(),
709
+ ),
710
+ );
711
  }
712
 
713
  $api->add_order_refund( $remote_id, $refund_data );
714
 
715
+ $parent_order->add_order_note(
716
+ sprintf(
717
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
718
+ __( 'Order refunded on %s.', 'facebook-for-woocommerce' ),
719
+ ucfirst( $parent_order->get_created_via() )
720
+ )
721
+ );
722
 
723
  } catch ( SV_WC_Plugin_Exception $exception ) {
724
 
725
  if ( ! empty( $parent_order ) && $parent_order instanceof \WC_Order ) {
726
 
727
+ $parent_order->add_order_note(
728
+ sprintf(
729
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
730
+ __( 'Could not refund %1$s order: %2$s', 'facebook-for-woocommerce' ),
731
+ ucfirst( $parent_order->get_created_via() ),
732
+ $exception->getMessage()
733
+ )
734
+ );
735
 
736
  } else {
737
 
755
  */
756
  private function get_refund_items( \WC_Order_Refund $refund ) {
757
 
758
+ $items = array();
759
 
760
  /** @var \WC_Order_Item_Product $item */
761
  foreach ( $refund->get_items() as $item ) {
762
 
763
  if ( $product = $item->get_product() ) {
764
 
765
+ $refund_item = array(
766
  'retailer_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ),
767
+ );
768
 
769
  if ( ! empty( $item->get_quantity() ) ) {
770
 
772
 
773
  } else {
774
 
775
+ $refund_item['item_refund_amount'] = array(
776
  'amount' => abs( $item->get_total() ),
777
  'currency' => $refund->get_currency(),
778
+ );
779
  }
780
 
781
  $items[] = $refund_item;
796
  * @since 2.1.0
797
  *
798
  * @param \WC_Order $order order object
799
+ * @param string $reason_code cancellation reason code
800
  * @throws SV_WC_Plugin_Exception
801
  */
802
  public function cancel_order( \WC_Order $order, $reason_code ) {
808
  $valid_reason_codes = array_keys( $this->get_cancellation_reasons() );
809
 
810
  if ( ! in_array( $reason_code, $valid_reason_codes, true ) ) {
811
+ $reason_code = self::CANCEL_REASON_OTHER;
812
  }
813
 
814
  try {
821
 
822
  $api->cancel_order( $remote_id, $reason_code );
823
 
824
+ $order->add_order_note(
825
+ sprintf(
826
  /* translators: Placeholder: %s - sales channel name, like Facebook or Instagram */
827
+ __( '%s order cancelled.', 'facebook-for-woocommerce' ),
828
+ ucfirst( $order->get_created_via() )
829
+ )
830
+ );
831
 
832
  } catch ( SV_WC_Plugin_Exception $exception ) {
833
 
834
+ $order->add_order_note(
835
+ sprintf(
836
  /* translators: Placeholders: %1$s - sales channel name, like Facebook or Instagram, %2$s - error message */
837
+ __( '%1$s order could not be cancelled. %2$s', 'facebook-for-woocommerce' ),
838
+ ucfirst( $order->get_created_via() ),
839
+ $exception->getMessage()
840
+ )
841
+ );
842
 
843
  throw $exception;
844
  }
854
  */
855
  public function get_cancellation_reasons() {
856
 
857
+ return array(
858
 
859
  self::CANCEL_REASON_CUSTOMER_REQUESTED => __( 'Customer requested cancellation', 'facebook-for-woocommerce' ),
860
  self::CANCEL_REASON_OUT_OF_STOCK => __( 'Product(s) are out of stock', 'facebook-for-woocommerce' ),
861
  self::CANCEL_REASON_INVALID_ADDRESS => __( 'Customer address is invalid', 'facebook-for-woocommerce' ),
862
  self::CANCEL_REASON_SUSPICIOUS_ORDER => __( 'Suspicious order', 'facebook-for-woocommerce' ),
863
  self::CANCEL_REASON_OTHER => __( 'Other', 'facebook-for-woocommerce' ),
864
+ );
865
  }
866
 
867
 
872
  *
873
  * @since 2.1.0
874
  *
875
+ * @param bool $is_enabled whether the email is enabled in the first place
876
  * @param \WC_Order $order order object
877
  * @return bool
878
  */
897
  * @param bool $is_enabled whether the email is enabled
898
  * @param \WC_Order $order order object
899
  * @param Orders $this admin orders instance
 
900
  */
901
  $is_enabled = (bool) apply_filters( 'wc_facebook_commerce_send_woocommerce_emails', $is_enabled, $order, $this );
902
  }
includes/Events/AAMSettings.php CHANGED
@@ -14,7 +14,6 @@ defined( 'ABSPATH' ) or exit;
14
 
15
  /**
16
  * Base Automatic advanced matching settings object
17
- *
18
  */
19
  class AAMSettings {
20
 
@@ -39,9 +38,9 @@ class AAMSettings {
39
  * @param array $data
40
  */
41
  public function __construct( $data = array() ) {
42
- $this->enable_automatic_matching = isset($data['enableAutomaticMatching']) ? $data['enableAutomaticMatching'] : null;
43
- $this->enabled_automatic_matching_fields = isset($data['enabledAutomaticMatchingFields']) ? $data['enabledAutomaticMatchingFields'] : null;
44
- $this->pixel_id = isset($data['pixelId']) ? $data['pixelId'] : null;
45
  }
46
 
47
 
@@ -53,9 +52,9 @@ class AAMSettings {
53
  * @param string $pixel_id pixel ID
54
  * @return string
55
  */
56
- public static function get_url( $pixel_id ){
57
 
58
- return self::CONNECT_FACEBOOK_DOMAIN.self::SIGNALS_JSON_CONFIG_PATH.'/'.$pixel_id;
59
  }
60
 
61
  /**
@@ -66,17 +65,16 @@ class AAMSettings {
66
  *
67
  * @param string $pixel_id
68
  */
69
- public static function build_from_pixel_id( $pixel_id ){
70
- $url = self::get_url($pixel_id);
71
- $response = wp_remote_get($url);
72
  if ( is_wp_error( $response ) ) {
73
  return null;
74
- }
75
- else{
76
  $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
77
- if (!array_key_exists('errorMessage', $response_body)){
78
  $response_body['matchingConfig']['pixelId'] = $pixel_id;
79
- return new AAMSettings($response_body['matchingConfig']);
80
  }
81
  }
82
  return null;
@@ -89,7 +87,7 @@ class AAMSettings {
89
  *
90
  * @return bool
91
  */
92
- public function get_enable_automatic_matching(){
93
  return $this->enable_automatic_matching;
94
  }
95
 
@@ -100,7 +98,7 @@ class AAMSettings {
100
  *
101
  * @return string[]
102
  */
103
- public function get_enabled_automatic_matching_fields(){
104
  return $this->enabled_automatic_matching_fields;
105
  }
106
 
@@ -111,7 +109,7 @@ class AAMSettings {
111
  *
112
  * @return string
113
  */
114
- public function get_pixel_id(){
115
  return $this->pixel_id;
116
  }
117
 
@@ -122,7 +120,7 @@ class AAMSettings {
122
  *
123
  * @return AAMSettings
124
  */
125
- public function set_enable_automatic_matching($enable_automatic_matching){
126
  $this->enable_automatic_matching = $enable_automatic_matching;
127
  return $this;
128
  }
@@ -134,7 +132,7 @@ class AAMSettings {
134
  *
135
  * @return AAMSettings
136
  */
137
- public function set_enabled_automatic_matching_fields($enabled_automatic_matching_fields){
138
  $this->enabled_automatic_matching_fields = $enabled_automatic_matching_fields;
139
  return $this;
140
  }
@@ -146,7 +144,7 @@ class AAMSettings {
146
  *
147
  * @return AAMSettings
148
  */
149
- public function set_pixel_id($pixel_id){
150
  $this->pixel_id = $pixel_id;
151
  return $this;
152
  }
@@ -158,12 +156,12 @@ class AAMSettings {
158
  *
159
  * @return string
160
  */
161
- public function __toString(){
162
  return json_encode(
163
  array(
164
- 'enableAutomaticMatching' => $this->enable_automatic_matching,
165
  'enabledAutomaticMatchingFields' => $this->enabled_automatic_matching_fields,
166
- 'pixelId' => $this->pixel_id
167
  )
168
  );
169
  }
14
 
15
  /**
16
  * Base Automatic advanced matching settings object
 
17
  */
18
  class AAMSettings {
19
 
38
  * @param array $data
39
  */
40
  public function __construct( $data = array() ) {
41
+ $this->enable_automatic_matching = isset( $data['enableAutomaticMatching'] ) ? $data['enableAutomaticMatching'] : null;
42
+ $this->enabled_automatic_matching_fields = isset( $data['enabledAutomaticMatchingFields'] ) ? $data['enabledAutomaticMatchingFields'] : null;
43
+ $this->pixel_id = isset( $data['pixelId'] ) ? $data['pixelId'] : null;
44
  }
45
 
46
 
52
  * @param string $pixel_id pixel ID
53
  * @return string
54
  */
55
+ public static function get_url( $pixel_id ) {
56
 
57
+ return self::CONNECT_FACEBOOK_DOMAIN . self::SIGNALS_JSON_CONFIG_PATH . '/' . $pixel_id;
58
  }
59
 
60
  /**
65
  *
66
  * @param string $pixel_id
67
  */
68
+ public static function build_from_pixel_id( $pixel_id ) {
69
+ $url = self::get_url( $pixel_id );
70
+ $response = wp_remote_get( $url );
71
  if ( is_wp_error( $response ) ) {
72
  return null;
73
+ } else {
 
74
  $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
75
+ if ( ! array_key_exists( 'errorMessage', $response_body ) ) {
76
  $response_body['matchingConfig']['pixelId'] = $pixel_id;
77
+ return new AAMSettings( $response_body['matchingConfig'] );
78
  }
79
  }
80
  return null;
87
  *
88
  * @return bool
89
  */
90
+ public function get_enable_automatic_matching() {
91
  return $this->enable_automatic_matching;
92
  }
93
 
98
  *
99
  * @return string[]
100
  */
101
+ public function get_enabled_automatic_matching_fields() {
102
  return $this->enabled_automatic_matching_fields;
103
  }
104
 
109
  *
110
  * @return string
111
  */
112
+ public function get_pixel_id() {
113
  return $this->pixel_id;
114
  }
115
 
120
  *
121
  * @return AAMSettings
122
  */
123
+ public function set_enable_automatic_matching( $enable_automatic_matching ) {
124
  $this->enable_automatic_matching = $enable_automatic_matching;
125
  return $this;
126
  }
132
  *
133
  * @return AAMSettings
134
  */
135
+ public function set_enabled_automatic_matching_fields( $enabled_automatic_matching_fields ) {
136
  $this->enabled_automatic_matching_fields = $enabled_automatic_matching_fields;
137
  return $this;
138
  }
144
  *
145
  * @return AAMSettings
146
  */
147
+ public function set_pixel_id( $pixel_id ) {
148
  $this->pixel_id = $pixel_id;
149
  return $this;
150
  }
156
  *
157
  * @return string
158
  */
159
+ public function __toString() {
160
  return json_encode(
161
  array(
162
+ 'enableAutomaticMatching' => $this->enable_automatic_matching,
163
  'enabledAutomaticMatchingFields' => $this->enabled_automatic_matching_fields,
164
+ 'pixelId' => $this->pixel_id,
165
  )
166
  );
167
  }
includes/Events/Event.php CHANGED
@@ -25,7 +25,7 @@ class Event {
25
  *
26
  * @see https://developers.facebook.com/docs/marketing-api/server-side-api/payload-helper
27
  */
28
- protected $data = [];
29
 
30
 
31
  /**
@@ -39,11 +39,11 @@ class Event {
39
  */
40
  public static function get_version_info() {
41
 
42
- return [
43
  'source' => 'woocommerce',
44
  'version' => WC()->version,
45
  'pluginVersion' => facebook_for_woocommerce()->get_version(),
46
- ];
47
  }
48
 
49
 
@@ -69,7 +69,7 @@ class Event {
69
  *
70
  * @param array $data event data
71
  */
72
- public function __construct( $data = [] ) {
73
 
74
  $this->prepare_data( $data );
75
  }
@@ -87,14 +87,17 @@ class Event {
87
  */
88
  protected function prepare_data( $data ) {
89
 
90
- $this->data = wp_parse_args( $data, [
91
- 'action_source' => 'website',
92
- 'event_time' => time(),
93
- 'event_id' => $this->generate_event_id(),
94
- 'event_source_url' => $this->get_current_url(),
95
- 'custom_data' => [],
96
- 'user_data' => [],
97
- ] );
 
 
 
98
 
99
  $this->prepare_user_data( $this->data['user_data'] );
100
  }
@@ -110,20 +113,23 @@ class Event {
110
  * @param array $data user data
111
  */
112
  protected function prepare_user_data( $data ) {
113
- $this->data['user_data'] = wp_parse_args( $data, [
114
- 'client_ip_address' => $this->get_client_ip(),
115
- 'client_user_agent' => $this->get_client_user_agent(),
116
- 'click_id' => $this->get_click_id(),
117
- 'browser_id' => $this->get_browser_id(),
118
- ] );
 
 
 
119
 
120
  // Country key is not the same in pixel and CAPI events, see:
121
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
122
  // https://developers.facebook.com/docs/marketing-api/conversions-api/parameters
123
- if(array_key_exists('cn', $this->data['user_data'])){
124
- $country = $this->data['user_data']['cn'];
125
  $this->data['user_data']['country'] = $country;
126
- unset($this->data['user_data']['cn']);
127
  }
128
 
129
  $this->data['user_data'] = Normalizer::normalize_array( $this->data['user_data'], false );
@@ -142,11 +148,11 @@ class Event {
142
  *
143
  * @return array
144
  */
145
- protected function hash_pii_data( $user_data ){
146
- $keys_to_hash = ['em', 'fn', 'ln', 'ph', 'ct', 'st', 'zp', 'country', 'external_id'];
147
- foreach( $keys_to_hash as $key ){
148
- if(array_key_exists($key, $user_data)){
149
- $user_data[$key] = hash('sha256', $user_data[$key], false);
150
  }
151
  }
152
  return $user_data;
@@ -174,25 +180,24 @@ class Event {
174
  } catch ( \Exception $e ) {
175
 
176
  // fall back to mt_rand if random_bytes is unavailable
177
- return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
178
-
179
  // 32 bits for "time_low"
180
- mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
181
-
182
  // 16 bits for "time_mid"
183
  mt_rand( 0, 0xffff ),
184
-
185
  // 16 bits for "time_hi_and_version",
186
  // four most significant bits holds version number 4
187
  mt_rand( 0, 0x0fff ) | 0x4000,
188
-
189
  // 16 bits, 8 bits for "clk_seq_hi_res",
190
  // 8 bits for "clk_seq_low",
191
  // two most significant bits holds zero and one for variant DCE1.1
192
  mt_rand( 0, 0x3fff ) | 0x8000,
193
-
194
  // 48 bits for "node"
195
- mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
 
 
196
  );
197
  }
198
  }
@@ -345,7 +350,7 @@ class Event {
345
  */
346
  public function get_user_data() {
347
 
348
- return ! empty( $this->data['user_data'] ) ? $this->data['user_data'] : [];
349
  }
350
 
351
 
@@ -358,7 +363,7 @@ class Event {
358
  */
359
  public function get_custom_data() {
360
 
361
- return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : [];
362
  }
363
 
364
  }
25
  *
26
  * @see https://developers.facebook.com/docs/marketing-api/server-side-api/payload-helper
27
  */
28
+ protected $data = array();
29
 
30
 
31
  /**
39
  */
40
  public static function get_version_info() {
41
 
42
+ return array(
43
  'source' => 'woocommerce',
44
  'version' => WC()->version,
45
  'pluginVersion' => facebook_for_woocommerce()->get_version(),
46
+ );
47
  }
48
 
49
 
69
  *
70
  * @param array $data event data
71
  */
72
+ public function __construct( $data = array() ) {
73
 
74
  $this->prepare_data( $data );
75
  }
87
  */
88
  protected function prepare_data( $data ) {
89
 
90
+ $this->data = wp_parse_args(
91
+ $data,
92
+ array(
93
+ 'action_source' => 'website',
94
+ 'event_time' => time(),
95
+ 'event_id' => $this->generate_event_id(),
96
+ 'event_source_url' => $this->get_current_url(),
97
+ 'custom_data' => array(),
98
+ 'user_data' => array(),
99
+ )
100
+ );
101
 
102
  $this->prepare_user_data( $this->data['user_data'] );
103
  }
113
  * @param array $data user data
114
  */
115
  protected function prepare_user_data( $data ) {
116
+ $this->data['user_data'] = wp_parse_args(
117
+ $data,
118
+ array(
119
+ 'client_ip_address' => $this->get_client_ip(),
120
+ 'client_user_agent' => $this->get_client_user_agent(),
121
+ 'click_id' => $this->get_click_id(),
122
+ 'browser_id' => $this->get_browser_id(),
123
+ )
124
+ );
125
 
126
  // Country key is not the same in pixel and CAPI events, see:
127
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
128
  // https://developers.facebook.com/docs/marketing-api/conversions-api/parameters
129
+ if ( array_key_exists( 'cn', $this->data['user_data'] ) ) {
130
+ $country = $this->data['user_data']['cn'];
131
  $this->data['user_data']['country'] = $country;
132
+ unset( $this->data['user_data']['cn'] );
133
  }
134
 
135
  $this->data['user_data'] = Normalizer::normalize_array( $this->data['user_data'], false );
148
  *
149
  * @return array
150
  */
151
+ protected function hash_pii_data( $user_data ) {
152
+ $keys_to_hash = array( 'em', 'fn', 'ln', 'ph', 'ct', 'st', 'zp', 'country', 'external_id' );
153
+ foreach ( $keys_to_hash as $key ) {
154
+ if ( array_key_exists( $key, $user_data ) ) {
155
+ $user_data[ $key ] = hash( 'sha256', $user_data[ $key ], false );
156
  }
157
  }
158
  return $user_data;
180
  } catch ( \Exception $e ) {
181
 
182
  // fall back to mt_rand if random_bytes is unavailable
183
+ return sprintf(
184
+ '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
185
  // 32 bits for "time_low"
186
+ mt_rand( 0, 0xffff ),
187
+ mt_rand( 0, 0xffff ),
188
  // 16 bits for "time_mid"
189
  mt_rand( 0, 0xffff ),
 
190
  // 16 bits for "time_hi_and_version",
191
  // four most significant bits holds version number 4
192
  mt_rand( 0, 0x0fff ) | 0x4000,
 
193
  // 16 bits, 8 bits for "clk_seq_hi_res",
194
  // 8 bits for "clk_seq_low",
195
  // two most significant bits holds zero and one for variant DCE1.1
196
  mt_rand( 0, 0x3fff ) | 0x8000,
 
197
  // 48 bits for "node"
198
+ mt_rand( 0, 0xffff ),
199
+ mt_rand( 0, 0xffff ),
200
+ mt_rand( 0, 0xffff )
201
  );
202
  }
203
  }
350
  */
351
  public function get_user_data() {
352
 
353
+ return ! empty( $this->data['user_data'] ) ? $this->data['user_data'] : array();
354
  }
355
 
356
 
363
  */
364
  public function get_custom_data() {
365
 
366
+ return ! empty( $this->data['custom_data'] ) ? $this->data['custom_data'] : array();
367
  }
368
 
369
  }
includes/Events/Normalizer.php CHANGED
@@ -16,7 +16,6 @@ defined( 'ABSPATH' ) or exit;
16
 
17
  /**
18
  * Normalizer class.
19
- *
20
  */
21
  class Normalizer {
22
 
@@ -30,42 +29,42 @@ class Normalizer {
30
  * @return string
31
  * @throws InvalidArgumentException
32
  */
33
- public static function normalize($field, $data) {
34
- if ($data == null || strlen($data) == 0) {
35
  return null;
36
  }
37
 
38
- $data = trim(strtolower($data));
39
  $normalized_data = $data;
40
 
41
- switch ($field) {
42
  case 'em':
43
- $normalized_data = Normalizer::normalizeEmail($data);
44
- break;
45
 
46
  case 'ph':
47
- $normalized_data = Normalizer::normalizePhone($data);
48
- break;
49
 
50
  case 'zp':
51
- $normalized_data = Normalizer::normalizeZipCode($data);
52
- break;
53
 
54
  case 'ct':
55
- $normalized_data = Normalizer::normalizeCity($data);
56
- break;
57
 
58
  case 'st':
59
- $normalized_data = Normalizer::normalizeState($data);
60
- break;
61
 
62
  case 'country':
63
- $normalized_data = Normalizer::normalizeCountry($data);
64
- break;
65
 
66
  case 'cn':
67
- $normalized_data = Normalizer::normalizeCountry($data);
68
- break;
69
 
70
  default:
71
  }
@@ -81,23 +80,21 @@ class Normalizer {
81
  * @param string[] array with user data to be normalized
82
  * @return string[]
83
  */
84
- public static function normalize_array($data, $is_pixel_data){
85
  // Country is encoded as cn in Pixel events and country in CAPI events
86
- $keys_to_normalize = ['fn', 'ln', 'em', 'ph', 'zp', 'ct', 'st'];
87
- if($is_pixel_data){
88
  $keys_to_normalize[] = 'cn';
89
- }
90
- else{
91
  $keys_to_normalize[] = 'country';
92
  }
93
- foreach($keys_to_normalize as $key){
94
- if(array_key_exists($key, $data)){
95
- //If the data is invalid, it is erased from the array
96
- try{
97
- $data[$key] = self::normalize($key, $data[$key]);
98
- }
99
- catch(InvalidArgumentException $e){
100
- unset($data[$key]);
101
  }
102
  }
103
  }
@@ -113,12 +110,12 @@ class Normalizer {
113
  * @return string
114
  * @throws InvalidArgumentException
115
  */
116
- private static function normalizeEmail($email) {
117
  // Validates email against RFC 822
118
- $result = filter_var($email, FILTER_SANITIZE_EMAIL);
119
 
120
- if (!filter_var($result, FILTER_VALIDATE_EMAIL)) {
121
- throw new InvalidArgumentException('Invalid email format for the passed email: ' . $email . 'Please check the passed email format.');
122
  }
123
 
124
  return $result;
@@ -132,8 +129,8 @@ class Normalizer {
132
  * @param string $city city name to be normalized.
133
  * @return string
134
  */
135
- private static function normalizeCity($city) {
136
- return trim(preg_replace('/[0-9.\s\-()]/', '', $city));
137
  }
138
 
139
  /**
@@ -144,8 +141,8 @@ class Normalizer {
144
  * @param string $state state name to be normalized.
145
  * @return string
146
  */
147
- private static function normalizeState($state) {
148
- return preg_replace('/[^a-z]/', '', $state);
149
  }
150
 
151
  /**
@@ -157,11 +154,11 @@ class Normalizer {
157
  * @return string
158
  * @throws InvalidArgumentException
159
  */
160
- private static function normalizeCountry($country) {
161
- $result = preg_replace('/[^a-z]/i', '', $country);
162
 
163
- if (strlen($result) != 2) {
164
- throw new InvalidArgumentException('Invalid country format passed(' . $country . '). Country Code should be a two-letter ISO Country Code');
165
  }
166
 
167
  return $result;
@@ -175,12 +172,12 @@ class Normalizer {
175
  * @param string $zip postal code to be normalized.
176
  * @return string
177
  */
178
- private static function normalizeZipCode($zip) {
179
  // Removing the spaces from the zip code. Eg:
180
- $zip = preg_replace('/[ ]/', '', $zip);
181
 
182
  // If the code has more than one part, retain the first part.
183
- $zip = explode('-', $zip)[0];
184
  return $zip;
185
  }
186
 
@@ -192,11 +189,11 @@ class Normalizer {
192
  * @param string $phone phone number to be normalized.
193
  * @return string
194
  */
195
- private static function normalizePhone($phone) {
196
- $result = trim(preg_replace('/[a-z()-]/', '', $phone));
197
 
198
- if (Normalizer::isInternationalNumber($result)) {
199
- $result = preg_replace('/[\-\s+]/', '', $result);
200
  }
201
 
202
  return $result;
@@ -210,20 +207,20 @@ class Normalizer {
210
  * @param string $phone_number Phone number to be normalized.
211
  * @return bool
212
  */
213
- private static function isInternationalNumber($phone_number) {
214
  // Remove spaces and hyphens
215
- $phone_number = preg_replace('/[\-\s]/', '', $phone_number);
216
 
217
  // Strip + and up to 2 leading 0s
218
- $phone_number = preg_replace('/^\+?0{0,2}/', '', $phone_number);
219
 
220
- if (substr($phone_number, 0, 1) === '0') {
221
  return false;
222
  }
223
 
224
  // International Phone number with country calling code.
225
  $international_number_regex = '/^\d{1,4}\(?\d{2,3}\)?\d{4,}$/';
226
 
227
- return preg_match($international_number_regex, $phone_number);
228
  }
229
  }
16
 
17
  /**
18
  * Normalizer class.
 
19
  */
20
  class Normalizer {
21
 
29
  * @return string
30
  * @throws InvalidArgumentException
31
  */
32
+ public static function normalize( $field, $data ) {
33
+ if ( $data == null || strlen( $data ) == 0 ) {
34
  return null;
35
  }
36
 
37
+ $data = trim( strtolower( $data ) );
38
  $normalized_data = $data;
39
 
40
+ switch ( $field ) {
41
  case 'em':
42
+ $normalized_data = self::normalizeEmail( $data );
43
+ break;
44
 
45
  case 'ph':
46
+ $normalized_data = self::normalizePhone( $data );
47
+ break;
48
 
49
  case 'zp':
50
+ $normalized_data = self::normalizeZipCode( $data );
51
+ break;
52
 
53
  case 'ct':
54
+ $normalized_data = self::normalizeCity( $data );
55
+ break;
56
 
57
  case 'st':
58
+ $normalized_data = self::normalizeState( $data );
59
+ break;
60
 
61
  case 'country':
62
+ $normalized_data = self::normalizeCountry( $data );
63
+ break;
64
 
65
  case 'cn':
66
+ $normalized_data = self::normalizeCountry( $data );
67
+ break;
68
 
69
  default:
70
  }
80
  * @param string[] array with user data to be normalized
81
  * @return string[]
82
  */
83
+ public static function normalize_array( $data, $is_pixel_data ) {
84
  // Country is encoded as cn in Pixel events and country in CAPI events
85
+ $keys_to_normalize = array( 'fn', 'ln', 'em', 'ph', 'zp', 'ct', 'st' );
86
+ if ( $is_pixel_data ) {
87
  $keys_to_normalize[] = 'cn';
88
+ } else {
 
89
  $keys_to_normalize[] = 'country';
90
  }
91
+ foreach ( $keys_to_normalize as $key ) {
92
+ if ( array_key_exists( $key, $data ) ) {
93
+ // If the data is invalid, it is erased from the array
94
+ try {
95
+ $data[ $key ] = self::normalize( $key, $data[ $key ] );
96
+ } catch ( InvalidArgumentException $e ) {
97
+ unset( $data[ $key ] );
 
98
  }
99
  }
100
  }
110
  * @return string
111
  * @throws InvalidArgumentException
112
  */
113
+ private static function normalizeEmail( $email ) {
114
  // Validates email against RFC 822
115
+ $result = filter_var( $email, FILTER_SANITIZE_EMAIL );
116
 
117
+ if ( ! filter_var( $result, FILTER_VALIDATE_EMAIL ) ) {
118
+ throw new InvalidArgumentException( 'Invalid email format for the passed email: ' . $email . 'Please check the passed email format.' );
119
  }
120
 
121
  return $result;
129
  * @param string $city city name to be normalized.
130
  * @return string
131
  */
132
+ private static function normalizeCity( $city ) {
133
+ return trim( preg_replace( '/[0-9.\s\-()]/', '', $city ) );
134
  }
135
 
136
  /**
141
  * @param string $state state name to be normalized.
142
  * @return string
143
  */
144
+ private static function normalizeState( $state ) {
145
+ return preg_replace( '/[^a-z]/', '', $state );
146
  }
147
 
148
  /**
154
  * @return string
155
  * @throws InvalidArgumentException
156
  */
157
+ private static function normalizeCountry( $country ) {
158
+ $result = preg_replace( '/[^a-z]/i', '', $country );
159
 
160
+ if ( strlen( $result ) != 2 ) {
161
+ throw new InvalidArgumentException( 'Invalid country format passed(' . $country . '). Country Code should be a two-letter ISO Country Code' );
162
  }
163
 
164
  return $result;
172
  * @param string $zip postal code to be normalized.
173
  * @return string
174
  */
175
+ private static function normalizeZipCode( $zip ) {
176
  // Removing the spaces from the zip code. Eg:
177
+ $zip = preg_replace( '/[ ]/', '', $zip );
178
 
179
  // If the code has more than one part, retain the first part.
180
+ $zip = explode( '-', $zip )[0];
181
  return $zip;
182
  }
183
 
189
  * @param string $phone phone number to be normalized.
190
  * @return string
191
  */
192
+ private static function normalizePhone( $phone ) {
193
+ $result = trim( preg_replace( '/[a-z()-]/', '', $phone ) );
194
 
195
+ if ( self::isInternationalNumber( $result ) ) {
196
+ $result = preg_replace( '/[\-\s+]/', '', $result );
197
  }
198
 
199
  return $result;
207
  * @param string $phone_number Phone number to be normalized.
208
  * @return bool
209
  */
210
+ private static function isInternationalNumber( $phone_number ) {
211
  // Remove spaces and hyphens
212
+ $phone_number = preg_replace( '/[\-\s]/', '', $phone_number );
213
 
214
  // Strip + and up to 2 leading 0s
215
+ $phone_number = preg_replace( '/^\+?0{0,2}/', '', $phone_number );
216
 
217
+ if ( substr( $phone_number, 0, 1 ) === '0' ) {
218
  return false;
219
  }
220
 
221
  // International Phone number with country calling code.
222
  $international_number_regex = '/^\d{1,4}\(?\d{2,3}\)?\d{4,}$/';
223
 
224
+ return preg_match( $international_number_regex, $phone_number );
225
  }
226
  }
includes/Exceptions/ConnectWCAPIException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\API\Exceptions;
4
+
5
+ defined( 'ABSPATH' ) or exit;
6
+
7
+ /**
8
+ * Class Connect_WC_API_Exception.
9
+ * Exception is trown when Connection with FB fails. @see \SkyVerge\WooCommerce\Facebook\Handlers\Connection
10
+ *
11
+ * @package SkyVerge\WooCommerce\Facebook\API\Exceptions
12
+ */
13
+ class Connect_WC_API_Exception extends \Exception {
14
+ }
includes/Handlers/Connection.php CHANGED
@@ -12,6 +12,7 @@ namespace SkyVerge\WooCommerce\Facebook\Handlers;
12
 
13
  use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_API_Exception;
14
  use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Helper;
 
15
 
16
  defined( 'ABSPATH' ) or exit;
17
 
@@ -92,6 +93,8 @@ class Connection {
92
  /** @var \WC_Facebookcommerce */
93
  private $plugin;
94
 
 
 
95
 
96
  /**
97
  * Constructs a new Connection.
@@ -102,15 +105,15 @@ class Connection {
102
 
103
  $this->plugin = $plugin;
104
 
105
- add_action( 'init', [ $this, 'refresh_business_configuration' ] );
106
 
107
- add_action( 'admin_init', [ $this, 'refresh_installation_data' ] );
108
 
109
- add_action( 'woocommerce_api_' . self::ACTION_CONNECT, [ $this, 'handle_connect' ] );
110
 
111
- add_action( 'admin_action_' . self::ACTION_DISCONNECT, [ $this, 'handle_disconnect' ] );
112
 
113
- add_action( 'woocommerce_api_' . self::ACTION_FBE_REDIRECT, [ $this, 'handle_fbe_redirect' ] );
114
 
115
  add_action( 'fbe_webhook', array( $this, 'fbe_install_webhook' ) );
116
 
@@ -159,7 +162,6 @@ class Connection {
159
  $this->get_plugin()->get_api()->update_messenger_configuration( $this->get_external_business_id(), $messenger_configuration );
160
  }
161
  }
162
-
163
  } catch ( SV_WC_API_Exception $exception ) {
164
 
165
  if ( $this->get_plugin()->get_integration()->is_debug_mode_enabled() ) {
@@ -272,20 +274,24 @@ class Connection {
272
  throw new SV_WC_API_Exception( 'Invalid nonce' );
273
  }
274
 
275
- $merchant_access_token = ! empty( $_GET['merchant_access_token'] ) ? sanitize_text_field( $_GET['merchant_access_token'] ) : '';
 
 
 
 
 
 
 
 
276
 
277
  if ( ! $merchant_access_token ) {
278
  throw new SV_WC_API_Exception( 'Access token is missing' );
279
  }
280
 
281
- $system_user_access_token = ! empty( $_GET['system_user_access_token'] ) ? sanitize_text_field( $_GET['system_user_access_token'] ) : '';
282
-
283
  if ( ! $system_user_access_token ) {
284
  throw new SV_WC_API_Exception( 'System User access token is missing' );
285
  }
286
 
287
- $system_user_id = ! empty( $_GET['system_user_id'] ) ? sanitize_text_field( $_GET['system_user_id'] ) : '';
288
-
289
  if ( ! $system_user_id ) {
290
  throw new SV_WC_API_Exception( 'System User ID is missing' );
291
  }
@@ -313,6 +319,11 @@ class Connection {
313
 
314
  facebook_for_woocommerce()->log( sprintf( 'Connection failed: %s', $exception->getMessage() ) );
315
 
 
 
 
 
 
316
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
317
  }
318
 
@@ -405,11 +416,13 @@ class Connection {
405
 
406
  facebook_for_woocommerce()->log( print_r( $body, true ) );
407
 
408
- throw new SV_WC_API_Exception( sprintf(
 
409
  /* translators: Placeholders: %s - API error message */
410
- __( 'Could not retrieve page access data. %s', 'facebook for woocommerce' ),
411
- wp_remote_retrieve_response_message( $response )
412
- ) );
 
413
  }
414
 
415
  $page_access_tokens = wp_list_pluck( $body['data'], 'access_token', 'id' );
@@ -417,11 +430,13 @@ class Connection {
417
  // bail if the user isn't authorized to manage the page
418
  if ( empty( $page_access_tokens[ $page_id ] ) ) {
419
 
420
- throw new SV_WC_API_Exception( sprintf(
 
421
  /* translators: Placeholders: %s - Facebook page ID */
422
- __( 'Page %s not authorized.', 'facebook-for-woocommerce' ),
423
- $page_id
424
- ) );
 
425
  }
426
 
427
  return $page_access_tokens[ $page_id ];
@@ -534,19 +549,25 @@ class Connection {
534
  public function get_commerce_connect_url() {
535
 
536
  // build the site URL to which the user will ultimately return
537
- $site_url = add_query_arg( [
538
- 'wc-api' => self::ACTION_CONNECT_COMMERCE,
539
- 'nonce' => wp_create_nonce( self::ACTION_CONNECT_COMMERCE ),
540
- ], home_url( '/' ) );
 
 
 
541
 
542
  // build the proxy app URL where the user will land after onboarding, to be redirected to the site URL
543
  $redirect_url = add_query_arg( 'site_url', urlencode( $site_url ), 'https://connect.woocommerce.com/auth/facebookcommerce/' );
544
 
545
  // build the final connect URL, direct to Facebook
546
- $connect_url = add_query_arg( [
547
- 'app_id' => $this->get_client_id(), // this endpoint calls the client ID "app ID"
548
- 'redirect_url' => urlencode( $redirect_url ),
549
- ], 'https://www.facebook.com/commerce_manager/onboarding/' );
 
 
 
550
 
551
  /**
552
  * Filters the URL used to connect to Facebook Commerce.
@@ -599,14 +620,14 @@ class Connection {
599
  */
600
  public function get_scopes() {
601
 
602
- $scopes = [
603
  'manage_business_extension',
604
  'catalog_management',
605
  'ads_management',
606
  'ads_read',
607
  'pages_read_engagement', // this scope is needed to enable order management if using the Commerce feature
608
  'instagram_basic',
609
- ];
610
 
611
  /**
612
  * Filters the scopes that will be requested during the connection flow.
@@ -648,7 +669,7 @@ class Connection {
648
  $external_id = sanitize_key( (string) apply_filters( 'wc_facebook_connection_business_id', get_bloginfo( 'name' ) ) );
649
 
650
  if ( empty( $external_id ) ) {
651
- $external_id = sanitize_key( str_replace( [ 'http', 'https', 'www' ], '', get_bloginfo( 'url' ) ) );
652
  }
653
 
654
  $external_id = uniqid( sprintf( '%s-', $external_id ), false );
@@ -829,12 +850,15 @@ class Connection {
829
  */
830
  public function get_redirect_url() {
831
 
832
- $redirect_url = add_query_arg( [
833
- 'wc-api' => self::ACTION_CONNECT,
834
- 'external_business_id' => $this->get_external_business_id(),
835
- 'nonce' => wp_create_nonce( self::ACTION_CONNECT ),
836
- 'type' => self::AUTH_TYPE_STANDARD,
837
- ], home_url( '/' ) );
 
 
 
838
 
839
  /**
840
  * Filters the redirect URL where the user will return to after OAuth.
@@ -871,15 +895,18 @@ class Connection {
871
  *
872
  * @param array $parameters connection parameters
873
  */
874
- return apply_filters( 'wc_facebook_connection_parameters', [
875
- 'client_id' => $this->get_client_id(),
876
- 'redirect_uri' => $this->get_proxy_url(),
877
- 'state' => $state,
878
- 'display' => 'page',
879
- 'response_type' => 'code',
880
- 'scope' => implode( ',', $this->get_scopes() ),
881
- 'extras' => json_encode( $this->get_connect_parameters_extras() ),
882
- ] );
 
 
 
883
  }
884
 
885
 
@@ -894,22 +921,22 @@ class Connection {
894
  */
895
  private function get_connect_parameters_extras() {
896
 
897
- $parameters = [
898
- 'setup' => [
899
  'external_business_id' => $this->get_external_business_id(),
900
  'timezone' => $this->get_timezone_string(),
901
  'currency' => get_woocommerce_currency(),
902
  'business_vertical' => 'ECOMMERCE',
903
  'domain' => home_url(),
904
  'channel' => 'COMMERCE_OFFSITE',
905
- ],
906
- 'business_config' => [
907
- 'business' => [
908
  'name' => $this->get_business_name(),
909
- ],
910
- ],
911
- 'repeat' => false,
912
- ];
913
 
914
  if ( $external_merchant_settings_id = facebook_for_woocommerce()->get_integration()->get_external_merchant_settings_id() ) {
915
  $parameters['setup']['merchant_settings_id'] = $external_merchant_settings_id;
@@ -918,12 +945,12 @@ class Connection {
918
  // if messenger was previously enabled
919
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) {
920
 
921
- $parameters['business_config']['messenger_chat'] = [
922
  'enabled' => true,
923
- 'domains' => [
924
  home_url( '/' ),
925
- ],
926
- ];
927
  }
928
 
929
  return $parameters;
@@ -1368,11 +1395,11 @@ class Connection {
1368
 
1369
  if ( empty( $_REQUEST['success'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1370
 
1371
- $url_params = [
1372
  'store_url' => '',
1373
  'redirect_uri' => rawurlencode( $redirect_uri ),
1374
- 'errors' => [ 'You need to grant access to WooCommerce.' ],
1375
- ];
1376
 
1377
  $redirect_url = add_query_arg(
1378
  $url_params,
12
 
13
  use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_API_Exception;
14
  use SkyVerge\WooCommerce\PluginFramework\v5_10_0\SV_WC_Helper;
15
+ use SkyVerge\WooCommerce\Facebook\API\Exceptions\Connect_WC_API_Exception;
16
 
17
  defined( 'ABSPATH' ) or exit;
18
 
93
  /** @var \WC_Facebookcommerce */
94
  private $plugin;
95
 
96
+ /** @var array */
97
+ protected $proxy_error_messages;
98
 
99
  /**
100
  * Constructs a new Connection.
105
 
106
  $this->plugin = $plugin;
107
 
108
+ add_action( 'init', array( $this, 'refresh_business_configuration' ) );
109
 
110
+ add_action( 'admin_init', array( $this, 'refresh_installation_data' ) );
111
 
112
+ add_action( 'woocommerce_api_' . self::ACTION_CONNECT, array( $this, 'handle_connect' ) );
113
 
114
+ add_action( 'admin_action_' . self::ACTION_DISCONNECT, array( $this, 'handle_disconnect' ) );
115
 
116
+ add_action( 'woocommerce_api_' . self::ACTION_FBE_REDIRECT, array( $this, 'handle_fbe_redirect' ) );
117
 
118
  add_action( 'fbe_webhook', array( $this, 'fbe_install_webhook' ) );
119
 
162
  $this->get_plugin()->get_api()->update_messenger_configuration( $this->get_external_business_id(), $messenger_configuration );
163
  }
164
  }
 
165
  } catch ( SV_WC_API_Exception $exception ) {
166
 
167
  if ( $this->get_plugin()->get_integration()->is_debug_mode_enabled() ) {
274
  throw new SV_WC_API_Exception( 'Invalid nonce' );
275
  }
276
 
277
+ $is_error = ! empty( $_GET['err'] ) ? true : false;
278
+ $error_code = ! empty( $_GET['err_code'] ) ? sanitize_text_field( $_GET['err_code'] ) : '';
279
+ $merchant_access_token = ! empty( $_GET['merchant_access_token'] ) ? sanitize_text_field( $_GET['merchant_access_token'] ) : '';
280
+ $system_user_access_token = ! empty( $_GET['system_user_access_token'] ) ? sanitize_text_field( $_GET['system_user_access_token'] ) : '';
281
+ $system_user_id = ! empty( $_GET['system_user_id'] ) ? sanitize_text_field( $_GET['system_user_id'] ) : '';
282
+
283
+ if ( $is_error && $error_code ) {
284
+ throw new Connect_WC_API_Exception( $error_code );
285
+ }
286
 
287
  if ( ! $merchant_access_token ) {
288
  throw new SV_WC_API_Exception( 'Access token is missing' );
289
  }
290
 
 
 
291
  if ( ! $system_user_access_token ) {
292
  throw new SV_WC_API_Exception( 'System User access token is missing' );
293
  }
294
 
 
 
295
  if ( ! $system_user_id ) {
296
  throw new SV_WC_API_Exception( 'System User ID is missing' );
297
  }
319
 
320
  facebook_for_woocommerce()->log( sprintf( 'Connection failed: %s', $exception->getMessage() ) );
321
 
322
+ set_transient( 'wc_facebook_connection_failed', time(), 30 );
323
+ } catch ( Connect_WC_API_Exception $exception ) {
324
+
325
+ facebook_for_woocommerce()->log( sprintf( 'Failed to connect to Facebook. Facebook API returned error code: %s', $exception->getMessage() ), 'facebook_for_woocommerce_connect' );
326
+
327
  set_transient( 'wc_facebook_connection_failed', time(), 30 );
328
  }
329
 
416
 
417
  facebook_for_woocommerce()->log( print_r( $body, true ) );
418
 
419
+ throw new SV_WC_API_Exception(
420
+ sprintf(
421
  /* translators: Placeholders: %s - API error message */
422
+ __( 'Could not retrieve page access data. %s', 'facebook for woocommerce' ),
423
+ wp_remote_retrieve_response_message( $response )
424
+ )
425
+ );
426
  }
427
 
428
  $page_access_tokens = wp_list_pluck( $body['data'], 'access_token', 'id' );
430
  // bail if the user isn't authorized to manage the page
431
  if ( empty( $page_access_tokens[ $page_id ] ) ) {
432
 
433
+ throw new SV_WC_API_Exception(
434
+ sprintf(
435
  /* translators: Placeholders: %s - Facebook page ID */
436
+ __( 'Page %s not authorized.', 'facebook-for-woocommerce' ),
437
+ $page_id
438
+ )
439
+ );
440
  }
441
 
442
  return $page_access_tokens[ $page_id ];
549
  public function get_commerce_connect_url() {
550
 
551
  // build the site URL to which the user will ultimately return
552
+ $site_url = add_query_arg(
553
+ array(
554
+ 'wc-api' => self::ACTION_CONNECT_COMMERCE,
555
+ 'nonce' => wp_create_nonce( self::ACTION_CONNECT_COMMERCE ),
556
+ ),
557
+ home_url( '/' )
558
+ );
559
 
560
  // build the proxy app URL where the user will land after onboarding, to be redirected to the site URL
561
  $redirect_url = add_query_arg( 'site_url', urlencode( $site_url ), 'https://connect.woocommerce.com/auth/facebookcommerce/' );
562
 
563
  // build the final connect URL, direct to Facebook
564
+ $connect_url = add_query_arg(
565
+ array(
566
+ 'app_id' => $this->get_client_id(), // this endpoint calls the client ID "app ID"
567
+ 'redirect_url' => urlencode( $redirect_url ),
568
+ ),
569
+ 'https://www.facebook.com/commerce_manager/onboarding/'
570
+ );
571
 
572
  /**
573
  * Filters the URL used to connect to Facebook Commerce.
620
  */
621
  public function get_scopes() {
622
 
623
+ $scopes = array(
624
  'manage_business_extension',
625
  'catalog_management',
626
  'ads_management',
627
  'ads_read',
628
  'pages_read_engagement', // this scope is needed to enable order management if using the Commerce feature
629
  'instagram_basic',
630
+ );
631
 
632
  /**
633
  * Filters the scopes that will be requested during the connection flow.
669
  $external_id = sanitize_key( (string) apply_filters( 'wc_facebook_connection_business_id', get_bloginfo( 'name' ) ) );
670
 
671
  if ( empty( $external_id ) ) {
672
+ $external_id = sanitize_key( str_replace( array( 'http', 'https', 'www' ), '', get_bloginfo( 'url' ) ) );
673
  }
674
 
675
  $external_id = uniqid( sprintf( '%s-', $external_id ), false );
850
  */
851
  public function get_redirect_url() {
852
 
853
+ $redirect_url = add_query_arg(
854
+ array(
855
+ 'wc-api' => self::ACTION_CONNECT,
856
+ 'external_business_id' => $this->get_external_business_id(),
857
+ 'nonce' => wp_create_nonce( self::ACTION_CONNECT ),
858
+ 'type' => self::AUTH_TYPE_STANDARD,
859
+ ),
860
+ home_url( '/' )
861
+ );
862
 
863
  /**
864
  * Filters the redirect URL where the user will return to after OAuth.
895
  *
896
  * @param array $parameters connection parameters
897
  */
898
+ return apply_filters(
899
+ 'wc_facebook_connection_parameters',
900
+ array(
901
+ 'client_id' => $this->get_client_id(),
902
+ 'redirect_uri' => $this->get_proxy_url(),
903
+ 'state' => $state,
904
+ 'display' => 'page',
905
+ 'response_type' => 'code',
906
+ 'scope' => implode( ',', $this->get_scopes() ),
907
+ 'extras' => json_encode( $this->get_connect_parameters_extras() ),
908
+ )
909
+ );
910
  }
911
 
912
 
921
  */
922
  private function get_connect_parameters_extras() {
923
 
924
+ $parameters = array(
925
+ 'setup' => array(
926
  'external_business_id' => $this->get_external_business_id(),
927
  'timezone' => $this->get_timezone_string(),
928
  'currency' => get_woocommerce_currency(),
929
  'business_vertical' => 'ECOMMERCE',
930
  'domain' => home_url(),
931
  'channel' => 'COMMERCE_OFFSITE',
932
+ ),
933
+ 'business_config' => array(
934
+ 'business' => array(
935
  'name' => $this->get_business_name(),
936
+ ),
937
+ ),
938
+ 'repeat' => false,
939
+ );
940
 
941
  if ( $external_merchant_settings_id = facebook_for_woocommerce()->get_integration()->get_external_merchant_settings_id() ) {
942
  $parameters['setup']['merchant_settings_id'] = $external_merchant_settings_id;
945
  // if messenger was previously enabled
946
  if ( facebook_for_woocommerce()->get_integration()->is_messenger_enabled() ) {
947
 
948
+ $parameters['business_config']['messenger_chat'] = array(
949
  'enabled' => true,
950
+ 'domains' => array(
951
  home_url( '/' ),
952
+ ),
953
+ );
954
  }
955
 
956
  return $parameters;
1395
 
1396
  if ( empty( $_REQUEST['success'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
1397
 
1398
+ $url_params = array(
1399
  'store_url' => '',
1400
  'redirect_uri' => rawurlencode( $redirect_uri ),
1401
+ 'errors' => array( 'You need to grant access to WooCommerce.' ),
1402
+ );
1403
 
1404
  $redirect_url = add_query_arg(
1405
  $url_params,
includes/Integrations/Bookings.php CHANGED
@@ -27,7 +27,7 @@ class Bookings {
27
  */
28
  public function __construct() {
29
 
30
- add_action( 'init', [ $this, 'add_hooks' ] );
31
  }
32
 
33
 
@@ -38,8 +38,8 @@ class Bookings {
38
  */
39
  public function add_hooks() {
40
 
41
- if ( facebook_for_woocommerce()->is_plugin_active( 'woocommerce-bookings.php') ) {
42
- add_filter( 'wc_facebook_product_price', [ $this, 'get_product_price' ], 10, 3 );
43
  }
44
  }
45
 
@@ -51,8 +51,8 @@ class Bookings {
51
  *
52
  * @since 2.0.0-dev.3
53
  *
54
- * @param int $price product price in cents
55
- * @param float $facebook_price user defined facebook price
56
  * @param \WC_Product $product product object
57
  * @return int
58
  */
@@ -61,9 +61,9 @@ class Bookings {
61
  if ( ! $facebook_price && $product instanceof \WC_Product && $this->is_bookable_product( $product ) ) {
62
 
63
  $product = new \WC_Product_Booking( $product );
64
- $display_cost = is_callable( [ $product, 'get_display_cost' ] ) ? $product->get_display_cost() : 0;
65
 
66
- $price = (int) round( wc_get_price_to_display( $product, [ 'price' => $display_cost ] ) * 100 );
67
  }
68
 
69
  return $price;
27
  */
28
  public function __construct() {
29
 
30
+ add_action( 'init', array( $this, 'add_hooks' ) );
31
  }
32
 
33
 
38
  */
39
  public function add_hooks() {
40
 
41
+ if ( facebook_for_woocommerce()->is_plugin_active( 'woocommerce-bookings.php' ) ) {
42
+ add_filter( 'wc_facebook_product_price', array( $this, 'get_product_price' ), 10, 3 );
43
  }
44
  }
45
 
51
  *
52
  * @since 2.0.0-dev.3
53
  *
54
+ * @param int $price product price in cents
55
+ * @param float $facebook_price user defined facebook price
56
  * @param \WC_Product $product product object
57
  * @return int
58
  */
61
  if ( ! $facebook_price && $product instanceof \WC_Product && $this->is_bookable_product( $product ) ) {
62
 
63
  $product = new \WC_Product_Booking( $product );
64
+ $display_cost = is_callable( array( $product, 'get_display_cost' ) ) ? $product->get_display_cost() : 0;
65
 
66
+ $price = (int) round( wc_get_price_to_display( $product, array( 'price' => $display_cost ) ) * 100 );
67
  }
68
 
69
  return $price;
includes/Integrations/Integrations.php CHANGED
@@ -51,10 +51,10 @@ class Integrations {
51
  */
52
  private function load_integrations() {
53
 
54
- $registered_integrations = [
55
  'WC_Facebook_WPML_Injector' => '/includes/fbwpml.php',
56
  Bookings::class => '/includes/Integrations/Bookings.php',
57
- ];
58
 
59
  foreach ( $registered_integrations as $class_name => $path ) {
60
 
51
  */
52
  private function load_integrations() {
53
 
54
+ $registered_integrations = array(
55
  'WC_Facebook_WPML_Injector' => '/includes/fbwpml.php',
56
  Bookings::class => '/includes/Integrations/Bookings.php',
57
+ );
58
 
59
  foreach ( $registered_integrations as $class_name => $path ) {
60
 
includes/Jobs/AbstractChainedJob.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\Jobs;
4
+
5
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\AbstractChainedJob as FrameworkAbstractChainedJob;
6
+ use Exception;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Class AbstractChainedJob
12
+ *
13
+ * @since 2.5.0
14
+ */
15
+ abstract class AbstractChainedJob extends FrameworkAbstractChainedJob {
16
+
17
+ /**
18
+ * Handle processing a chain batch.
19
+ *
20
+ * @hooked {plugin_name}/jobs/{job_name}/chain_batch
21
+ *
22
+ * @param int $batch_number The batch number for the new batch.
23
+ * @param array $args The args for the job.
24
+ *
25
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
26
+ */
27
+ public function handle_batch_action( int $batch_number, array $args ) {
28
+ // Use the profile logger to log the usage of each job batch
29
+ $logger = facebook_for_woocommerce()->get_profiling_logger();
30
+ $process_name = $this->get_name() . '_job';
31
+
32
+ $logger->start( $process_name );
33
+
34
+ parent::handle_batch_action( $batch_number, $args );
35
+
36
+ $logger->stop( $process_name );
37
+ }
38
+
39
+ }
includes/Jobs/GenerateProductFeed.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\Jobs;
4
+
5
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities\BatchQueryOffset;
6
+ use Exception;
7
+ use WC_Facebookcommerce;
8
+ use WC_Product;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Class GenerateProductFeed
14
+ *
15
+ * @since 2.5.0
16
+ */
17
+ class GenerateProductFeed extends AbstractChainedJob {
18
+
19
+ use BatchQueryOffset, LoggingTrait;
20
+
21
+ /**
22
+ * Called before starting the job.
23
+ */
24
+ protected function handle_start() {
25
+ // Optionally override this method in child class.
26
+ }
27
+
28
+ /**
29
+ * Called after the finishing the job.
30
+ */
31
+ protected function handle_end() {
32
+ // Optionally override this method in child class.
33
+ }
34
+
35
+ /**
36
+ * Get a set of items for the batch.
37
+ *
38
+ * NOTE: when using an OFFSET based query to retrieve items it's recommended to order by the item ID while
39
+ * ASCENDING. This is so that any newly added items will not disrupt the query offset.
40
+ *
41
+ * @param int $batch_number The batch number increments for each new batch in the job cycle.
42
+ * @param array $args The args for the job.
43
+ *
44
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
45
+ */
46
+ protected function get_items_for_batch( int $batch_number, array $args ): array {
47
+ global $wpdb;
48
+
49
+ $product_ids = $wpdb->get_col(
50
+ $wpdb->prepare(
51
+ "SELECT post.ID
52
+ FROM wp_posts as post
53
+ LEFT JOIN wp_posts as parent ON post.post_parent = parent.ID
54
+ WHERE
55
+ ( post.post_type = 'product_variation' AND parent.post_status = 'publish' )
56
+ OR
57
+ ( post.post_type = 'product' AND post.post_status = 'publish' )
58
+ ORDER BY post.ID ASC
59
+ LIMIT %d OFFSET %d",
60
+ $this->get_batch_size(),
61
+ $this->get_query_offset( $batch_number )
62
+ )
63
+ );
64
+
65
+ return array_map( 'intval', $product_ids );
66
+ }
67
+
68
+ /**
69
+ * Filter-like function that runs before items in a batch are processed.
70
+ *
71
+ * For example, this could be useful for pre-fetching full objects.
72
+ *
73
+ * @param array $items
74
+ *
75
+ * @return array
76
+ */
77
+ protected function filter_items_before_processing( array $items ): array {
78
+ // Pre-fetch full product objects.
79
+ // Variable products will be filtered out here since we don't need them for the feed. It's important to not
80
+ // filter out variable products in ::get_items_for_batch() because if a batch only contains variable products
81
+ // the job will end prematurely thinking it has nothing more to process.
82
+ return wc_get_products(
83
+ [
84
+ 'type' => [ 'simple', 'variation' ],
85
+ 'include' => $items,
86
+ 'orderby' => 'none',
87
+ ]
88
+ );
89
+ }
90
+
91
+ /**
92
+ * Process a single item.
93
+ *
94
+ * @param WC_Product $product A single item from the get_items_for_batch() method.
95
+ * @param array $args The args for the job.
96
+ *
97
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
98
+ */
99
+ protected function process_item( $product, array $args ) {
100
+ try {
101
+ if ( ! $product ) {
102
+ throw new Exception( 'Product not found.' );
103
+ }
104
+
105
+ $this->log( $product->get_id() );
106
+
107
+ } catch ( Exception $e ) {
108
+ $this->log(
109
+ sprintf(
110
+ 'Error processing item #%d - %s',
111
+ $product instanceof WC_Product ? $product->get_id() : 0,
112
+ $e->getMessage()
113
+ )
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Get the name/slug of the job.
120
+ *
121
+ * @return string
122
+ */
123
+ public function get_name(): string {
124
+ return 'generate_feed';
125
+ }
126
+
127
+ /**
128
+ * Get the name/slug of the plugin that owns the job.
129
+ *
130
+ * @return string
131
+ */
132
+ public function get_plugin_name(): string {
133
+ return WC_Facebookcommerce::PLUGIN_ID;
134
+ }
135
+
136
+ /**
137
+ * Get the job's batch size.
138
+ *
139
+ * @return int
140
+ */
141
+ protected function get_batch_size(): int {
142
+ return 15;
143
+ }
144
+
145
+ }
includes/Jobs/JobRegistry.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\Jobs;
4
+
5
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies\ActionScheduler;
6
+
7
+ defined( 'ABSPATH' ) || exit;
8
+
9
+ /**
10
+ * Class JobRegistry
11
+ *
12
+ * @since 2.5.0
13
+ */
14
+ class JobRegistry {
15
+
16
+ /**
17
+ * @var GenerateProductFeed
18
+ */
19
+ public $generate_product_feed_job;
20
+
21
+ /**
22
+ * Instantiate and init all jobs for the plugin.
23
+ */
24
+ public function init() {
25
+ $action_scheduler_proxy = new ActionScheduler();
26
+
27
+ $this->generate_product_feed_job = new GenerateProductFeed( $action_scheduler_proxy );
28
+ $this->generate_product_feed_job->init();
29
+ }
30
+
31
+ }
includes/Jobs/LoggingTrait.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\Jobs;
4
+
5
+ use WC_Facebookcommerce;
6
+
7
+ defined( 'ABSPATH' ) || exit;
8
+
9
+ /**
10
+ * Trait LoggingTrait
11
+ *
12
+ * Logging helper trait for jobs.
13
+ *
14
+ * @since 2.5.0
15
+ */
16
+ trait LoggingTrait {
17
+
18
+ /**
19
+ * Get the name/slug of the job.
20
+ *
21
+ * @return string
22
+ */
23
+ abstract public function get_name(): string;
24
+
25
+ /**
26
+ * Write a log entry using the plugin's logger.
27
+ *
28
+ * @param string $message
29
+ */
30
+ protected function log( string $message ) {
31
+ facebook_for_woocommerce()->log(
32
+ $message,
33
+ sprintf( '%s_%s', WC_Facebookcommerce::PLUGIN_ID, $this->get_name() )
34
+ );
35
+ }
36
+
37
+
38
+ }
includes/Lifecycle.php CHANGED
@@ -35,7 +35,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
35
 
36
  parent::__construct( $plugin );
37
 
38
- $this->upgrade_versions = [
39
  '1.10.0',
40
  '1.10.1',
41
  '1.11.0',
@@ -43,7 +43,8 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
43
  '2.0.3',
44
  '2.0.4',
45
  '2.4.0',
46
- ];
 
47
  }
48
 
49
 
@@ -86,7 +87,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
86
  */
87
  private function migrate_1_9_settings() {
88
 
89
- $values = get_option( 'woocommerce_facebookcommerce_settings', [] );
90
 
91
  // preserve legacy values
92
  if ( false === get_option( 'woocommerce_facebookcommerce_legacy_settings' ) ) {
@@ -94,14 +95,14 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
94
  }
95
 
96
  // migrate options from woocommerce_facebookcommerce_settings
97
- $options = [
98
  'fb_api_key' => \WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN,
99
  'fb_product_catalog_id' => \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID,
100
  'fb_external_merchant_settings_id' => \WC_Facebookcommerce_Integration::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID,
101
  'fb_feed_id' => \WC_Facebookcommerce_Integration::OPTION_FEED_ID,
102
  'facebook_jssdk_version' => \WC_Facebookcommerce_Integration::OPTION_JS_SDK_VERSION,
103
  'pixel_install_time' => \WC_Facebookcommerce_Integration::OPTION_PIXEL_INSTALL_TIME,
104
- ];
105
 
106
  foreach ( $options as $old_index => $new_option_name ) {
107
 
@@ -125,10 +126,10 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
125
  }
126
  }
127
 
128
- $new_settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', [] );
129
 
130
  // migrate settings from woocommerce_facebookcommerce_settings
131
- $settings = [
132
  'fb_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
133
  'fb_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
134
  'fb_pixel_use_pii' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_ADVANCED_MATCHING,
@@ -136,7 +137,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
136
  'msger_chat_customization_locale' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE,
137
  'msger_chat_customization_greeting_text_code' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_GREETING,
138
  'msger_chat_customization_theme_color_code' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
139
- ];
140
 
141
  foreach ( $settings as $old_index => $new_index ) {
142
 
@@ -174,7 +175,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
174
  }
175
 
176
  // maybe remove old settings entries
177
- $old_indexes = array_merge( array_keys( $options ), array_keys( $settings ), [ 'fb_settings_heading', 'fb_upload_id', 'upload_end_time' ] );
178
 
179
  foreach ( $old_indexes as $old_index ) {
180
  unset( $new_settings[ $old_index ] );
@@ -212,7 +213,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
212
  */
213
  protected function upgrade_to_1_11_0() {
214
 
215
- $settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', [] );
216
 
217
  // moves the upload ID to a standalone option
218
  if ( ! empty( $settings['fb_upload_id'] ) ) {
@@ -232,7 +233,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
232
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
233
 
234
  // create_job() expects an non-empty array of attributes
235
- $handler->create_job( [ 'created_at' => current_time( 'mysql' ) ] );
236
  $handler->dispatch();
237
  }
238
 
@@ -242,7 +243,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
242
 
243
  if ( is_array( $settings ) ) {
244
 
245
- $settings_map = [
246
  'facebook_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
247
  'facebook_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
248
  'enable_product_sync' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
@@ -254,7 +255,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
254
  'messenger_greeting' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_GREETING,
255
  'messenger_color_hex' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
256
  'enable_debug_mode' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
257
- ];
258
 
259
  foreach ( $settings_map as $old_name => $new_name ) {
260
 
@@ -289,7 +290,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
289
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
290
 
291
  // create_job() expects an non-empty array of attributes
292
- $handler->create_job( [ 'created_at' => current_time( 'mysql' ) ] );
293
  $handler->dispatch();
294
  }
295
  }
@@ -347,4 +348,18 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
347
  delete_transient( 'wc_facebook_google_product_categories' );
348
  }
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  }
35
 
36
  parent::__construct( $plugin );
37
 
38
+ $this->upgrade_versions = array(
39
  '1.10.0',
40
  '1.10.1',
41
  '1.11.0',
43
  '2.0.3',
44
  '2.0.4',
45
  '2.4.0',
46
+ '2.5.0',
47
+ );
48
  }
49
 
50
 
87
  */
88
  private function migrate_1_9_settings() {
89
 
90
+ $values = get_option( 'woocommerce_facebookcommerce_settings', array() );
91
 
92
  // preserve legacy values
93
  if ( false === get_option( 'woocommerce_facebookcommerce_legacy_settings' ) ) {
95
  }
96
 
97
  // migrate options from woocommerce_facebookcommerce_settings
98
+ $options = array(
99
  'fb_api_key' => \WC_Facebookcommerce_Integration::OPTION_PAGE_ACCESS_TOKEN,
100
  'fb_product_catalog_id' => \WC_Facebookcommerce_Integration::OPTION_PRODUCT_CATALOG_ID,
101
  'fb_external_merchant_settings_id' => \WC_Facebookcommerce_Integration::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID,
102
  'fb_feed_id' => \WC_Facebookcommerce_Integration::OPTION_FEED_ID,
103
  'facebook_jssdk_version' => \WC_Facebookcommerce_Integration::OPTION_JS_SDK_VERSION,
104
  'pixel_install_time' => \WC_Facebookcommerce_Integration::OPTION_PIXEL_INSTALL_TIME,
105
+ );
106
 
107
  foreach ( $options as $old_index => $new_option_name ) {
108
 
126
  }
127
  }
128
 
129
+ $new_settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', array() );
130
 
131
  // migrate settings from woocommerce_facebookcommerce_settings
132
+ $settings = array(
133
  'fb_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
134
  'fb_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
135
  'fb_pixel_use_pii' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_ADVANCED_MATCHING,
137
  'msger_chat_customization_locale' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_LOCALE,
138
  'msger_chat_customization_greeting_text_code' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_GREETING,
139
  'msger_chat_customization_theme_color_code' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
140
+ );
141
 
142
  foreach ( $settings as $old_index => $new_index ) {
143
 
175
  }
176
 
177
  // maybe remove old settings entries
178
+ $old_indexes = array_merge( array_keys( $options ), array_keys( $settings ), array( 'fb_settings_heading', 'fb_upload_id', 'upload_end_time' ) );
179
 
180
  foreach ( $old_indexes as $old_index ) {
181
  unset( $new_settings[ $old_index ] );
213
  */
214
  protected function upgrade_to_1_11_0() {
215
 
216
+ $settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', array() );
217
 
218
  // moves the upload ID to a standalone option
219
  if ( ! empty( $settings['fb_upload_id'] ) ) {
233
  if ( $handler = $this->get_plugin()->get_background_handle_virtual_products_variations_instance() ) {
234
 
235
  // create_job() expects an non-empty array of attributes
236
+ $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
237
  $handler->dispatch();
238
  }
239
 
243
 
244
  if ( is_array( $settings ) ) {
245
 
246
+ $settings_map = array(
247
  'facebook_pixel_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PIXEL_ID,
248
  'facebook_page_id' => \WC_Facebookcommerce_Integration::SETTING_FACEBOOK_PAGE_ID,
249
  'enable_product_sync' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_PRODUCT_SYNC,
255
  'messenger_greeting' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_GREETING,
256
  'messenger_color_hex' => \WC_Facebookcommerce_Integration::SETTING_MESSENGER_COLOR_HEX,
257
  'enable_debug_mode' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
258
+ );
259
 
260
  foreach ( $settings_map as $old_name => $new_name ) {
261
 
290
  if ( $handler = $this->get_plugin()->get_background_remove_duplicate_visibility_meta_instance() ) {
291
 
292
  // create_job() expects an non-empty array of attributes
293
+ $handler->create_job( array( 'created_at' => current_time( 'mysql' ) ) );
294
  $handler->dispatch();
295
  }
296
  }
348
  delete_transient( 'wc_facebook_google_product_categories' );
349
  }
350
 
351
+ /**
352
+ * Upgrades to version 2.5.0
353
+ *
354
+ * @since 2.5.0
355
+ */
356
+ protected function upgrade_to_2_5_0() {
357
+ /**
358
+ * Since 2.5.0 the feed generation interval is increased to 24h.
359
+ * Update procedure just needs to remove all current actions.
360
+ * The Feed class will reschedule new generation with proper cadence.
361
+ */
362
+ as_unschedule_all_actions( \SkyVerge\WooCommerce\Facebook\Products\Feed::GENERATE_FEED_ACTION );
363
+ }
364
+
365
  }
includes/Locale.php CHANGED
@@ -25,7 +25,7 @@ class Locale {
25
 
26
 
27
  /** @var string[] an array of supported locale identifiers */
28
- private static $supported_locales = [
29
  'af_ZA',
30
  'ar_AR',
31
  'as_IN',
@@ -128,7 +128,7 @@ class Locale {
128
  'zh_CN',
129
  'zh_HK',
130
  'zh_TW',
131
- ];
132
 
133
 
134
  /**
@@ -143,7 +143,7 @@ class Locale {
143
  */
144
  public static function get_supported_locales() {
145
 
146
- $locales = [];
147
 
148
  if ( class_exists( 'Locale' ) ) {
149
 
@@ -154,10 +154,9 @@ class Locale {
154
  $locales[ $locale ] = ucfirst( $name );
155
  }
156
  }
157
-
158
  } else {
159
 
160
- include_once( ABSPATH . '/wp-admin/includes/translation-install.php' );
161
 
162
  $translations = wp_get_available_translations();
163
 
25
 
26
 
27
  /** @var string[] an array of supported locale identifiers */
28
+ private static $supported_locales = array(
29
  'af_ZA',
30
  'ar_AR',
31
  'as_IN',
128
  'zh_CN',
129
  'zh_HK',
130
  'zh_TW',
131
+ );
132
 
133
 
134
  /**
143
  */
144
  public static function get_supported_locales() {
145
 
146
+ $locales = array();
147
 
148
  if ( class_exists( 'Locale' ) ) {
149
 
154
  $locales[ $locale ] = ucfirst( $name );
155
  }
156
  }
 
157
  } else {
158
 
159
+ include_once ABSPATH . '/wp-admin/includes/translation-install.php';
160
 
161
  $translations = wp_get_available_translations();
162
 
includes/ProductSets/Sync.php CHANGED
@@ -385,8 +385,8 @@ class Sync {
385
  }
386
 
387
  $data = array(
388
- 'name' => $term->name,
389
- 'filter' => wp_json_encode( array( 'or' => $products ) ),
390
  'metadata' => wp_json_encode( array( 'description' => $term->description ) ),
391
  );
392
 
385
  }
386
 
387
  $data = array(
388
+ 'name' => $term->name,
389
+ 'filter' => wp_json_encode( array( 'or' => $products ) ),
390
  'metadata' => wp_json_encode( array( 'description' => $term->description ) ),
391
  );
392
 
includes/Product_Categories.php CHANGED
@@ -41,7 +41,7 @@ class Product_Categories {
41
  *
42
  * @since 2.2.0
43
  *
44
- * @param int $id category ID
45
  * @param string $category_id Google product category ID
46
  */
47
  public static function update_google_product_category_id( $id, $category_id ) {
41
  *
42
  * @since 2.2.0
43
  *
44
+ * @param int $id category ID
45
  * @param string $category_id Google product category ID
46
  */
47
  public static function update_google_product_category_id( $id, $category_id ) {
includes/Products.php CHANGED
@@ -431,11 +431,11 @@ class Products {
431
  $price = get_option( 'woocommerce_tax_display_shop' ) === 'incl' ? $product->get_composite_price_including_tax() : $product->get_composite_price();
432
 
433
  } elseif ( class_exists( 'WC_Product_Bundle' )
434
- && empty( $product->get_regular_price() )
435
- && 'bundle' === $product->get_type() ) {
436
 
437
  // if product is a product bundle with individually priced items, we rely on their pricing
438
- $price = wc_get_price_to_display( $product, [ 'price' => $product->get_bundle_price() ] );
439
 
440
  } else {
441
 
@@ -1132,8 +1132,8 @@ class Products {
1132
  // Check normal product attributes
1133
  foreach ( $product->get_attributes() as $slug => $attribute ) {
1134
  if ( $product->is_type( 'variation' ) ) {
1135
- $attr_name = $slug;
1136
- $attr_val = \WC_Facebookcommerce_Utils::get_variant_option_name( $product_id, 'attribute_' . $slug, $attribute );
1137
  } else {
1138
  $attr_name = $attribute->get_name();
1139
  $attr_val = $product->get_attribute( $slug );
@@ -1202,12 +1202,12 @@ class Products {
1202
  function( $attrs, $attr_key ) use ( $prefix ) {
1203
  return array_merge(
1204
  $attrs,
1205
- [
1206
  str_replace( $prefix, '', $attr_key ) => wc_clean( Framework\SV_WC_Helper::get_posted_value( $attr_key ) ),
1207
- ]
1208
  );
1209
  },
1210
- []
1211
  );
1212
  }
1213
 
431
  $price = get_option( 'woocommerce_tax_display_shop' ) === 'incl' ? $product->get_composite_price_including_tax() : $product->get_composite_price();
432
 
433
  } elseif ( class_exists( 'WC_Product_Bundle' )
434
+ && empty( $product->get_regular_price() )
435
+ && 'bundle' === $product->get_type() ) {
436
 
437
  // if product is a product bundle with individually priced items, we rely on their pricing
438
+ $price = wc_get_price_to_display( $product, array( 'price' => $product->get_bundle_price() ) );
439
 
440
  } else {
441
 
1132
  // Check normal product attributes
1133
  foreach ( $product->get_attributes() as $slug => $attribute ) {
1134
  if ( $product->is_type( 'variation' ) ) {
1135
+ $attr_name = $slug;
1136
+ $attr_val = \WC_Facebookcommerce_Utils::get_variant_option_name( $product_id, 'attribute_' . $slug, $attribute );
1137
  } else {
1138
  $attr_name = $attribute->get_name();
1139
  $attr_val = $product->get_attribute( $slug );
1202
  function( $attrs, $attr_key ) use ( $prefix ) {
1203
  return array_merge(
1204
  $attrs,
1205
+ array(
1206
  str_replace( $prefix, '', $attr_key ) => wc_clean( Framework\SV_WC_Helper::get_posted_value( $attr_key ) ),
1207
+ )
1208
  );
1209
  },
1210
+ array()
1211
  );
1212
  }
1213
 
includes/Products/FBCategories.php CHANGED
@@ -232,7 +232,7 @@ class FBCategories {
232
  if ( $contents ) {
233
  $data = json_decode( $contents, true );
234
  } else {
235
- $data = [];
236
  facebook_for_woocommerce()->log( 'Error reading category attributes JSON data.' );
237
  }
238
  }
@@ -253,7 +253,7 @@ class FBCategories {
253
  if ( $contents ) {
254
  $data = json_decode( $contents, true );
255
  } else {
256
- $data = [];
257
  facebook_for_woocommerce()->log( 'Error reading category attributes fields JSON data.' );
258
  }
259
  }
232
  if ( $contents ) {
233
  $data = json_decode( $contents, true );
234
  } else {
235
+ $data = array();
236
  facebook_for_woocommerce()->log( 'Error reading category attributes JSON data.' );
237
  }
238
  }
253
  if ( $contents ) {
254
  $data = json_decode( $contents, true );
255
  } else {
256
+ $data = array();
257
  facebook_for_woocommerce()->log( 'Error reading category attributes fields JSON data.' );
258
  }
259
  }
includes/Products/Feed.php CHANGED
@@ -54,13 +54,13 @@ class Feed {
54
  private function add_hooks() {
55
 
56
  // schedule the recurring feed generation
57
- add_action( 'init', [ $this, 'schedule_feed_generation' ] );
58
 
59
  // regenerate the product feed
60
- add_action( self::GENERATE_FEED_ACTION, [ $this, 'regenerate_feed' ] );
61
 
62
  // handle the feed data request
63
- add_action( 'woocommerce_api_' . self::REQUEST_FEED_ACTION, [ $this, 'handle_feed_data_request' ] );
64
  }
65
 
66
 
@@ -86,7 +86,7 @@ class Feed {
86
  try {
87
 
88
  // bail early if the feed secret is not included or is not valid
89
- if ( Feed::get_feed_secret() !== Framework\SV_WC_Helper::get_requested_value( 'secret' ) ) {
90
  throw new Framework\SV_WC_Plugin_Exception( 'Invalid feed secret provided.', 401 );
91
  }
92
 
@@ -102,7 +102,7 @@ class Feed {
102
  header( 'Expires: 0' );
103
  header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
104
  header( 'Pragma: public' );
105
- header( 'Content-Length:'. filesize( $file_path ) );
106
 
107
  $file = @fopen( $file_path, 'rb' );
108
 
@@ -123,7 +123,6 @@ class Feed {
123
 
124
  echo $contents;
125
  }
126
-
127
  } catch ( \Exception $exception ) {
128
 
129
  \WC_Facebookcommerce_Utils::log( 'Could not serve product feed. ' . $exception->getMessage() . ' (' . $exception->getCode() . ')' );
@@ -158,11 +157,14 @@ class Feed {
158
  * @since 1.11.0
159
  */
160
  public function schedule_feed_generation() {
161
-
162
- $integration = facebook_for_woocommerce()->get_integration();
163
-
164
- // only schedule if configured
165
- if ( ! $integration || ! $integration->is_configured() || ! $integration->is_product_sync_enabled() ) {
 
 
 
166
  as_unschedule_all_actions( self::GENERATE_FEED_ACTION );
167
  return;
168
  }
@@ -171,13 +173,14 @@ class Feed {
171
  * Filters the frequency with which the product feed data is generated.
172
  *
173
  * @since 1.11.0
 
174
  *
175
  * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes.
176
  */
177
- $interval = apply_filters( 'wc_facebook_feed_generation_interval', MINUTE_IN_SECONDS * 15 );
178
 
179
  if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) {
180
- as_schedule_recurring_action( time(), max( 2, $interval ), self::GENERATE_FEED_ACTION, [], facebook_for_woocommerce()->get_id_dasherized() );
181
  }
182
  }
183
 
@@ -215,10 +218,10 @@ class Feed {
215
  */
216
  public static function get_feed_data_url() {
217
 
218
- $query_args = [
219
  'wc-api' => self::REQUEST_FEED_ACTION,
220
  'secret' => self::get_feed_secret(),
221
- ];
222
 
223
  return add_query_arg( $query_args, home_url( '/' ) );
224
  }
@@ -237,7 +240,7 @@ class Feed {
237
 
238
  $secret = get_option( self::OPTION_FEED_URL_SECRET, '' );
239
 
240
- if ( ! $secret ) {
241
 
242
  $secret = wp_hash( 'products-feed-' . time() );
243
 
54
  private function add_hooks() {
55
 
56
  // schedule the recurring feed generation
57
+ add_action( 'admin_init', array( $this, 'schedule_feed_generation' ) );
58
 
59
  // regenerate the product feed
60
+ add_action( self::GENERATE_FEED_ACTION, array( $this, 'regenerate_feed' ) );
61
 
62
  // handle the feed data request
63
+ add_action( 'woocommerce_api_' . self::REQUEST_FEED_ACTION, array( $this, 'handle_feed_data_request' ) );
64
  }
65
 
66
 
86
  try {
87
 
88
  // bail early if the feed secret is not included or is not valid
89
+ if ( self::get_feed_secret() !== Framework\SV_WC_Helper::get_requested_value( 'secret' ) ) {
90
  throw new Framework\SV_WC_Plugin_Exception( 'Invalid feed secret provided.', 401 );
91
  }
92
 
102
  header( 'Expires: 0' );
103
  header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
104
  header( 'Pragma: public' );
105
+ header( 'Content-Length:' . filesize( $file_path ) );
106
 
107
  $file = @fopen( $file_path, 'rb' );
108
 
123
 
124
  echo $contents;
125
  }
 
126
  } catch ( \Exception $exception ) {
127
 
128
  \WC_Facebookcommerce_Utils::log( 'Could not serve product feed. ' . $exception->getMessage() . ' (' . $exception->getCode() . ')' );
157
  * @since 1.11.0
158
  */
159
  public function schedule_feed_generation() {
160
+ $integration = facebook_for_woocommerce()->get_integration();
161
+ $configured_ok = $integration && $integration->is_configured();
162
+
163
+ // Only schedule feed job if store has not opted out of product sync.
164
+ $store_allows_sync = $configured_ok && $integration->is_product_sync_enabled();
165
+ // Only schedule if has not opted out of feed generation (e.g. large stores).
166
+ $store_allows_feed = $configured_ok && $integration->is_legacy_feed_file_generation_enabled();
167
+ if ( ! $store_allows_sync || ! $store_allows_feed ) {
168
  as_unschedule_all_actions( self::GENERATE_FEED_ACTION );
169
  return;
170
  }
173
  * Filters the frequency with which the product feed data is generated.
174
  *
175
  * @since 1.11.0
176
+ * @since 2.5.0 Feed generation interval increased to 24h.
177
  *
178
  * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes.
179
  */
180
+ $interval = apply_filters( 'wc_facebook_feed_generation_interval', DAY_IN_SECONDS );
181
 
182
  if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) {
183
+ as_schedule_recurring_action( time(), max( 2, $interval ), self::GENERATE_FEED_ACTION, array(), facebook_for_woocommerce()->get_id_dasherized() );
184
  }
185
  }
186
 
218
  */
219
  public static function get_feed_data_url() {
220
 
221
+ $query_args = array(
222
  'wc-api' => self::REQUEST_FEED_ACTION,
223
  'secret' => self::get_feed_secret(),
224
+ );
225
 
226
  return add_query_arg( $query_args, home_url( '/' ) );
227
  }
240
 
241
  $secret = get_option( self::OPTION_FEED_URL_SECRET, '' );
242
 
243
+ if ( ! $secret ) {
244
 
245
  $secret = wp_hash( 'products-feed-' . time() );
246
 
includes/Products/Stock.php CHANGED
@@ -40,8 +40,8 @@ class Stock {
40
  */
41
  private function add_hooks() {
42
 
43
- add_action( 'woocommerce_variation_set_stock', [ $this, 'set_product_stock' ] );
44
- add_action( 'woocommerce_product_set_stock', [ $this, 'set_product_stock' ] );
45
  }
46
 
47
 
@@ -81,12 +81,15 @@ class Stock {
81
 
82
  if ( $product->is_type( 'variable' ) ) {
83
 
84
- return array_filter( array_map( 'wc_get_product', $product->get_children() ), function ( $item ) {
85
- return $item instanceof \WC_Product;
86
- } );
 
 
 
87
  }
88
 
89
- return [ $product ];
90
  }
91
 
92
 
@@ -103,11 +106,11 @@ class Stock {
103
 
104
  if ( Products::product_should_be_deleted( $product ) ) {
105
 
106
- facebook_for_woocommerce()->get_products_sync_handler()->delete_products( [ \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ) ] );
107
  return;
108
  }
109
 
110
- facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( [ $product->get_id() ] );
111
  }
112
 
113
 
40
  */
41
  private function add_hooks() {
42
 
43
+ add_action( 'woocommerce_variation_set_stock', array( $this, 'set_product_stock' ) );
44
+ add_action( 'woocommerce_product_set_stock', array( $this, 'set_product_stock' ) );
45
  }
46
 
47
 
81
 
82
  if ( $product->is_type( 'variable' ) ) {
83
 
84
+ return array_filter(
85
+ array_map( 'wc_get_product', $product->get_children() ),
86
+ function ( $item ) {
87
+ return $item instanceof \WC_Product;
88
+ }
89
+ );
90
  }
91
 
92
+ return array( $product );
93
  }
94
 
95
 
106
 
107
  if ( Products::product_should_be_deleted( $product ) ) {
108
 
109
+ facebook_for_woocommerce()->get_products_sync_handler()->delete_products( array( \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product ) ) );
110
  return;
111
  }
112
 
113
+ facebook_for_woocommerce()->get_products_sync_handler()->create_or_update_products( array( $product->get_id() ) );
114
  }
115
 
116
 
includes/Products/Sync.php CHANGED
@@ -32,7 +32,7 @@ class Sync {
32
  const ACTION_DELETE = 'DELETE';
33
 
34
  /** @var array the array of requests to schedule for sync */
35
- protected $requests = [];
36
 
37
 
38
  /**
@@ -53,11 +53,11 @@ class Sync {
53
  */
54
  public function add_hooks() {
55
 
56
- add_action( 'shutdown', [ $this, 'schedule_sync' ] );
57
 
58
  // stock update actions
59
- add_action( 'woocommerce_product_set_stock', [ $this, 'handle_stock_update' ] );
60
- add_action( 'woocommerce_variation_set_stock', [ $this, 'handle_stock_update' ] );
61
  }
62
 
63
 
@@ -134,7 +134,7 @@ class Sync {
134
  }
135
 
136
  // add the product to the list of products to be updated
137
- $this->create_or_update_products( [ $product->get_id() ] );
138
  }
139
 
140
 
@@ -150,7 +150,7 @@ class Sync {
150
  if ( ! empty( $this->requests ) ) {
151
 
152
  $job_handler = facebook_for_woocommerce()->get_products_sync_background_handler();
153
- $job = $job_handler->create_job( [ 'requests' => $this->requests ] );
154
 
155
  $job_handler->dispatch();
156
 
@@ -182,9 +182,11 @@ class Sync {
182
  */
183
  public static function is_sync_in_progress() {
184
 
185
- $jobs = facebook_for_woocommerce()->get_products_sync_background_handler()->get_jobs( [
186
- 'status' => 'processing',
187
- ] );
 
 
188
 
189
  return ! empty( $jobs );
190
  }
32
  const ACTION_DELETE = 'DELETE';
33
 
34
  /** @var array the array of requests to schedule for sync */
35
+ protected $requests = array();
36
 
37
 
38
  /**
53
  */
54
  public function add_hooks() {
55
 
56
+ add_action( 'shutdown', array( $this, 'schedule_sync' ) );
57
 
58
  // stock update actions
59
+ add_action( 'woocommerce_product_set_stock', array( $this, 'handle_stock_update' ) );
60
+ add_action( 'woocommerce_variation_set_stock', array( $this, 'handle_stock_update' ) );
61
  }
62
 
63
 
134
  }
135
 
136
  // add the product to the list of products to be updated
137
+ $this->create_or_update_products( array( $product->get_id() ) );
138
  }
139
 
140
 
150
  if ( ! empty( $this->requests ) ) {
151
 
152
  $job_handler = facebook_for_woocommerce()->get_products_sync_background_handler();
153
+ $job = $job_handler->create_job( array( 'requests' => $this->requests ) );
154
 
155
  $job_handler->dispatch();
156
 
182
  */
183
  public static function is_sync_in_progress() {
184
 
185
+ $jobs = facebook_for_woocommerce()->get_products_sync_background_handler()->get_jobs(
186
+ array(
187
+ 'status' => 'processing',
188
+ )
189
+ );
190
 
191
  return ! empty( $jobs );
192
  }
includes/Products/Sync/Background.php CHANGED
@@ -40,7 +40,7 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
40
  * @since 2.0.0
41
  *
42
  * @param \stdClass|object $job
43
- * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
44
  * @throws \Exception when job data is incorrect
45
  * @return \stdClass $job
46
  */
@@ -109,23 +109,22 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
109
  * @since 2.0.0
110
  *
111
  * @param \stdClass|object $job
112
- * @param array $data
113
- * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
114
  */
115
  public function process_items( $job, $data, $items_per_batch = null ) {
116
 
117
  $processed = 0;
118
- $requests = [];
119
 
120
  foreach ( $data as $item_id => $method ) {
121
 
122
  try {
123
 
124
- if ( $request = $this->process_item( [ $item_id, $method ], $job ) ) {
125
  $requests[] = $request;
126
  }
127
-
128
- } catch ( Framework\SV_WC_Plugin_Exception $e ) {
129
 
130
  facebook_for_woocommerce()->log( "Background sync error: {$e->getMessage()}" );
131
  }
@@ -155,7 +154,7 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
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 %s: %s' ), $job->id, $e->getMessage() );
159
 
160
  facebook_for_woocommerce()->log( $message );
161
  }
@@ -168,7 +167,7 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
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
@@ -177,7 +176,7 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
177
 
178
  list( $item_id, $method ) = $item;
179
 
180
- if ( ! in_array( $method, [ Sync::ACTION_UPDATE, Sync::ACTION_DELETE ], true ) ) {
181
  throw new Framework\SV_WC_Plugin_Exception( "Invalid sync request method: {$method}." );
182
  }
183
 
@@ -222,16 +221,16 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
222
  // extract the retailer_id
223
  $retailer_id = $product_data['retailer_id'];
224
 
225
- //NB: Changing this to get items_batch to work
226
  // retailer_id cannot be included in the data object
227
  unset( $product_data['retailer_id'] );
228
  $product_data['id'] = $retailer_id;
229
 
230
- $request = [
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.
@@ -283,24 +282,44 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
283
  *
284
  * @since 2.0.0
285
  *
286
- * @param array $data product data
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 = [];
298
-
299
- foreach ($data['custom_data'] as $key => $val) {
300
- $attributes[] = $key . ':' . $val;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  }
302
 
303
- $data['additional_variant_attribute'] = implode(',', $attributes);
304
  unset( $data['custom_data'] );
305
  }
306
 
@@ -337,10 +356,10 @@ class Background extends Framework\SV_WP_Background_Job_Handler {
337
  */
338
  private function process_item_delete( $retailer_id ) {
339
 
340
- $request = [
341
  'data' => array( 'id' => $retailer_id ),
342
  'method' => Sync::ACTION_DELETE,
343
- ];
344
 
345
  /**
346
  * Filters the data that will be included in a DELETE sync request.
40
  * @since 2.0.0
41
  *
42
  * @param \stdClass|object $job
43
+ * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
44
  * @throws \Exception when job data is incorrect
45
  * @return \stdClass $job
46
  */
109
  * @since 2.0.0
110
  *
111
  * @param \stdClass|object $job
112
+ * @param array $data
113
+ * @param int|null $items_per_batch number of items to process in a single request (defaults to null for unlimited)
114
  */
115
  public function process_items( $job, $data, $items_per_batch = null ) {
116
 
117
  $processed = 0;
118
+ $requests = array();
119
 
120
  foreach ( $data as $item_id => $method ) {
121
 
122
  try {
123
 
124
+ if ( $request = $this->process_item( array( $item_id, $method ), $job ) ) {
125
  $requests[] = $request;
126
  }
127
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
 
128
 
129
  facebook_for_woocommerce()->log( "Background sync error: {$e->getMessage()}" );
130
  }
154
 
155
  } catch ( Framework\SV_WC_API_Exception $e ) {
156
 
157
+ $message = sprintf( __( 'There was an error trying sync products using the Catalog Batch API for job %1$s: %2$s' ), $job->id, $e->getMessage() );
158
 
159
  facebook_for_woocommerce()->log( $message );
160
  }
167
  *
168
  * @since 2.0.0
169
  *
170
+ * @param mixed $item
171
  * @param object|\stdClass $job
172
  * @return array|null
173
  * @throws Framework\SV_WC_Plugin_Exception
176
 
177
  list( $item_id, $method ) = $item;
178
 
179
+ if ( ! in_array( $method, array( Sync::ACTION_UPDATE, Sync::ACTION_DELETE ), true ) ) {
180
  throw new Framework\SV_WC_Plugin_Exception( "Invalid sync request method: {$method}." );
181
  }
182
 
221
  // extract the retailer_id
222
  $retailer_id = $product_data['retailer_id'];
223
 
224
+ // NB: Changing this to get items_batch to work
225
  // retailer_id cannot be included in the data object
226
  unset( $product_data['retailer_id'] );
227
  $product_data['id'] = $retailer_id;
228
 
229
+ $request = array(
230
  // 'retailer_id' => $retailer_id,
231
+ 'method' => Sync::ACTION_UPDATE,
232
+ 'data' => $product_data,
233
+ );
234
 
235
  /**
236
  * Filters the data that will be included in a UPDATE sync request.
282
  *
283
  * @since 2.0.0
284
  *
285
+ * @param array $data product data.
286
  * @return array
287
  */
288
  private function normalize_product_data( $data ) {
289
 
290
+ // Allowed values are 'refurbished', 'used', and 'new', but the plugin has always used the latter.
291
  $data['condition'] = 'new';
292
 
293
+ // Attributes other than size, color, pattern, or gender need to be included in the additional_variant_attributes field.
294
  if ( isset( $data['custom_data'] ) && is_array( $data['custom_data'] ) ) {
295
 
296
+ $attributes = array();
297
+
298
+ foreach ( $data['custom_data'] as $key => $val ) {
299
+ /**
300
+ * Filter: facebook_for_woocommerce_variant_attribute_comma_replacement
301
+ *
302
+ * The Facebook API expects a comma-separated list of attributes in `additional_variant_attribute` field.
303
+ * https://developers.facebook.com/docs/marketing-api/catalog/reference/
304
+ * This means that WooCommerce product attributes included in this field should avoid the comma (`,`) character.
305
+ * Facebook for WooCommerce replaces any `,` with a space by default.
306
+ * This filter allows a site to provide a different replacement string.
307
+ *
308
+ * @since 2.5.0
309
+ *
310
+ * @param string $replacement The default replacement string (`,`).
311
+ * @param string $value Attribute value.
312
+ * @return string Return the desired replacement string.
313
+ */
314
+ $attribute_value = str_replace(
315
+ ',',
316
+ apply_filters( 'facebook_for_woocommerce_variant_attribute_comma_replacement', ' ', $val ),
317
+ $val
318
+ );
319
+ $attributes[] = $key . ':' . $attribute_value;
320
  }
321
 
322
+ $data['additional_variant_attribute'] = implode( ',', $attributes );
323
  unset( $data['custom_data'] );
324
  }
325
 
356
  */
357
  private function process_item_delete( $retailer_id ) {
358
 
359
+ $request = array(
360
  'data' => array( 'id' => $retailer_id ),
361
  'method' => Sync::ACTION_DELETE,
362
+ );
363
 
364
  /**
365
  * Filters the data that will be included in a DELETE sync request.
includes/Utilities/Background_Handle_Virtual_Products_Variations.php CHANGED
@@ -46,7 +46,7 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
46
  * @since 2.0.0
47
  *
48
  * @param object $job
49
- * @param int $items_per_batch number of items to process in a single request. Defaults to unlimited.
50
  * @return object
51
  */
52
  public function process_job( $job, $items_per_batch = null ) {
@@ -140,7 +140,7 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
140
  return 0;
141
  }
142
 
143
- $insert = $update = [];
144
 
145
  foreach ( $results as $result ) {
146
 
@@ -202,7 +202,7 @@ class Background_Handle_Virtual_Products_Variations extends Framework\SV_WP_Back
202
  return 0;
203
  }
204
 
205
- $values = [];
206
 
207
  foreach ( $post_ids as $post_id ) {
208
  $values[] = "('{$post_id}', 'fb_visibility', 'no')";
46
  * @since 2.0.0
47
  *
48
  * @param object $job
49
+ * @param int $items_per_batch number of items to process in a single request. Defaults to unlimited.
50
  * @return object
51
  */
52
  public function process_job( $job, $items_per_batch = null ) {
140
  return 0;
141
  }
142
 
143
+ $insert = $update = array();
144
 
145
  foreach ( $results as $result ) {
146
 
202
  return 0;
203
  }
204
 
205
+ $values = array();
206
 
207
  foreach ( $post_ids as $post_id ) {
208
  $values[] = "('{$post_id}', 'fb_visibility', 'no')";
includes/Utilities/Background_Remove_Duplicate_Visibility_Meta.php CHANGED
@@ -48,7 +48,7 @@ class Background_Remove_Duplicate_Visibility_Meta extends Framework\SV_WP_Backgr
48
  * @since 2.0.3
49
  *
50
  * @param object $job
51
- * @param int $items_per_batch number of items to process in a single request. Defaults to unlimited.
52
  * @return object
53
  */
54
  public function process_job( $job, $items_per_batch = null ) {
@@ -56,7 +56,7 @@ class Background_Remove_Duplicate_Visibility_Meta extends Framework\SV_WP_Backgr
56
  // don't do anything until the job used to hide virtual variations is done
57
  $handler = facebook_for_woocommerce()->get_background_handle_virtual_products_variations_instance();
58
 
59
- if ( $handler && $handler->get_jobs( [ 'status' => [ 'processing', 'queued' ] ] ) ) {
60
  return $job;
61
  }
62
 
@@ -142,10 +142,12 @@ class Background_Remove_Duplicate_Visibility_Meta extends Framework\SV_WP_Backgr
142
 
143
  if ( false === $wpdb->query( $wpdb->prepare( $sql, $result->post_id, $result->last_meta_id ) ) ) {
144
 
145
- facebook_for_woocommerce()->log( sprintf(
146
- 'There was an error trying to set products and variations meta data. %s',
147
- $wpdb->last_error
148
- ) );
 
 
149
 
150
  continue;
151
  }
48
  * @since 2.0.3
49
  *
50
  * @param object $job
51
+ * @param int $items_per_batch number of items to process in a single request. Defaults to unlimited.
52
  * @return object
53
  */
54
  public function process_job( $job, $items_per_batch = null ) {
56
  // don't do anything until the job used to hide virtual variations is done
57
  $handler = facebook_for_woocommerce()->get_background_handle_virtual_products_variations_instance();
58
 
59
+ if ( $handler && $handler->get_jobs( array( 'status' => array( 'processing', 'queued' ) ) ) ) {
60
  return $job;
61
  }
62
 
142
 
143
  if ( false === $wpdb->query( $wpdb->prepare( $sql, $result->post_id, $result->last_meta_id ) ) ) {
144
 
145
+ facebook_for_woocommerce()->log(
146
+ sprintf(
147
+ 'There was an error trying to set products and variations meta data. %s',
148
+ $wpdb->last_error
149
+ )
150
+ );
151
 
152
  continue;
153
  }
includes/Utilities/Shipment.php CHANGED
@@ -37,521 +37,521 @@ class Shipment {
37
  /**
38
  * @see https://developers.facebook.com/docs/commerce-platform/order-management/carrier-codes
39
  */
40
- $this->valid_carriers = [
41
- 'AUSTRALIA_POST' => 'Australia Post',
42
- 'CANADA_POST' => 'Canada Post',
43
- 'DHL' => 'DHL',
44
- 'DHL_ECOMMERCE_US' => 'DHL eCommerce US',
45
- 'EAGLE' => 'Eagle',
46
- 'FEDEX' => 'FedEx',
47
- 'FEDEX_UK' => 'FedEx UK',
48
- 'NEW_ZEALAND_POST' => 'New Zealand Post',
49
- 'ONTRAC' => 'OnTrac',
50
- 'POST_DANMARK' => 'Post Danmark',
51
- 'PUROLATOR' => 'Purolator',
52
- 'ROYAL_MAIL' => 'Royal Mail',
53
- 'SPEE_DEE' => 'Spee-Dee',
54
- 'TNT' => 'TNT',
55
- 'TNT_POST' => 'TNT Post',
56
- 'UPS' => 'UPS',
57
- 'USPS' => 'USPS',
58
- 'OTHER' => 'Other tracking number',
59
- 'ABF_FREIGHT' => 'ABF Freight',
60
- 'ABX_EXPRESS' => 'ABX Express',
61
- 'AB_CUSTOM_GROUP' => 'AB Custom Group',
62
- 'ACOMMERCE' => 'aCommerce',
63
- 'ACS_COURIER' => 'ACS Courier',
64
- 'ACS_WORLDWIDE_EXPRESS' => 'ACS Worldwide Express',
65
- 'ADICIONAL_LOGISTICS' => 'Adicional Logistics',
66
- 'ADSONE' => 'ADSOne',
67
- 'AIR21' => 'AIR21',
68
- 'AIRPAK_EXPRESS' => 'Airpak Express',
69
- 'AIRSPEED_INTERNATIONAL_CORPORATION' => 'Airspeed International Corporation',
70
- 'ALFATREX' => 'AlfaTrex',
71
- 'ALLIED_EXPRESS' => 'Allied Express',
72
- 'ALLJOY_SUPPLY_CHAIN_CO_LTD' => 'ALLJOY SUPPLY CHAIN CO., LTD',
73
- 'ALPHAFAST' => 'alphaFAST',
74
- 'AMAZON_FBA_USA' => 'Amazon FBA USA',
75
- 'AMAZON_LOGISTICS' => 'Amazon Logistics',
76
- 'AN_POST' => 'An Post',
77
- 'APC_OVERNIGHT' => 'APC Overnight',
78
- 'APC_OVERNIGHT_REFERENCE' => 'APC Overnight Reference',
79
- 'APC_POSTAL_LOGISTICS' => 'APC Postal Logistics',
80
- 'APRISA_EXPRESS' => 'Aprisa Express',
81
- 'ARAMEX' => 'Aramex',
82
- 'ARROW_XL' => 'Arrow XL',
83
- 'ASENDIA_GERMANY' => 'Asendia Germany',
84
- 'ASENDIA_HK' => 'Asendia HK',
85
- 'ASENDIA_HK_PREMIUM_SERVICE_LATAM' => 'Asendia HK - Premium Service (LATAM)',
86
- 'ASENDIA_UK' => 'Asendia UK',
87
- 'ASENDIA_USA' => 'Asendia USA',
88
- 'ASM' => 'ASM',
89
- 'AUPOST_CHINA' => 'AuPost China',
90
- 'AUSTRALIA_POST_SFTP' => 'Australia Post Sftp',
91
- 'AUSTRIAN_POST_EXPRESS' => 'Austrian Post (Express)',
92
- 'AUSTRIAN_POST_REGISTERED' => 'Austrian Post (Registered)',
93
- 'AXL_EXPRESS_LOGISTICS' => 'AXL Express & Logistics',
94
- 'A_DUIE_PYLE' => 'A Duie Pyle',
95
- 'A_J_EXPRESS' => 'a j express',
96
- 'B2C_EUROPE' => 'B2C Europe',
97
- 'BELPOST' => 'Belpost',
98
- 'BERT_TRANSPORT' => 'Bert Transport',
99
- 'BEST_EXPRESS' => 'Best Express',
100
- 'BEST_WAY_PARCEL' => 'Best Way Parcel',
101
- 'BIRDSYSTEM' => 'BirdSystem',
102
- 'BJS_DISTRIBUTION_STORAGE_COURIERS' => 'BJS Distribution, Storage & Couriers',
103
- 'BJS_DISTRIBUTION_STORAGE_COURIERS_FTP' => 'BJS Distribution, Storage & Couriers - FTP',
104
- 'BLUECARE_EXPRESS_LTD' => 'Bluecare Express Ltd',
105
- 'BLUEDART' => 'Bluedart',
106
- 'BLUE_STAR' => 'Blue Star',
107
- 'BNEED' => 'Bneed',
108
- 'BONDS_COURIERS' => 'Bonds Couriers',
109
- 'BOXC' => 'BoxC',
110
- 'BPOST' => 'Bpost',
111
- 'BPOST_INTERNATIONAL' => 'Bpost international',
112
- 'BRAZIL_CORREIOS' => 'Brazil Correios',
113
- 'BRT_BARTOLINI' => 'BRT Bartolini',
114
- 'BRT_BARTOLINI_PARCEL_ID' => 'BRT Bartolini(Parcel ID)',
115
- 'BULGARIAN_POSTS' => 'Bulgarian Posts',
116
- 'BUYLOGIC' => 'Buylogic',
117
- 'CAMBODIA_POST' => 'Cambodia Post',
118
- 'CANPAR_COURIER' => 'Canpar Courier',
119
- 'CAPITAL_TRANSPORT' => 'Capital Transport',
120
- 'CARRIER_007EX' => '007EX',
121
- 'CARRIER_17_POST_SERVICE' => '17 Post Service',
122
- 'CARRIER_2GO' => '2GO',
123
- 'CARRIER_360_LION_EXPRESS' => '360 Lion Express',
124
- 'CARRIER_4PX' => '4PX',
125
- 'CARRIER_4_72_ENTREGANDO' => '4-72 Entregando',
126
- 'CARRIER_ECHO' => 'Echo',
127
- 'CBL_LOGISTICS' => 'CBL Logistics',
128
- 'CELERITAS_TRANSPORTE_SL' => 'Celeritas Transporte, S.L',
129
- 'CESKA_POSTA' => 'Česká Poš',
130
- 'CHINA_EMS_EPACKET' => 'China EMS (ePacket)',
131
- 'CHINA_POST' => 'China Post',
132
- 'CHIT_CHATS' => 'Chit Chats',
133
- 'CHRONOPOST_FRANCE' => 'Chronopost France',
134
- 'CHRONOPOST_PORTUGAL' => 'Chronopost Portugal',
135
- 'CH_ROBINSON_WORLDWIDE_INC' => 'C.H. Robinson Worldwide, Inc.',
136
- 'CITY_LINK_EXPRESS' => 'City-Link Express',
137
- 'CJ_CENTURY' => 'CJ Century',
138
- 'CJ_CENTURY_INTERNATIONAL' => 'CJ Century (International)',
139
- 'CJ_GLS' => 'CJ GLS',
140
- 'CJ_KOREA_EXPRESS' => 'CJ Korea Express',
141
- 'CJ_LOGISTICS_INTERNATIONAL' => 'CJ Logistics International',
142
- 'CJ_TRANSNATIONAL_PHILIPPINES' => 'CJ Transnational Philippines',
143
- 'CLEVY_LINKS' => 'Clevy Links',
144
- 'CLOUDWISH_ASIA' => 'Cloudwish Asia',
145
- 'CNE_EXPRESS' => 'CNE Express',
146
- 'COLISSIMO' => 'Colissimo',
147
- 'COLIS_PRIVE' => 'Colis Privé',
148
- 'COLLECTCO' => 'CollectCo',
149
- 'COLLECT_PLUS' => 'Collect+',
150
- 'CON_WAY_FREIGHT' => 'Con-way Freight',
151
- 'COPA_AIRLINES_COURIER' => 'Copa Airlines Courier',
152
- 'CORREOS_CHILE' => 'Correos Chile',
153
- 'CORREOS_DE_COSTA_RICA' => 'Correos de Costa Rica',
154
- 'CORREOS_DE_ESPANA' => 'Correos de España',
155
- 'CORREOS_DE_MEXICO' => 'Correos de Mexico',
156
- 'CORREOS_EXPRESS' => 'Correos Express',
157
- 'CORREO_ARGENTINO' => 'Correo Argentino',
158
- 'COSMETICS_NOW' => 'Cosmetics Now',
159
- 'COUREX' => 'Courex',
160
- 'COURIERPOST' => 'CourierPost',
161
- 'COURIERS_PLEASE' => 'Couriers Please',
162
- 'COURIER_IT' => 'Courier IT',
163
- 'COURIER_PLUS' => 'Courier Plus',
164
- 'CPACKET' => 'cPacket',
165
- 'CUCKOO_EXPRESS' => 'Cuckoo Express',
166
- 'CYPRUS_POST' => 'Cyprus Post',
167
- 'DACHSER' => 'DACHSER',
168
- 'DAWN_WING' => 'Dawn Wing',
169
- 'DAYLIGHT_TRANSPORT_LLC' => 'Daylight Transport, LLC',
170
- 'DB_SCHENKER' => 'DB Schenker',
171
- 'DB_SCHENKER_SWEDEN' => 'DB Schenker Sweden',
172
- 'DD_EXPRESS_COURIER' => 'DD Express Courier',
173
- 'DELCART' => 'Delcart',
174
- 'DELHIVERY' => 'Delhivery',
175
- 'DELIVERYONTIME_LOGISTICS_PVT_LTD' => 'DELIVERYONTIME LOGISTICS PVT LTD',
176
- 'DELTEC_COURIER' => 'Deltec Courier',
177
- 'DEMANDSHIP' => 'DemandShip',
178
- 'DETRACK' => 'Detrack',
179
- 'DEUTSCHE_POST_DHL' => 'Deutsche Post DHL',
180
- 'DEUTSCHE_POST_MAIL' => 'Deutsche Post Mail',
181
- 'DEX_I' => 'DEX-I',
182
- 'DHL_2_MANN_HANDLING' => 'DHL 2-Mann-Handling',
183
- 'DHL_ACTIVE_TRACING' => 'DHL Active Tracing',
184
- 'DHL_BENELUX' => 'DHL Benelux',
185
- 'DHL_ECOMMERCE_ASIA' => 'DHL eCommerce Asia',
186
- 'DHL_EXPRESS_PIECE_ID' => 'DHL Express (Piece ID)',
187
- 'DHL_GLOBAL_FORWARDING' => 'DHL Global Forwarding',
188
- 'DHL_HONG_KONG' => 'DHL Hong Kong',
189
- 'DHL_NETHERLANDS' => 'DHL Netherlands',
190
- 'DHL_PARCEL_NL' => 'DHL Parcel NL',
191
- 'DHL_PARCEL_SPAIN' => 'DHL Parcel Spain',
192
- 'DHL_POLAND_DOMESTIC' => 'DHL Poland Domestic',
193
- 'DHL_SPAIN_DOMESTIC' => 'DHL Spain Domestic',
194
- 'DIMERCO_EXPRESS_GROUP' => 'Dimerco Express Group',
195
- 'DIRECTLOG' => 'Directlog',
196
- 'DIRECT_FREIGHT_EXPRESS' => 'Direct Freight Express',
197
- 'DIRECT_LINK' => 'Direct Link',
198
- 'DMM_NETWORK' => 'DMM Network',
199
- 'DOORA_LOGISTICS' => 'Doora Logistics',
200
- 'DOTZOT' => 'Dotzot',
201
- 'DPD' => 'DPD',
202
- 'DPD_FRANCE' => 'DPD France',
203
- 'DPD_GERMANY' => 'DPD Germany',
204
- 'DPD_HK' => 'DPD HK',
205
- 'DPD_IRELAND' => 'DPD Ireland',
206
- 'DPD_LOCAL' => 'DPD Local',
207
- 'DPD_LOCAL_REFERENCE' => 'DPD Local reference',
208
- 'DPD_POLAND' => 'DPD Poland',
209
- 'DPD_ROMANIA' => 'DPD Romania',
210
- 'DPD_RUSSIA' => 'DPD Russia',
211
- 'DPD_UK' => 'DPD UK',
212
- 'DPEX' => 'DPEX',
213
- 'DPEX_CHINA' => 'DPEX China',
214
- 'DPE_EXPRESS' => 'DPE Express',
215
- 'DPE_SOUTH_AFRICA' => 'DPE South Africa',
216
- 'DSV' => 'DSV',
217
- 'DTDC_AUSTRALIA' => 'DTDC Australia',
218
- 'DTDC_EXPRESS_GLOBAL_PTE_LTD' => 'DTDC Express Global PTE LTD',
219
- 'DTDC_INDIA' => 'DTDC India',
220
- 'DX' => 'DX',
221
- 'DX_FREIGHT' => 'DX Freight',
222
- 'DYNALOGIC_BENELUX_BV' => 'Dynalogic Benelux BV',
223
- 'DYNAMIC_LOGISTICS' => 'Dynamic Logistics',
224
- 'EASY_MAIL' => 'Easy Mail',
225
- 'ECARGO' => 'Ecargo',
226
- 'ECMS_INTERNATIONAL_LOGISTICS_CO_LTD' => 'ECMS International Logistics Co., Ltd.',
227
- 'ECOM_EXPRESS' => 'Ecom Express',
228
- 'EC_FIRSTCLASS' => 'EC-Firstclass',
229
- 'EFS_E_COMMERCE_FULFILLMENT_SERVICE' => 'EFS (E-commerce Fulfillment Service)',
230
- 'EKART' => 'Ekart',
231
- 'ELTA_HELLENIC_POST' => 'ELTA Hellenic Post',
232
- 'EMIRATES_POST' => 'Emirates Post',
233
- 'EMPS_EXPRESS' => 'EMPS Express',
234
- 'ENSENDA' => 'Ensenda',
235
- 'ENVIALIA' => 'Envialia',
236
- 'EPARCEL_KOREA' => 'eParcel Korea',
237
- 'EP_BOX' => 'EP-Box',
238
- 'EQUICK_CHINA' => 'Equick China',
239
- 'ESTAFETA' => 'Estafeta',
240
- 'ESTES' => 'Estes',
241
- 'ETOTAL_SOLUTION_LIMITED' => 'eTotal Solution Limited',
242
- 'EURODIS' => 'Eurodis',
243
- 'EXPEDITORS' => 'Expeditors',
244
- 'EZSHIP' => 'EZship',
245
- 'FASTRAK_SERVICES' => 'Fastrak Services',
246
- 'FASTWAY_AUSTRALIA' => 'Fastway Australia',
247
- 'FASTWAY_IRELAND' => 'Fastway Ireland',
248
- 'FASTWAY_NEW_ZEALAND' => 'Fastway New Zealand',
249
- 'FASTWAY_SOUTH_AFRICA' => 'Fastway South Africa',
250
- 'FEDEX_CROSS_BORDER' => 'Fedex Cross Border',
251
- 'FEDEX_FREIGHT' => 'FedEx Freight',
252
- 'FEDEX_POLAND_DOMESTIC' => 'FedEx Poland Domestic',
253
- 'FERCAM_LOGISTICS_TRANSPORT' => 'FERCAM Logistics & Transport',
254
- 'FIRST_FLIGHT_COURIERS' => 'First Flight Couriers',
255
- 'FIRST_LOGISTICS' => 'First Logistics',
256
- 'FLYT_EXPRESS' => 'Flyt Express',
257
- 'GATI_KWE' => 'Gati-KWE',
258
- 'GDEX' => 'GDEX',
259
- 'GENIKI_TAXYDROMIKI' => 'Geniki Taxydromiki',
260
- 'GEODIS_E_SPACE' => 'Geodis E-space',
261
- 'GEODIS_DISTRIBUTION_EXPRESS' => 'GEODIS - Distribution & Express',
262
- 'GIAO_HANG_NHANH' => 'Giao hàng nhanh',
263
- 'GLOBEGISTICS_INC' => 'Globegistics Inc.',
264
- 'GLS' => 'GLS',
265
- 'GLS_CZECH_REPUBLIC' => 'GLS Czech Republic',
266
- 'GLS_ITALY' => 'GLS Italy',
267
- 'GLS_NETHERLANDS' => 'GLS Netherlands',
268
- 'GOFLY' => 'GoFly',
269
- 'GOJAVAS' => 'GoJavas',
270
- 'GREYHOUND' => 'Greyhound',
271
- 'GSI_EXPRESS' => 'GSI EXPRESS',
272
- 'HERMESWORLD' => 'Hermesworld',
273
- 'HERMES_GERMANY' => 'Hermes Germany',
274
- 'HERMES_ITALY' => 'Hermes Italy',
275
- 'HOLISOL' => 'Holisol',
276
- 'HOMEDIRECT_LOGISTICS' => 'Homedirect Logistics',
277
- 'HONG_KONG_POST' => 'Hong Kong Post',
278
- 'HRVATSKA_POSTA' => 'Hrvatska Pošta',
279
- 'HUA_HAN_LOGISTICS' => 'Hua Han Logistics',
280
- 'HUNTER_EXPRESS' => 'Hunter Express',
281
- 'ICELAND_POST' => 'Iceland Post',
282
- 'IDEX' => 'IDEX',
283
- 'IMEX_GLOBAL_SOLUTIONS' => 'IMEX Global Solutions',
284
- 'IMX_MAIL' => 'IMX Mail',
285
- 'INDIA_POST_DOMESTIC' => 'India Post Domestic',
286
- 'INDIA_POST_INTERNATIONAL' => 'India Post International',
287
- 'INPOST_PACZKOMATY' => 'InPost Paczkomaty',
288
- 'INSTANT_TIONG_NAM_EBIZ_EXPRESS_SDN_BHD' => 'INSTANT (Tiong Nam Ebiz Express Sdn Bhd)',
289
- 'INTERNATIONAL_SEUR' => 'International Seur',
290
- 'INTERNET_EXPRESS' => 'Internet Express',
291
- 'ISRAEL_POST' => 'Israel Post',
292
- 'ISRAEL_POST_DOMESTIC' => 'Israel Post Domestic',
293
- 'ITALY_SDA' => 'Italy SDA',
294
- 'I_PARCEL' => 'i-parcel',
295
- 'J_T_EXPRESS' => 'J&T EXPRESS',
296
- 'JAM_EXPRESS' => 'Jam Express',
297
- 'JANCO_ECOMMERCE' => 'Janco Ecommerce',
298
- 'JAPAN_POST' => 'Japan Post',
299
- 'JAYON_EXPRESS_JEX' => 'Jayon Express (JEX)',
300
- 'JCEX' => 'JCEX',
301
- 'JERSEY_POST' => 'Jersey Post',
302
- 'JET_SHIP_WORLDWIDE' => 'Jet-Ship Worldwide',
303
- 'JINSUNG_TRADING' => 'JINSUNG TRADING',
304
- 'JNE' => 'JNE',
305
- 'JOCOM' => 'Jocom',
306
- 'JP_BH_POSTA' => 'JP BH Pošta',
307
- 'JX' => 'JX',
308
- 'J_NET' => 'J-Net',
309
- 'K1_EXPRESS' => 'K1 Express',
310
- 'KANGAROO_WORLDWIDE_EXPRESS' => 'Kangaroo Worldwide Express',
311
- 'KERRY_EXPRESS_HONG_KONG' => 'Kerry Express Hong Kong',
312
- 'KERRY_EXPRESS_THAILAND' => 'Kerry Express Thailand',
313
- 'KERRY_EXPRESS_VIETNAM_CO_LTD' => 'Kerry Express (Vietnam) Co Ltd',
314
- 'KGM_HUB' => 'KGM Hub',
315
- 'KIALA' => 'Kiala',
316
- 'KOREA_POST' => 'Korea Post',
317
- 'KOREA_POST_EMS' => 'Korea Post EMS',
318
- 'KRONOS_EXPRESS' => 'Kronos Express',
319
- 'KUEHNE_NAGEL' => 'Kuehne + Nagel',
320
- 'LANDMARK_GLOBAL' => 'Landmark Global',
321
- 'LAO_POST' => 'Lao Post',
322
- 'LASERSHIP' => 'LaserShip',
323
- 'LA_POSTE' => 'La Poste',
324
- 'LBC_EXPRESS' => 'LBC Express',
325
- 'LHT_EXPRESS' => 'LHT Express',
326
- 'LIETUVOS_PASTAS' => 'Lietuvos Paštas',
327
- 'LINE_CLEAR_EXPRESS_LOGISTICS_SDN_BHD' => 'Line Clear Express & Logistics Sdn Bhd',
328
  'LINK_BRIDGE_BEIJING_INTERNATIONAL_LOGISTICS_COLTD' => 'Link Bridge(BeiJing)international logistics co.,ltd',
329
- 'LION_PARCEL' => 'Lion Parcel',
330
- 'LOGISTIC_WORLDWIDE_EXPRESS' => 'Logistic Worldwide Express',
331
- 'LOGWIN_LOGISTICS' => 'Logwin Logistics',
332
- 'LONE_STAR_OVERNIGHT' => 'Lone Star Overnight',
333
- 'MAGYAR_POSTA' => 'Magyar Posta',
334
- 'MAILAMERICAS' => 'MailAmericas',
335
- 'MAILPLUS' => 'MailPlus',
336
- 'MAINFREIGHT' => 'Mainfreight',
337
- 'MALAYSIA_POST_EMS_POS_LAJU' => 'Malaysia Post EMS / Pos Laju',
338
- 'MALAYSIA_POST_REGISTERED' => 'Malaysia Post - Registered',
339
- 'MARA_XPRESS' => 'Mara Xpress',
340
- 'MATDESPATCH' => 'Matdespatch',
341
- 'MATKAHUOLTO' => 'Matkahuolto',
342
- 'MDS_COLLIVERY_PTY_LTD' => 'MDS Collivery Pty (Ltd)',
343
- 'MEGASAVE' => 'Megasave',
344
- 'MEXICO_AEROFLASH' => 'Mexico AeroFlash',
345
- 'MEXICO_REDPACK' => 'Mexico Redpack',
346
- 'MEXICO_SENDA_EXPRESS' => 'Mexico Senda Express',
347
- 'MIKROPAKKET' => 'Mikropakket',
348
- 'MONDIAL_RELAY' => 'Mondial Relay',
349
- 'MRW' => 'MRW',
350
- 'MUDITA' => 'MUDITA',
351
- 'MXE_EXPRESS' => 'MXE Express',
352
- 'MYHERMES_UK' => 'myHermes UK',
353
- 'MYPOSTONLINE' => 'Mypostonline',
354
- 'M_XPRESS_SDN_BHD' => 'M Xpress Sdn Bhd',
355
- 'NACEX_SPAIN' => 'NACEX Spain',
356
- 'NANJING_WOYUAN' => 'Nanjing Woyuan',
357
- 'NATIONAL_SAMEDAY' => 'National Sameday',
358
- 'NATIONWIDE_EXPRESS' => 'Nationwide Express',
359
- 'NEWGISTICS' => 'Newgistics',
360
- 'NEWGISTICS_API' => 'Newgistics API',
361
- 'NEXIVE_TNT_POST_ITALY' => 'Nexive (TNT Post Italy)',
362
- 'NHANS_SOLUTIONS' => 'Nhans Solutions',
363
- 'NIGHTLINE' => 'Nightline',
364
- 'NIM_EXPRESS' => 'Nim Express',
365
- 'NINJA_VAN' => 'Ninja Van',
366
- 'NINJA_VAN_INDONESIA' => 'Ninja Van Indonesia',
367
- 'NINJA_VAN_MALAYSIA' => 'Ninja Van Malaysia',
368
- 'NINJA_VAN_PHILIPPINES' => 'Ninja Van Philippines',
369
- 'NINJA_VAN_THAILAND' => 'Ninja Van Thailand',
370
- 'NIPOST' => 'NiPost',
371
- 'NORSK_GLOBAL' => 'Norsk Global',
372
- 'NOVA_POSHTA' => 'Nova Poshta',
373
- 'NOVA_POSHTA_INTERNATIONAL' => 'Nova Poshta (International)',
374
- 'OCA_ARGENTINA' => 'OCA Argentina',
375
- 'OLD_DOMINION_FREIGHT_LINE' => 'Old Dominion Freight Line',
376
- 'OMNIVA' => 'Omniva',
377
- 'OMNI_PARCEL' => 'Omni Parcel',
378
- 'ONE_WORLD_EXPRESS' => 'One World Express',
379
- 'PACKLINK' => 'Packlink',
380
- 'PAL_EXPRESS_LIMITED' => 'PAL Express Limited',
381
- 'PANDU_LOGISTICS' => 'Pandu Logistics',
382
- 'PANTHER' => 'Panther',
383
- 'PANTHER_ORDER_NUMBER' => 'Panther Order Number',
384
- 'PANTHER_REFERENCE' => 'Panther Reference',
385
- 'PAQUETEXPRESS' => 'Paquetexpress',
386
- 'PARCELLEDIN' => 'Parcelled.in',
387
- 'PARCELPOINT_PTY_LTD' => 'ParcelPoint Pty Ltd',
388
- 'PARCEL_FORCE' => 'Parcel Force',
389
- 'PARCEL_POST_SINGAPORE' => 'Parcel Post Singapore',
390
- 'PAYPAL_PACKAGE' => 'PayPal Package',
391
- 'PFC_EXPRESS' => 'PFC Express',
392
- 'PICKUPP' => 'Pickupp',
393
- 'PICK_UPP_MYS_SGP' => 'PICK UPP',
394
- 'PILOT_FREIGHT_SERVICES' => 'Pilot Freight Services',
395
- 'PITNEY_BOWES' => 'Pitney Bowes',
396
- 'PIXSELL_LOGISTICS' => 'PIXSELL LOGISTICS',
397
- 'POCZTA_POLSKA' => 'Poczta Polska',
398
- 'PORTUGAL_CTT' => 'Portugal CTT',
399
- 'PORTUGAL_SEUR' => 'Portugal Seur',
400
- 'POST56' => 'Post56',
401
- 'POSTEN_NORGE_BRING' => 'Posten Norge / Bring',
402
- 'POSTE_ITALIANE' => 'Poste Italiane',
403
- 'POSTE_ITALIANE_PACCOCELERE' => 'Poste Italiane Paccocelere',
404
- 'POSTI' => 'Posti',
405
- 'POSTNL_DOMESTIC' => 'PostNL Domestic',
406
- 'POSTNL_INTERNATIONAL' => 'PostNL International',
407
- 'POSTNL_INTERNATIONAL_3S' => 'PostNL International 3S',
408
- 'POSTNORD_DENMARK' => 'PostNord Denmark',
409
- 'POSTNORD_LOGISTICS' => 'PostNord Logistics',
410
- 'POSTNORD_SWEDEN' => 'PostNord Sweden',
411
- 'POST_OF_SLOVENIA' => 'Post of Slovenia',
412
- 'POST_SERBIA' => 'Post Serbia',
413
- 'POS_INDONESIA_DOMESTIC' => 'Pos Indonesia Domestic',
414
- 'POS_INDONESIA_INTL' => "Pos Indonesia Int'l",
415
- 'POSTA_ROMANA' => 'Poșta Română',
416
- 'PROFESSIONAL_COURIERS' => 'Professional Couriers',
417
- 'PTT_POSTA' => 'PTT Posta',
418
- 'QUALITYPOST' => 'QualityPost',
419
- 'QUANTIUM' => 'Quantium',
420
- 'QXPRESS' => 'Qxpress',
421
- 'RABEN_GROUP' => 'Raben Group',
422
- 'RAF_PHILIPPINES' => 'RAF Philippines',
423
- 'RAIDEREX' => 'RaidereX',
424
- 'RAM' => 'RAM',
425
- 'REDUR_SPAIN' => 'Redur Spain',
426
- 'RED_CARPET_LOGISTICS' => 'Red Carpet Logistics',
427
- 'RINCOS' => 'Rincos',
428
- 'RL_CARRIERS' => 'RL Carriers',
429
- 'ROADBULL_LOGISTICS' => 'Roadbull Logistics',
430
- 'ROCKET_PARCEL_INTERNATIONAL' => 'Rocket Parcel International',
431
- 'RPD2MAN_DELIVERIES' => 'RPD2man Deliveries',
432
- 'RPX_INDONESIA' => 'RPX Indonesia',
433
- 'RPX_ONLINE' => 'RPX Online',
434
- 'RRD_INTERNATIONAL_LOGISTICS_USA' => 'RRD International Logistics U.S.A',
435
- 'RUSSIAN_POST' => 'Russian Post',
436
- 'RUSTON' => 'Ruston',
437
- 'RZY_EXPRESS' => 'RZY Express',
438
- 'SAFEXPRESS' => 'Safexpress',
439
- 'SAGAWA' => 'Sagawa',
440
- 'SAIA_LTL_FREIGHT' => 'Saia LTL Freight',
441
- 'SAILPOST' => 'SAILPOST',
442
- 'SAP_EXPRESS' => 'SAP EXPRESS',
443
- 'SAUDI_POST' => 'Saudi Post',
444
- 'SCUDEX_EXPRESS' => 'Scudex Express',
445
- 'SEINO' => 'Seino',
446
- 'SEKO_LOGISTICS' => 'SEKO Logistics',
447
- 'SENDING_TRANSPORTE_URGENTE_Y_COMUNICACION_SAU' => 'Sending Transporte Urgente y Comunicacion, S.A.U',
448
- 'SENDIT' => 'Sendit',
449
- 'SENDLE' => 'Sendle',
450
- 'SFC_SERVICE' => 'SFC Service',
451
- 'SF_EXPRESS' => 'S.F. Express',
452
- 'SF_INTERNATIONAL' => 'S.F International',
453
- 'SGT_CORRIERE_ESPRESSO' => 'SGT Corriere Espresso',
454
- 'SHANGHAI_WISE_SUPPLY_CHAIN_MANAGEMENT_CO_LTD' => '上海万色供应链管理有限公司(原:上海万色速递有限公司) Shanghai Wise Supply Chain Management Co., Ltd',
455
- 'SHENZHEN_JINGHUADA_LOGISTICS_CO_LTD' => 'Shenzhen Jinghuada Logistics Co., Ltd',
456
- 'SHIPPIT' => 'Shippit',
457
- 'SHIPTOR' => 'Shiptor',
458
- 'SHOPFANSRU_LLC' => 'ShopfansRU LLC',
459
- 'SHREE_MARUTI_COURIER_SERVICES_PVT_LTD' => 'Shree Maruti Courier Services Pvt Ltd',
460
- 'SHREE_TIRUPATI_COURIER_SERVICES_PVT_LTD' => 'SHREE TIRUPATI COURIER SERVICES PVT. LTD.',
461
- 'SHUNYOU_POST' => 'Shunyou Post',
462
- 'SIMPLYPOST' => 'SimplyPost',
463
- 'SINGAPORE_POST' => 'Singapore Post',
464
- 'SINGAPORE_SPEEDPOST' => 'Singapore Speedpost',
465
- 'SIODEMKA' => 'Siodemka',
466
- 'SKYBOX' => 'SKYBOX',
467
- 'SKYNET_MALAYSIA' => 'SkyNet Malaysia',
468
- 'SKYNET_WORLDWIDE_EXPRESS' => 'SkyNet Worldwide Express',
469
- 'SKYNET_WORLDWIDE_EXPRESS_UAE' => 'SkyNet Worldwide Express UAE',
470
- 'SKYNET_WORLDWIDE_EXPRESS_UK' => 'Skynet Worldwide Express UK',
471
- 'SKYNET_WORLD_WIDE_EXPRESS_SOUTH_AFRICA' => 'Skynet World Wide Express South Africa',
472
- 'SKYPOSTAL' => 'SkyPostal',
473
- 'SMOOTH_COURIERS' => 'Smooth Couriers',
474
- 'SMSA_EXPRESS' => 'SMSA Express',
475
- 'SOUTH_AFRICAN_POST_OFFICE' => 'South African Post Office',
476
- 'SPANISH_SEUR' => 'Spanish Seur',
477
- 'SPECIALISED_FREIGHT' => 'Specialised Freight',
478
- 'SPEEDEX_COURIER' => 'Speedex Courier',
479
- 'SPEED_COURIERS' => 'Speed Couriers',
480
- 'SPOTON_LOGISTICS_PVT_LTD' => 'SPOTON Logistics Pvt Ltd',
481
- 'SRE_KOREA' => 'SRE Korea',
482
- 'STARTRACK' => 'StarTrack',
483
- 'STAR_TRACK_COURIER' => 'Star Track Courier',
484
- 'STAR_TRACK_EXPRESS' => 'Star Track Express',
485
- 'STO_EXPRESS' => 'STO Express',
486
- 'SWISS_POST' => 'Swiss Post',
487
- 'TAIWAN_POST' => 'Taiwan Post',
488
- 'TAQBIN_HONG_KONG' => 'TAQBIN Hong Kong',
489
- 'TAQBIN_MALAYSIA' => 'TAQBIN Malaysia',
490
- 'TAQBIN_SINGAPORE' => 'TAQBIN Singapore',
491
- 'TCS' => 'TCS',
492
- 'TELIWAY_SIC_EXPRESS' => 'Teliway SIC Express',
493
- 'THAILAND_THAI_POST' => 'Thailand Thai Post',
494
- 'THE_COURIER_GUY' => 'The Courier Guy',
495
- 'TIKI' => 'Tiki',
496
- 'TIPSA' => 'TIPSA',
497
- 'TNT_AUSTRALIA' => 'TNT Australia',
498
- 'TNT_CLICK_ITALY' => 'TNT-Click Italy',
499
- 'TNT_FRANCE' => 'TNT France',
500
- 'TNT_ITALY' => 'TNT Italy',
501
- 'TNT_REFERENCE' => 'TNT Reference',
502
- 'TNT_UK_REFERENCE' => 'TNT UK Reference',
503
- 'TOLL_IPEC' => 'Toll IPEC',
504
- 'TOLL_PRIORITY' => 'Toll Priority',
505
- 'TOLOS' => 'Tolos',
506
- 'TRAKPAK' => 'TrakPak',
507
- 'TRANSMISSION' => 'TransMission',
508
- 'TRANS_KARGO_INTERNASIONAL' => 'Trans Kargo Internasional',
509
- 'TUFFNELLS_PARCELS_EXPRESS' => 'Tuffnells Parcels Express',
510
- 'UBI_SMART_PARCEL' => 'UBI Smart Parcel',
511
- 'UKRPOSHTA' => 'UkrPoshta',
512
- 'UK_MAIL' => 'UK Mail',
513
- 'UNITED_DELIVERY_SERVICE_LTD' => 'United Delivery Service, Ltd',
514
- 'UPS_FREIGHT' => 'UPS Freight',
515
- 'UPS_MAIL_INNOVATIONS' => 'UPS Mail Innovations',
516
- 'VIETNAM_POST' => 'Vietnam Post',
517
- 'VIETNAM_POST_EMS' => 'Vietnam Post EMS',
518
- 'VIETTELPOST' => 'ViettelPost',
519
- 'WAHANA' => 'Wahana',
520
- 'WANBEXPRESS' => 'WanbExpress',
521
- 'WEDO_LOGISTICS' => 'WeDo Logistics',
522
- 'WEPOST_LOGISTICS' => 'WePost Logistics',
523
- 'WHISTL' => 'Whistl',
524
- 'WISELOADS' => 'Wiseloads',
525
- 'WISE_EXPRESS' => 'Wise Express',
526
- 'WISHPOST' => 'WishPost',
527
- 'WNDIRECT' => 'wnDirect',
528
- 'XDP_EXPRESS' => 'XDP Express',
529
- 'XDP_EXPRESS_REFERENCE' => 'XDP Express Reference',
530
- 'XEND_EXPRESS' => 'Xend Express',
531
- 'XL_EXPRESS' => 'XL Express',
532
- 'XPOSTPH' => 'Xpost.ph',
533
- 'XPRESSBEES' => 'XpressBees',
534
- 'XQ_EXPRESS' => 'XQ Express',
535
- 'YAKIT' => 'Yakit',
536
- 'YAMATO_JAPAN' => 'Yamato Japan',
537
- 'YANWEN' => 'Yanwen',
538
- 'YODEL_DOMESTIC' => 'Yodel Domestic',
539
- 'YODEL_INTERNATIONAL' => 'Yodel International',
540
- 'YRC' => 'YRC',
541
- 'YTO_EXPRESS' => 'YTO Express',
542
- 'YUNDA_EXPRESS' => 'Yunda Express',
543
- 'YUN_EXPRESS' => 'Yun Express',
544
- 'ZEPTOEXPRESS' => 'ZeptoExpress',
545
- 'ZINC' => 'Zinc',
546
- 'ZJS_INTERNATIONAL' => 'ZJS International',
547
- 'ZTO_EXPRESS' => 'ZTO Express',
548
- 'ZYLLEM' => 'Zyllem',
549
- ];
550
 
551
  /**
552
  * @see https://docs.woocommerce.com/document/shipment-tracking/#section-5
553
  */
554
- $this->shipment_tracking_carriers = [
555
  // Australia
556
  'Australia Post' => 'AUSTRALIA_POST',
557
  'Fastway Couriers' => 'FASTWAY_AUSTRALIA',
@@ -630,7 +630,7 @@ class Shipment {
630
  'UPS' => 'UPS',
631
  'USPS' => 'USPS',
632
  'DHL US' => 'DHL_ECOMMERCE_US', // TODO: may be DHL
633
- ];
634
  }
635
 
636
 
@@ -673,7 +673,7 @@ class Shipment {
673
 
674
  if ( isset( $this->shipment_tracking_carriers[ $carrier ] ) ) {
675
  $carrier_code = $this->shipment_tracking_carriers[ $carrier ];
676
- } elseif ( in_array( $carrier, $this->get_carrier_options(), true ) ) {
677
  $carrier_code = array_search( $carrier, $this->get_carrier_options(), true );
678
  } elseif ( array_key_exists( $carrier, $this->get_carrier_options() ) ) {
679
  $carrier_code = $carrier;
37
  /**
38
  * @see https://developers.facebook.com/docs/commerce-platform/order-management/carrier-codes
39
  */
40
+ $this->valid_carriers = array(
41
+ 'AUSTRALIA_POST' => 'Australia Post',
42
+ 'CANADA_POST' => 'Canada Post',
43
+ 'DHL' => 'DHL',
44
+ 'DHL_ECOMMERCE_US' => 'DHL eCommerce US',
45
+ 'EAGLE' => 'Eagle',
46
+ 'FEDEX' => 'FedEx',
47
+ 'FEDEX_UK' => 'FedEx UK',
48
+ 'NEW_ZEALAND_POST' => 'New Zealand Post',
49
+ 'ONTRAC' => 'OnTrac',
50
+ 'POST_DANMARK' => 'Post Danmark',
51
+ 'PUROLATOR' => 'Purolator',
52
+ 'ROYAL_MAIL' => 'Royal Mail',
53
+ 'SPEE_DEE' => 'Spee-Dee',
54
+ 'TNT' => 'TNT',
55
+ 'TNT_POST' => 'TNT Post',
56
+ 'UPS' => 'UPS',
57
+ 'USPS' => 'USPS',
58
+ 'OTHER' => 'Other tracking number',
59
+ 'ABF_FREIGHT' => 'ABF Freight',
60
+ 'ABX_EXPRESS' => 'ABX Express',
61
+ 'AB_CUSTOM_GROUP' => 'AB Custom Group',
62
+ 'ACOMMERCE' => 'aCommerce',
63
+ 'ACS_COURIER' => 'ACS Courier',
64
+ 'ACS_WORLDWIDE_EXPRESS' => 'ACS Worldwide Express',
65
+ 'ADICIONAL_LOGISTICS' => 'Adicional Logistics',
66
+ 'ADSONE' => 'ADSOne',
67
+ 'AIR21' => 'AIR21',
68
+ 'AIRPAK_EXPRESS' => 'Airpak Express',
69
+ 'AIRSPEED_INTERNATIONAL_CORPORATION' => 'Airspeed International Corporation',
70
+ 'ALFATREX' => 'AlfaTrex',
71
+ 'ALLIED_EXPRESS' => 'Allied Express',
72
+ 'ALLJOY_SUPPLY_CHAIN_CO_LTD' => 'ALLJOY SUPPLY CHAIN CO., LTD',
73
+ 'ALPHAFAST' => 'alphaFAST',
74
+ 'AMAZON_FBA_USA' => 'Amazon FBA USA',
75
+ 'AMAZON_LOGISTICS' => 'Amazon Logistics',
76
+ 'AN_POST' => 'An Post',
77
+ 'APC_OVERNIGHT' => 'APC Overnight',
78
+ 'APC_OVERNIGHT_REFERENCE' => 'APC Overnight Reference',
79
+ 'APC_POSTAL_LOGISTICS' => 'APC Postal Logistics',
80
+ 'APRISA_EXPRESS' => 'Aprisa Express',
81
+ 'ARAMEX' => 'Aramex',
82
+ 'ARROW_XL' => 'Arrow XL',
83
+ 'ASENDIA_GERMANY' => 'Asendia Germany',
84
+ 'ASENDIA_HK' => 'Asendia HK',
85
+ 'ASENDIA_HK_PREMIUM_SERVICE_LATAM' => 'Asendia HK - Premium Service (LATAM)',
86
+ 'ASENDIA_UK' => 'Asendia UK',
87
+ 'ASENDIA_USA' => 'Asendia USA',
88
+ 'ASM' => 'ASM',
89
+ 'AUPOST_CHINA' => 'AuPost China',
90
+ 'AUSTRALIA_POST_SFTP' => 'Australia Post Sftp',
91
+ 'AUSTRIAN_POST_EXPRESS' => 'Austrian Post (Express)',
92
+ 'AUSTRIAN_POST_REGISTERED' => 'Austrian Post (Registered)',
93
+ 'AXL_EXPRESS_LOGISTICS' => 'AXL Express & Logistics',
94
+ 'A_DUIE_PYLE' => 'A Duie Pyle',
95
+ 'A_J_EXPRESS' => 'a j express',
96
+ 'B2C_EUROPE' => 'B2C Europe',
97
+ 'BELPOST' => 'Belpost',
98
+ 'BERT_TRANSPORT' => 'Bert Transport',
99
+ 'BEST_EXPRESS' => 'Best Express',
100
+ 'BEST_WAY_PARCEL' => 'Best Way Parcel',
101
+ 'BIRDSYSTEM' => 'BirdSystem',
102
+ 'BJS_DISTRIBUTION_STORAGE_COURIERS' => 'BJS Distribution, Storage & Couriers',
103
+ 'BJS_DISTRIBUTION_STORAGE_COURIERS_FTP' => 'BJS Distribution, Storage & Couriers - FTP',
104
+ 'BLUECARE_EXPRESS_LTD' => 'Bluecare Express Ltd',
105
+ 'BLUEDART' => 'Bluedart',
106
+ 'BLUE_STAR' => 'Blue Star',
107
+ 'BNEED' => 'Bneed',
108
+ 'BONDS_COURIERS' => 'Bonds Couriers',
109
+ 'BOXC' => 'BoxC',
110
+ 'BPOST' => 'Bpost',
111
+ 'BPOST_INTERNATIONAL' => 'Bpost international',
112
+ 'BRAZIL_CORREIOS' => 'Brazil Correios',
113
+ 'BRT_BARTOLINI' => 'BRT Bartolini',
114
+ 'BRT_BARTOLINI_PARCEL_ID' => 'BRT Bartolini(Parcel ID)',
115
+ 'BULGARIAN_POSTS' => 'Bulgarian Posts',
116
+ 'BUYLOGIC' => 'Buylogic',
117
+ 'CAMBODIA_POST' => 'Cambodia Post',
118
+ 'CANPAR_COURIER' => 'Canpar Courier',
119
+ 'CAPITAL_TRANSPORT' => 'Capital Transport',
120
+ 'CARRIER_007EX' => '007EX',
121
+ 'CARRIER_17_POST_SERVICE' => '17 Post Service',
122
+ 'CARRIER_2GO' => '2GO',
123
+ 'CARRIER_360_LION_EXPRESS' => '360 Lion Express',
124
+ 'CARRIER_4PX' => '4PX',
125
+ 'CARRIER_4_72_ENTREGANDO' => '4-72 Entregando',
126
+ 'CARRIER_ECHO' => 'Echo',
127
+ 'CBL_LOGISTICS' => 'CBL Logistics',
128
+ 'CELERITAS_TRANSPORTE_SL' => 'Celeritas Transporte, S.L',
129
+ 'CESKA_POSTA' => 'Česká Poš',
130
+ 'CHINA_EMS_EPACKET' => 'China EMS (ePacket)',
131
+ 'CHINA_POST' => 'China Post',
132
+ 'CHIT_CHATS' => 'Chit Chats',
133
+ 'CHRONOPOST_FRANCE' => 'Chronopost France',
134
+ 'CHRONOPOST_PORTUGAL' => 'Chronopost Portugal',
135
+ 'CH_ROBINSON_WORLDWIDE_INC' => 'C.H. Robinson Worldwide, Inc.',
136
+ 'CITY_LINK_EXPRESS' => 'City-Link Express',
137
+ 'CJ_CENTURY' => 'CJ Century',
138
+ 'CJ_CENTURY_INTERNATIONAL' => 'CJ Century (International)',
139
+ 'CJ_GLS' => 'CJ GLS',
140
+ 'CJ_KOREA_EXPRESS' => 'CJ Korea Express',
141
+ 'CJ_LOGISTICS_INTERNATIONAL' => 'CJ Logistics International',
142
+ 'CJ_TRANSNATIONAL_PHILIPPINES' => 'CJ Transnational Philippines',
143
+ 'CLEVY_LINKS' => 'Clevy Links',
144
+ 'CLOUDWISH_ASIA' => 'Cloudwish Asia',
145
+ 'CNE_EXPRESS' => 'CNE Express',
146
+ 'COLISSIMO' => 'Colissimo',
147
+ 'COLIS_PRIVE' => 'Colis Privé',
148
+ 'COLLECTCO' => 'CollectCo',
149
+ 'COLLECT_PLUS' => 'Collect+',
150
+ 'CON_WAY_FREIGHT' => 'Con-way Freight',
151
+ 'COPA_AIRLINES_COURIER' => 'Copa Airlines Courier',
152
+ 'CORREOS_CHILE' => 'Correos Chile',
153
+ 'CORREOS_DE_COSTA_RICA' => 'Correos de Costa Rica',
154
+ 'CORREOS_DE_ESPANA' => 'Correos de España',
155
+ 'CORREOS_DE_MEXICO' => 'Correos de Mexico',
156
+ 'CORREOS_EXPRESS' => 'Correos Express',
157
+ 'CORREO_ARGENTINO' => 'Correo Argentino',
158
+ 'COSMETICS_NOW' => 'Cosmetics Now',
159
+ 'COUREX' => 'Courex',
160
+ 'COURIERPOST' => 'CourierPost',
161
+ 'COURIERS_PLEASE' => 'Couriers Please',
162
+ 'COURIER_IT' => 'Courier IT',
163
+ 'COURIER_PLUS' => 'Courier Plus',
164
+ 'CPACKET' => 'cPacket',
165
+ 'CUCKOO_EXPRESS' => 'Cuckoo Express',
166
+ 'CYPRUS_POST' => 'Cyprus Post',
167
+ 'DACHSER' => 'DACHSER',
168
+ 'DAWN_WING' => 'Dawn Wing',
169
+ 'DAYLIGHT_TRANSPORT_LLC' => 'Daylight Transport, LLC',
170
+ 'DB_SCHENKER' => 'DB Schenker',
171
+ 'DB_SCHENKER_SWEDEN' => 'DB Schenker Sweden',
172
+ 'DD_EXPRESS_COURIER' => 'DD Express Courier',
173
+ 'DELCART' => 'Delcart',
174
+ 'DELHIVERY' => 'Delhivery',
175
+ 'DELIVERYONTIME_LOGISTICS_PVT_LTD' => 'DELIVERYONTIME LOGISTICS PVT LTD',
176
+ 'DELTEC_COURIER' => 'Deltec Courier',
177
+ 'DEMANDSHIP' => 'DemandShip',
178
+ 'DETRACK' => 'Detrack',
179
+ 'DEUTSCHE_POST_DHL' => 'Deutsche Post DHL',
180
+ 'DEUTSCHE_POST_MAIL' => 'Deutsche Post Mail',
181
+ 'DEX_I' => 'DEX-I',
182
+ 'DHL_2_MANN_HANDLING' => 'DHL 2-Mann-Handling',
183
+ 'DHL_ACTIVE_TRACING' => 'DHL Active Tracing',
184
+ 'DHL_BENELUX' => 'DHL Benelux',
185
+ 'DHL_ECOMMERCE_ASIA' => 'DHL eCommerce Asia',
186
+ 'DHL_EXPRESS_PIECE_ID' => 'DHL Express (Piece ID)',
187
+ 'DHL_GLOBAL_FORWARDING' => 'DHL Global Forwarding',
188
+ 'DHL_HONG_KONG' => 'DHL Hong Kong',
189
+ 'DHL_NETHERLANDS' => 'DHL Netherlands',
190
+ 'DHL_PARCEL_NL' => 'DHL Parcel NL',
191
+ 'DHL_PARCEL_SPAIN' => 'DHL Parcel Spain',
192
+ 'DHL_POLAND_DOMESTIC' => 'DHL Poland Domestic',
193
+ 'DHL_SPAIN_DOMESTIC' => 'DHL Spain Domestic',
194
+ 'DIMERCO_EXPRESS_GROUP' => 'Dimerco Express Group',
195
+ 'DIRECTLOG' => 'Directlog',
196
+ 'DIRECT_FREIGHT_EXPRESS' => 'Direct Freight Express',
197
+ 'DIRECT_LINK' => 'Direct Link',
198
+ 'DMM_NETWORK' => 'DMM Network',
199
+ 'DOORA_LOGISTICS' => 'Doora Logistics',
200
+ 'DOTZOT' => 'Dotzot',
201
+ 'DPD' => 'DPD',
202
+ 'DPD_FRANCE' => 'DPD France',
203
+ 'DPD_GERMANY' => 'DPD Germany',
204
+ 'DPD_HK' => 'DPD HK',
205
+ 'DPD_IRELAND' => 'DPD Ireland',
206
+ 'DPD_LOCAL' => 'DPD Local',
207
+ 'DPD_LOCAL_REFERENCE' => 'DPD Local reference',
208
+ 'DPD_POLAND' => 'DPD Poland',
209
+ 'DPD_ROMANIA' => 'DPD Romania',
210
+ 'DPD_RUSSIA' => 'DPD Russia',
211
+ 'DPD_UK' => 'DPD UK',
212
+ 'DPEX' => 'DPEX',
213
+ 'DPEX_CHINA' => 'DPEX China',
214
+ 'DPE_EXPRESS' => 'DPE Express',
215
+ 'DPE_SOUTH_AFRICA' => 'DPE South Africa',
216
+ 'DSV' => 'DSV',
217
+ 'DTDC_AUSTRALIA' => 'DTDC Australia',
218
+ 'DTDC_EXPRESS_GLOBAL_PTE_LTD' => 'DTDC Express Global PTE LTD',
219
+ 'DTDC_INDIA' => 'DTDC India',
220
+ 'DX' => 'DX',
221
+ 'DX_FREIGHT' => 'DX Freight',
222
+ 'DYNALOGIC_BENELUX_BV' => 'Dynalogic Benelux BV',
223
+ 'DYNAMIC_LOGISTICS' => 'Dynamic Logistics',
224
+ 'EASY_MAIL' => 'Easy Mail',
225
+ 'ECARGO' => 'Ecargo',
226
+ 'ECMS_INTERNATIONAL_LOGISTICS_CO_LTD' => 'ECMS International Logistics Co., Ltd.',
227
+ 'ECOM_EXPRESS' => 'Ecom Express',
228
+ 'EC_FIRSTCLASS' => 'EC-Firstclass',
229
+ 'EFS_E_COMMERCE_FULFILLMENT_SERVICE' => 'EFS (E-commerce Fulfillment Service)',
230
+ 'EKART' => 'Ekart',
231
+ 'ELTA_HELLENIC_POST' => 'ELTA Hellenic Post',
232
+ 'EMIRATES_POST' => 'Emirates Post',
233
+ 'EMPS_EXPRESS' => 'EMPS Express',
234
+ 'ENSENDA' => 'Ensenda',
235
+ 'ENVIALIA' => 'Envialia',
236
+ 'EPARCEL_KOREA' => 'eParcel Korea',
237
+ 'EP_BOX' => 'EP-Box',
238
+ 'EQUICK_CHINA' => 'Equick China',
239
+ 'ESTAFETA' => 'Estafeta',
240
+ 'ESTES' => 'Estes',
241
+ 'ETOTAL_SOLUTION_LIMITED' => 'eTotal Solution Limited',
242
+ 'EURODIS' => 'Eurodis',
243
+ 'EXPEDITORS' => 'Expeditors',
244
+ 'EZSHIP' => 'EZship',
245
+ 'FASTRAK_SERVICES' => 'Fastrak Services',
246
+ 'FASTWAY_AUSTRALIA' => 'Fastway Australia',
247
+ 'FASTWAY_IRELAND' => 'Fastway Ireland',
248
+ 'FASTWAY_NEW_ZEALAND' => 'Fastway New Zealand',
249
+ 'FASTWAY_SOUTH_AFRICA' => 'Fastway South Africa',
250
+ 'FEDEX_CROSS_BORDER' => 'Fedex Cross Border',
251
+ 'FEDEX_FREIGHT' => 'FedEx Freight',
252
+ 'FEDEX_POLAND_DOMESTIC' => 'FedEx Poland Domestic',
253
+ 'FERCAM_LOGISTICS_TRANSPORT' => 'FERCAM Logistics & Transport',
254
+ 'FIRST_FLIGHT_COURIERS' => 'First Flight Couriers',
255
+ 'FIRST_LOGISTICS' => 'First Logistics',
256
+ 'FLYT_EXPRESS' => 'Flyt Express',
257
+ 'GATI_KWE' => 'Gati-KWE',
258
+ 'GDEX' => 'GDEX',
259
+ 'GENIKI_TAXYDROMIKI' => 'Geniki Taxydromiki',
260
+ 'GEODIS_E_SPACE' => 'Geodis E-space',
261
+ 'GEODIS_DISTRIBUTION_EXPRESS' => 'GEODIS - Distribution & Express',
262
+ 'GIAO_HANG_NHANH' => 'Giao hàng nhanh',
263
+ 'GLOBEGISTICS_INC' => 'Globegistics Inc.',
264
+ 'GLS' => 'GLS',
265
+ 'GLS_CZECH_REPUBLIC' => 'GLS Czech Republic',
266
+ 'GLS_ITALY' => 'GLS Italy',
267
+ 'GLS_NETHERLANDS' => 'GLS Netherlands',
268
+ 'GOFLY' => 'GoFly',
269
+ 'GOJAVAS' => 'GoJavas',
270
+ 'GREYHOUND' => 'Greyhound',
271
+ 'GSI_EXPRESS' => 'GSI EXPRESS',
272
+ 'HERMESWORLD' => 'Hermesworld',
273
+ 'HERMES_GERMANY' => 'Hermes Germany',
274
+ 'HERMES_ITALY' => 'Hermes Italy',
275
+ 'HOLISOL' => 'Holisol',
276
+ 'HOMEDIRECT_LOGISTICS' => 'Homedirect Logistics',
277
+ 'HONG_KONG_POST' => 'Hong Kong Post',
278
+ 'HRVATSKA_POSTA' => 'Hrvatska Pošta',
279
+ 'HUA_HAN_LOGISTICS' => 'Hua Han Logistics',
280
+ 'HUNTER_EXPRESS' => 'Hunter Express',
281
+ 'ICELAND_POST' => 'Iceland Post',
282
+ 'IDEX' => 'IDEX',
283
+ 'IMEX_GLOBAL_SOLUTIONS' => 'IMEX Global Solutions',
284
+ 'IMX_MAIL' => 'IMX Mail',
285
+ 'INDIA_POST_DOMESTIC' => 'India Post Domestic',
286
+ 'INDIA_POST_INTERNATIONAL' => 'India Post International',
287
+ 'INPOST_PACZKOMATY' => 'InPost Paczkomaty',
288
+ 'INSTANT_TIONG_NAM_EBIZ_EXPRESS_SDN_BHD' => 'INSTANT (Tiong Nam Ebiz Express Sdn Bhd)',
289
+ 'INTERNATIONAL_SEUR' => 'International Seur',
290
+ 'INTERNET_EXPRESS' => 'Internet Express',
291
+ 'ISRAEL_POST' => 'Israel Post',
292
+ 'ISRAEL_POST_DOMESTIC' => 'Israel Post Domestic',
293
+ 'ITALY_SDA' => 'Italy SDA',
294
+ 'I_PARCEL' => 'i-parcel',
295
+ 'J_T_EXPRESS' => 'J&T EXPRESS',
296
+ 'JAM_EXPRESS' => 'Jam Express',
297
+ 'JANCO_ECOMMERCE' => 'Janco Ecommerce',
298
+ 'JAPAN_POST' => 'Japan Post',
299
+ 'JAYON_EXPRESS_JEX' => 'Jayon Express (JEX)',
300
+ 'JCEX' => 'JCEX',
301
+ 'JERSEY_POST' => 'Jersey Post',
302
+ 'JET_SHIP_WORLDWIDE' => 'Jet-Ship Worldwide',
303
+ 'JINSUNG_TRADING' => 'JINSUNG TRADING',
304
+ 'JNE' => 'JNE',
305
+ 'JOCOM' => 'Jocom',
306
+ 'JP_BH_POSTA' => 'JP BH Pošta',
307
+ 'JX' => 'JX',
308
+ 'J_NET' => 'J-Net',
309
+ 'K1_EXPRESS' => 'K1 Express',
310
+ 'KANGAROO_WORLDWIDE_EXPRESS' => 'Kangaroo Worldwide Express',
311
+ 'KERRY_EXPRESS_HONG_KONG' => 'Kerry Express Hong Kong',
312
+ 'KERRY_EXPRESS_THAILAND' => 'Kerry Express Thailand',
313
+ 'KERRY_EXPRESS_VIETNAM_CO_LTD' => 'Kerry Express (Vietnam) Co Ltd',
314
+ 'KGM_HUB' => 'KGM Hub',
315
+ 'KIALA' => 'Kiala',
316
+ 'KOREA_POST' => 'Korea Post',
317
+ 'KOREA_POST_EMS' => 'Korea Post EMS',
318
+ 'KRONOS_EXPRESS' => 'Kronos Express',
319
+ 'KUEHNE_NAGEL' => 'Kuehne + Nagel',
320
+ 'LANDMARK_GLOBAL' => 'Landmark Global',
321
+ 'LAO_POST' => 'Lao Post',
322
+ 'LASERSHIP' => 'LaserShip',
323
+ 'LA_POSTE' => 'La Poste',
324
+ 'LBC_EXPRESS' => 'LBC Express',
325
+ 'LHT_EXPRESS' => 'LHT Express',
326
+ 'LIETUVOS_PASTAS' => 'Lietuvos Paštas',
327
+ 'LINE_CLEAR_EXPRESS_LOGISTICS_SDN_BHD' => 'Line Clear Express & Logistics Sdn Bhd',
328
  'LINK_BRIDGE_BEIJING_INTERNATIONAL_LOGISTICS_COLTD' => 'Link Bridge(BeiJing)international logistics co.,ltd',
329
+ 'LION_PARCEL' => 'Lion Parcel',
330
+ 'LOGISTIC_WORLDWIDE_EXPRESS' => 'Logistic Worldwide Express',
331
+ 'LOGWIN_LOGISTICS' => 'Logwin Logistics',
332
+ 'LONE_STAR_OVERNIGHT' => 'Lone Star Overnight',
333
+ 'MAGYAR_POSTA' => 'Magyar Posta',
334
+ 'MAILAMERICAS' => 'MailAmericas',
335
+ 'MAILPLUS' => 'MailPlus',
336
+ 'MAINFREIGHT' => 'Mainfreight',
337
+ 'MALAYSIA_POST_EMS_POS_LAJU' => 'Malaysia Post EMS / Pos Laju',
338
+ 'MALAYSIA_POST_REGISTERED' => 'Malaysia Post - Registered',
339
+ 'MARA_XPRESS' => 'Mara Xpress',
340
+ 'MATDESPATCH' => 'Matdespatch',
341
+ 'MATKAHUOLTO' => 'Matkahuolto',
342
+ 'MDS_COLLIVERY_PTY_LTD' => 'MDS Collivery Pty (Ltd)',
343
+ 'MEGASAVE' => 'Megasave',
344
+ 'MEXICO_AEROFLASH' => 'Mexico AeroFlash',
345
+ 'MEXICO_REDPACK' => 'Mexico Redpack',
346
+ 'MEXICO_SENDA_EXPRESS' => 'Mexico Senda Express',
347
+ 'MIKROPAKKET' => 'Mikropakket',
348
+ 'MONDIAL_RELAY' => 'Mondial Relay',
349
+ 'MRW' => 'MRW',
350
+ 'MUDITA' => 'MUDITA',
351
+ 'MXE_EXPRESS' => 'MXE Express',
352
+ 'MYHERMES_UK' => 'myHermes UK',
353
+ 'MYPOSTONLINE' => 'Mypostonline',
354
+ 'M_XPRESS_SDN_BHD' => 'M Xpress Sdn Bhd',
355
+ 'NACEX_SPAIN' => 'NACEX Spain',
356
+ 'NANJING_WOYUAN' => 'Nanjing Woyuan',
357
+ 'NATIONAL_SAMEDAY' => 'National Sameday',
358
+ 'NATIONWIDE_EXPRESS' => 'Nationwide Express',
359
+ 'NEWGISTICS' => 'Newgistics',
360
+ 'NEWGISTICS_API' => 'Newgistics API',
361
+ 'NEXIVE_TNT_POST_ITALY' => 'Nexive (TNT Post Italy)',
362
+ 'NHANS_SOLUTIONS' => 'Nhans Solutions',
363
+ 'NIGHTLINE' => 'Nightline',
364
+ 'NIM_EXPRESS' => 'Nim Express',
365
+ 'NINJA_VAN' => 'Ninja Van',
366
+ 'NINJA_VAN_INDONESIA' => 'Ninja Van Indonesia',
367
+ 'NINJA_VAN_MALAYSIA' => 'Ninja Van Malaysia',
368
+ 'NINJA_VAN_PHILIPPINES' => 'Ninja Van Philippines',
369
+ 'NINJA_VAN_THAILAND' => 'Ninja Van Thailand',
370
+ 'NIPOST' => 'NiPost',
371
+ 'NORSK_GLOBAL' => 'Norsk Global',
372
+ 'NOVA_POSHTA' => 'Nova Poshta',
373
+ 'NOVA_POSHTA_INTERNATIONAL' => 'Nova Poshta (International)',
374
+ 'OCA_ARGENTINA' => 'OCA Argentina',
375
+ 'OLD_DOMINION_FREIGHT_LINE' => 'Old Dominion Freight Line',
376
+ 'OMNIVA' => 'Omniva',
377
+ 'OMNI_PARCEL' => 'Omni Parcel',
378
+ 'ONE_WORLD_EXPRESS' => 'One World Express',
379
+ 'PACKLINK' => 'Packlink',
380
+ 'PAL_EXPRESS_LIMITED' => 'PAL Express Limited',
381
+ 'PANDU_LOGISTICS' => 'Pandu Logistics',
382
+ 'PANTHER' => 'Panther',
383
+ 'PANTHER_ORDER_NUMBER' => 'Panther Order Number',
384
+ 'PANTHER_REFERENCE' => 'Panther Reference',
385
+ 'PAQUETEXPRESS' => 'Paquetexpress',
386
+ 'PARCELLEDIN' => 'Parcelled.in',
387
+ 'PARCELPOINT_PTY_LTD' => 'ParcelPoint Pty Ltd',
388
+ 'PARCEL_FORCE' => 'Parcel Force',
389
+ 'PARCEL_POST_SINGAPORE' => 'Parcel Post Singapore',
390
+ 'PAYPAL_PACKAGE' => 'PayPal Package',
391
+ 'PFC_EXPRESS' => 'PFC Express',
392
+ 'PICKUPP' => 'Pickupp',
393
+ 'PICK_UPP_MYS_SGP' => 'PICK UPP',
394
+ 'PILOT_FREIGHT_SERVICES' => 'Pilot Freight Services',
395
+ 'PITNEY_BOWES' => 'Pitney Bowes',
396
+ 'PIXSELL_LOGISTICS' => 'PIXSELL LOGISTICS',
397
+ 'POCZTA_POLSKA' => 'Poczta Polska',
398
+ 'PORTUGAL_CTT' => 'Portugal CTT',
399
+ 'PORTUGAL_SEUR' => 'Portugal Seur',
400
+ 'POST56' => 'Post56',
401
+ 'POSTEN_NORGE_BRING' => 'Posten Norge / Bring',
402
+ 'POSTE_ITALIANE' => 'Poste Italiane',
403
+ 'POSTE_ITALIANE_PACCOCELERE' => 'Poste Italiane Paccocelere',
404
+ 'POSTI' => 'Posti',
405
+ 'POSTNL_DOMESTIC' => 'PostNL Domestic',
406
+ 'POSTNL_INTERNATIONAL' => 'PostNL International',
407
+ 'POSTNL_INTERNATIONAL_3S' => 'PostNL International 3S',
408
+ 'POSTNORD_DENMARK' => 'PostNord Denmark',
409
+ 'POSTNORD_LOGISTICS' => 'PostNord Logistics',
410
+ 'POSTNORD_SWEDEN' => 'PostNord Sweden',
411
+ 'POST_OF_SLOVENIA' => 'Post of Slovenia',
412
+ 'POST_SERBIA' => 'Post Serbia',
413
+ 'POS_INDONESIA_DOMESTIC' => 'Pos Indonesia Domestic',
414
+ 'POS_INDONESIA_INTL' => "Pos Indonesia Int'l",
415
+ 'POSTA_ROMANA' => 'Poșta Română',
416
+ 'PROFESSIONAL_COURIERS' => 'Professional Couriers',
417
+ 'PTT_POSTA' => 'PTT Posta',
418
+ 'QUALITYPOST' => 'QualityPost',
419
+ 'QUANTIUM' => 'Quantium',
420
+ 'QXPRESS' => 'Qxpress',
421
+ 'RABEN_GROUP' => 'Raben Group',
422
+ 'RAF_PHILIPPINES' => 'RAF Philippines',
423
+ 'RAIDEREX' => 'RaidereX',
424
+ 'RAM' => 'RAM',
425
+ 'REDUR_SPAIN' => 'Redur Spain',
426
+ 'RED_CARPET_LOGISTICS' => 'Red Carpet Logistics',
427
+ 'RINCOS' => 'Rincos',
428
+ 'RL_CARRIERS' => 'RL Carriers',
429
+ 'ROADBULL_LOGISTICS' => 'Roadbull Logistics',
430
+ 'ROCKET_PARCEL_INTERNATIONAL' => 'Rocket Parcel International',
431
+ 'RPD2MAN_DELIVERIES' => 'RPD2man Deliveries',
432
+ 'RPX_INDONESIA' => 'RPX Indonesia',
433
+ 'RPX_ONLINE' => 'RPX Online',
434
+ 'RRD_INTERNATIONAL_LOGISTICS_USA' => 'RRD International Logistics U.S.A',
435
+ 'RUSSIAN_POST' => 'Russian Post',
436
+ 'RUSTON' => 'Ruston',
437
+ 'RZY_EXPRESS' => 'RZY Express',
438
+ 'SAFEXPRESS' => 'Safexpress',
439
+ 'SAGAWA' => 'Sagawa',
440
+ 'SAIA_LTL_FREIGHT' => 'Saia LTL Freight',
441
+ 'SAILPOST' => 'SAILPOST',
442
+ 'SAP_EXPRESS' => 'SAP EXPRESS',
443
+ 'SAUDI_POST' => 'Saudi Post',
444
+ 'SCUDEX_EXPRESS' => 'Scudex Express',
445
+ 'SEINO' => 'Seino',
446
+ 'SEKO_LOGISTICS' => 'SEKO Logistics',
447
+ 'SENDING_TRANSPORTE_URGENTE_Y_COMUNICACION_SAU' => 'Sending Transporte Urgente y Comunicacion, S.A.U',
448
+ 'SENDIT' => 'Sendit',
449
+ 'SENDLE' => 'Sendle',
450
+ 'SFC_SERVICE' => 'SFC Service',
451
+ 'SF_EXPRESS' => 'S.F. Express',
452
+ 'SF_INTERNATIONAL' => 'S.F International',
453
+ 'SGT_CORRIERE_ESPRESSO' => 'SGT Corriere Espresso',
454
+ 'SHANGHAI_WISE_SUPPLY_CHAIN_MANAGEMENT_CO_LTD' => '上海万色供应链管理有限公司(原:上海万色速递有限公司) Shanghai Wise Supply Chain Management Co., Ltd',
455
+ 'SHENZHEN_JINGHUADA_LOGISTICS_CO_LTD' => 'Shenzhen Jinghuada Logistics Co., Ltd',
456
+ 'SHIPPIT' => 'Shippit',
457
+ 'SHIPTOR' => 'Shiptor',
458
+ 'SHOPFANSRU_LLC' => 'ShopfansRU LLC',
459
+ 'SHREE_MARUTI_COURIER_SERVICES_PVT_LTD' => 'Shree Maruti Courier Services Pvt Ltd',
460
+ 'SHREE_TIRUPATI_COURIER_SERVICES_PVT_LTD' => 'SHREE TIRUPATI COURIER SERVICES PVT. LTD.',
461
+ 'SHUNYOU_POST' => 'Shunyou Post',
462
+ 'SIMPLYPOST' => 'SimplyPost',
463
+ 'SINGAPORE_POST' => 'Singapore Post',
464
+ 'SINGAPORE_SPEEDPOST' => 'Singapore Speedpost',
465
+ 'SIODEMKA' => 'Siodemka',
466
+ 'SKYBOX' => 'SKYBOX',
467
+ 'SKYNET_MALAYSIA' => 'SkyNet Malaysia',
468
+ 'SKYNET_WORLDWIDE_EXPRESS' => 'SkyNet Worldwide Express',
469
+ 'SKYNET_WORLDWIDE_EXPRESS_UAE' => 'SkyNet Worldwide Express UAE',
470
+ 'SKYNET_WORLDWIDE_EXPRESS_UK' => 'Skynet Worldwide Express UK',
471
+ 'SKYNET_WORLD_WIDE_EXPRESS_SOUTH_AFRICA' => 'Skynet World Wide Express South Africa',
472
+ 'SKYPOSTAL' => 'SkyPostal',
473
+ 'SMOOTH_COURIERS' => 'Smooth Couriers',
474
+ 'SMSA_EXPRESS' => 'SMSA Express',
475
+ 'SOUTH_AFRICAN_POST_OFFICE' => 'South African Post Office',
476
+ 'SPANISH_SEUR' => 'Spanish Seur',
477
+ 'SPECIALISED_FREIGHT' => 'Specialised Freight',
478
+ 'SPEEDEX_COURIER' => 'Speedex Courier',
479
+ 'SPEED_COURIERS' => 'Speed Couriers',
480
+ 'SPOTON_LOGISTICS_PVT_LTD' => 'SPOTON Logistics Pvt Ltd',
481
+ 'SRE_KOREA' => 'SRE Korea',
482
+ 'STARTRACK' => 'StarTrack',
483
+ 'STAR_TRACK_COURIER' => 'Star Track Courier',
484
+ 'STAR_TRACK_EXPRESS' => 'Star Track Express',
485
+ 'STO_EXPRESS' => 'STO Express',
486
+ 'SWISS_POST' => 'Swiss Post',
487
+ 'TAIWAN_POST' => 'Taiwan Post',
488
+ 'TAQBIN_HONG_KONG' => 'TAQBIN Hong Kong',
489
+ 'TAQBIN_MALAYSIA' => 'TAQBIN Malaysia',
490
+ 'TAQBIN_SINGAPORE' => 'TAQBIN Singapore',
491
+ 'TCS' => 'TCS',
492
+ 'TELIWAY_SIC_EXPRESS' => 'Teliway SIC Express',
493
+ 'THAILAND_THAI_POST' => 'Thailand Thai Post',
494
+ 'THE_COURIER_GUY' => 'The Courier Guy',
495
+ 'TIKI' => 'Tiki',
496
+ 'TIPSA' => 'TIPSA',
497
+ 'TNT_AUSTRALIA' => 'TNT Australia',
498
+ 'TNT_CLICK_ITALY' => 'TNT-Click Italy',
499
+ 'TNT_FRANCE' => 'TNT France',
500
+ 'TNT_ITALY' => 'TNT Italy',
501
+ 'TNT_REFERENCE' => 'TNT Reference',
502
+ 'TNT_UK_REFERENCE' => 'TNT UK Reference',
503
+ 'TOLL_IPEC' => 'Toll IPEC',
504
+ 'TOLL_PRIORITY' => 'Toll Priority',
505
+ 'TOLOS' => 'Tolos',
506
+ 'TRAKPAK' => 'TrakPak',
507
+ 'TRANSMISSION' => 'TransMission',
508
+ 'TRANS_KARGO_INTERNASIONAL' => 'Trans Kargo Internasional',
509
+ 'TUFFNELLS_PARCELS_EXPRESS' => 'Tuffnells Parcels Express',
510
+ 'UBI_SMART_PARCEL' => 'UBI Smart Parcel',
511
+ 'UKRPOSHTA' => 'UkrPoshta',
512
+ 'UK_MAIL' => 'UK Mail',
513
+ 'UNITED_DELIVERY_SERVICE_LTD' => 'United Delivery Service, Ltd',
514
+ 'UPS_FREIGHT' => 'UPS Freight',
515
+ 'UPS_MAIL_INNOVATIONS' => 'UPS Mail Innovations',
516
+ 'VIETNAM_POST' => 'Vietnam Post',
517
+ 'VIETNAM_POST_EMS' => 'Vietnam Post EMS',
518
+ 'VIETTELPOST' => 'ViettelPost',
519
+ 'WAHANA' => 'Wahana',
520
+ 'WANBEXPRESS' => 'WanbExpress',
521
+ 'WEDO_LOGISTICS' => 'WeDo Logistics',
522
+ 'WEPOST_LOGISTICS' => 'WePost Logistics',
523
+ 'WHISTL' => 'Whistl',
524
+ 'WISELOADS' => 'Wiseloads',
525
+ 'WISE_EXPRESS' => 'Wise Express',
526
+ 'WISHPOST' => 'WishPost',
527
+ 'WNDIRECT' => 'wnDirect',
528
+ 'XDP_EXPRESS' => 'XDP Express',
529
+ 'XDP_EXPRESS_REFERENCE' => 'XDP Express Reference',
530
+ 'XEND_EXPRESS' => 'Xend Express',
531
+ 'XL_EXPRESS' => 'XL Express',
532
+ 'XPOSTPH' => 'Xpost.ph',
533
+ 'XPRESSBEES' => 'XpressBees',
534
+ 'XQ_EXPRESS' => 'XQ Express',
535
+ 'YAKIT' => 'Yakit',
536
+ 'YAMATO_JAPAN' => 'Yamato Japan',
537
+ 'YANWEN' => 'Yanwen',
538
+ 'YODEL_DOMESTIC' => 'Yodel Domestic',
539
+ 'YODEL_INTERNATIONAL' => 'Yodel International',
540
+ 'YRC' => 'YRC',
541
+ 'YTO_EXPRESS' => 'YTO Express',
542
+ 'YUNDA_EXPRESS' => 'Yunda Express',
543
+ 'YUN_EXPRESS' => 'Yun Express',
544
+ 'ZEPTOEXPRESS' => 'ZeptoExpress',
545
+ 'ZINC' => 'Zinc',
546
+ 'ZJS_INTERNATIONAL' => 'ZJS International',
547
+ 'ZTO_EXPRESS' => 'ZTO Express',
548
+ 'ZYLLEM' => 'Zyllem',
549
+ );
550
 
551
  /**
552
  * @see https://docs.woocommerce.com/document/shipment-tracking/#section-5
553
  */
554
+ $this->shipment_tracking_carriers = array(
555
  // Australia
556
  'Australia Post' => 'AUSTRALIA_POST',
557
  'Fastway Couriers' => 'FASTWAY_AUSTRALIA',
630
  'UPS' => 'UPS',
631
  'USPS' => 'USPS',
632
  'DHL US' => 'DHL_ECOMMERCE_US', // TODO: may be DHL
633
+ );
634
  }
635
 
636
 
673
 
674
  if ( isset( $this->shipment_tracking_carriers[ $carrier ] ) ) {
675
  $carrier_code = $this->shipment_tracking_carriers[ $carrier ];
676
+ } elseif ( in_array( $carrier, $this->get_carrier_options(), true ) ) {
677
  $carrier_code = array_search( $carrier, $this->get_carrier_options(), true );
678
  } elseif ( array_key_exists( $carrier, $this->get_carrier_options() ) ) {
679
  $carrier_code = $carrier;
includes/Utilities/Tracker.php CHANGED
@@ -48,7 +48,7 @@ class Tracker {
48
  // Is the site connected?
49
  // @since 2.3.4
50
  $connection_is_happy = false;
51
- $connection_handler = facebook_for_woocommerce()->get_connection_handler();
52
  if ( $connection_handler ) {
53
  $connection_is_happy = $connection_handler->is_connected() && ! get_transient( 'wc_facebook_connection_invalid' );
54
  }
48
  // Is the site connected?
49
  // @since 2.3.4
50
  $connection_is_happy = false;
51
+ $connection_handler = facebook_for_woocommerce()->get_connection_handler();
52
  if ( $connection_handler ) {
53
  $connection_is_happy = $connection_handler->is_connected() && ! get_transient( 'wc_facebook_connection_invalid' );
54
  }
includes/fbgraph.php CHANGED
@@ -52,12 +52,12 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
52
 
53
  $api_key = $api_key ?: $this->api_key;
54
 
55
- $request_args = [
56
- 'headers' => [
57
  'Authorization' => 'Bearer ' . $api_key,
58
- ],
59
  'timeout' => self::CURL_TIMEOUT,
60
- ];
61
 
62
  $response = wp_remote_get( $url, $request_args );
63
 
@@ -80,12 +80,12 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
80
  */
81
  public function perform_request( $url ) {
82
 
83
- $request_args = [
84
- 'headers' => [
85
  'Authorization' => 'Bearer ' . $this->api_key,
86
- ],
87
  'timeout' => self::CURL_TIMEOUT,
88
- ];
89
 
90
  $response = wp_remote_get( $url, $request_args );
91
 
@@ -102,7 +102,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
102
  if ( isset( $response_body->error->code, $response_body->error->message ) ) {
103
  throw new Framework\SV_WC_API_Exception( $response_body->error->message, $response_body->error->code );
104
  } else {
105
- throw new Framework\SV_WC_API_Exception( sprintf( __( 'HTTP %s: %s', 'facebook-for-woocommerce' ), wp_remote_retrieve_response_code( $response ), wp_remote_retrieve_response_message( $response ) ) );
106
  }
107
  }
108
 
@@ -121,13 +121,13 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
121
  public function _post_sync( $url, $data, $api_key = '' ) {
122
  $api_key = $api_key ?: $this->api_key;
123
 
124
- $request_args = [
125
  'body' => $data,
126
- 'headers' => [
127
  'Authorization' => 'Bearer ' . $api_key,
128
- ],
129
  'timeout' => self::CURL_TIMEOUT,
130
- ];
131
 
132
  $response = wp_remote_post( $url, $request_args );
133
 
@@ -141,7 +141,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
141
  * Issues an asynchronous POST request to the Graph API.
142
  *
143
  * @param string $url request URL
144
- * @param array $data request data
145
  * @param string $api_key Graph API key
146
  * @return array|\WP_Error
147
  */
@@ -153,16 +153,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
153
 
154
  $api_key = $api_key ?: $this->api_key;
155
 
156
- $request_args = [
157
  'body' => $data,
158
- 'headers' => [
159
  'Authorization' => 'Bearer ' . $api_key,
160
- ],
161
  'timeout' => self::CURL_TIMEOUT,
162
- ];
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;
@@ -186,13 +185,13 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
186
 
187
  $api_key = $api_key ?: $this->api_key;
188
 
189
- $request_args = [
190
- 'headers' => [
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
 
@@ -210,7 +209,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
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
 
@@ -220,9 +219,12 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
220
  }
221
 
222
  // add the URI to the data
223
- $request_data = array_merge( [
224
- 'uri' => $url,
225
- ], $request_args );
 
 
 
226
 
227
  // the request args may not include the method, so allow it to be set
228
  if ( $method ) {
@@ -242,7 +244,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
242
 
243
  $code = $response->get_error_code();
244
  $message = $response->get_error_message();
245
- $headers = [];
246
  $body = '';
247
 
248
  } else {
@@ -252,7 +254,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
252
  if ( is_object( $headers ) ) {
253
  $headers = $headers->getAll();
254
  } elseif ( ! is_array( $headers ) ) {
255
- $headers = [];
256
  }
257
 
258
  $code = wp_remote_retrieve_response_code( $response );
@@ -260,12 +262,12 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
260
  $body = wp_remote_retrieve_body( $response );
261
  }
262
 
263
- $response_data = [
264
  'code' => $code,
265
  'message' => $message,
266
  'headers' => $headers,
267
  'body' => $body,
268
- ];
269
 
270
  facebook_for_woocommerce()->log_api_request( $request_data, $response_data );
271
  }
@@ -298,7 +300,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
298
  * Endpoint: https://graph.facebook.com/vX.X/{page-id}/?fields=link
299
  *
300
  * @param string|int $page_id page identifier
301
- * @param string $api_key API key
302
  * @return string URL
303
  */
304
  public function get_page_url( $page_id, $api_key = '' ) {
@@ -441,15 +443,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
441
  }
442
 
443
  public function create_upload( $facebook_feed_id, $path_to_feed_file ) {
444
- $url = $this->build_url(
445
  $facebook_feed_id,
446
  '/uploads?access_token=' . $this->api_key
447
  );
448
 
449
- $data = [
450
  'file' => new CurlFile( $path_to_feed_file, 'text/csv' ),
451
  'update_only' => true,
452
- ];
453
 
454
  $curl = curl_init();
455
  curl_setopt_array(
52
 
53
  $api_key = $api_key ?: $this->api_key;
54
 
55
+ $request_args = array(
56
+ 'headers' => array(
57
  'Authorization' => 'Bearer ' . $api_key,
58
+ ),
59
  'timeout' => self::CURL_TIMEOUT,
60
+ );
61
 
62
  $response = wp_remote_get( $url, $request_args );
63
 
80
  */
81
  public function perform_request( $url ) {
82
 
83
+ $request_args = array(
84
+ 'headers' => array(
85
  'Authorization' => 'Bearer ' . $this->api_key,
86
+ ),
87
  'timeout' => self::CURL_TIMEOUT,
88
+ );
89
 
90
  $response = wp_remote_get( $url, $request_args );
91
 
102
  if ( isset( $response_body->error->code, $response_body->error->message ) ) {
103
  throw new Framework\SV_WC_API_Exception( $response_body->error->message, $response_body->error->code );
104
  } else {
105
+ 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 ) ) );
106
  }
107
  }
108
 
121
  public function _post_sync( $url, $data, $api_key = '' ) {
122
  $api_key = $api_key ?: $this->api_key;
123
 
124
+ $request_args = array(
125
  'body' => $data,
126
+ 'headers' => array(
127
  'Authorization' => 'Bearer ' . $api_key,
128
+ ),
129
  'timeout' => self::CURL_TIMEOUT,
130
+ );
131
 
132
  $response = wp_remote_post( $url, $request_args );
133
 
141
  * Issues an asynchronous POST request to the Graph API.
142
  *
143
  * @param string $url request URL
144
+ * @param array $data request data
145
  * @param string $api_key Graph API key
146
  * @return array|\WP_Error
147
  */
153
 
154
  $api_key = $api_key ?: $this->api_key;
155
 
156
+ $request_args = array(
157
  'body' => $data,
158
+ 'headers' => array(
159
  'Authorization' => 'Bearer ' . $api_key,
160
+ ),
161
  'timeout' => self::CURL_TIMEOUT,
162
+ );
 
163
 
164
+ $fbasync = new WC_Facebookcommerce_Async_Request();
165
  $fbasync->query_url = $url;
166
  $fbasync->query_args = array();
167
  $fbasync->post_args = $request_args;
185
 
186
  $api_key = $api_key ?: $this->api_key;
187
 
188
+ $request_args = array(
189
+ 'headers' => array(
190
  'Authorization' => 'Bearer ' . $api_key,
191
+ ),
192
  'timeout' => self::CURL_TIMEOUT,
193
  'method' => 'DELETE',
194
+ );
195
 
196
  $response = wp_remote_request( $url, $request_args );
197
 
209
  * @param $url
210
  * @param $request_args
211
  * @param array|\WP_Error $response WordPress response object
212
+ * @param string $method
213
  */
214
  private function log_request( $url, $request_args, $response, $method = '' ) {
215
 
219
  }
220
 
221
  // add the URI to the data
222
+ $request_data = array_merge(
223
+ array(
224
+ 'uri' => $url,
225
+ ),
226
+ $request_args
227
+ );
228
 
229
  // the request args may not include the method, so allow it to be set
230
  if ( $method ) {
244
 
245
  $code = $response->get_error_code();
246
  $message = $response->get_error_message();
247
+ $headers = array();
248
  $body = '';
249
 
250
  } else {
254
  if ( is_object( $headers ) ) {
255
  $headers = $headers->getAll();
256
  } elseif ( ! is_array( $headers ) ) {
257
+ $headers = array();
258
  }
259
 
260
  $code = wp_remote_retrieve_response_code( $response );
262
  $body = wp_remote_retrieve_body( $response );
263
  }
264
 
265
+ $response_data = array(
266
  'code' => $code,
267
  'message' => $message,
268
  'headers' => $headers,
269
  'body' => $body,
270
+ );
271
 
272
  facebook_for_woocommerce()->log_api_request( $request_data, $response_data );
273
  }
300
  * Endpoint: https://graph.facebook.com/vX.X/{page-id}/?fields=link
301
  *
302
  * @param string|int $page_id page identifier
303
+ * @param string $api_key API key
304
  * @return string URL
305
  */
306
  public function get_page_url( $page_id, $api_key = '' ) {
443
  }
444
 
445
  public function create_upload( $facebook_feed_id, $path_to_feed_file ) {
446
+ $url = $this->build_url(
447
  $facebook_feed_id,
448
  '/uploads?access_token=' . $this->api_key
449
  );
450
 
451
+ $data = array(
452
  'file' => new CurlFile( $path_to_feed_file, 'text/csv' ),
453
  'update_only' => true,
454
+ );
455
 
456
  $curl = curl_init();
457
  curl_setopt_array(
includes/fbproduct.php CHANGED
@@ -430,7 +430,7 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
430
  // Convert all slug_names in $option_values into the visible names that
431
  // advertisers have set to be the display names for a given attribute value
432
  $terms = get_the_terms( $this->id, $key );
433
- return ! is_array( $terms ) ? [] : array_map(
434
  function ( $slug_name ) use ( $terms ) {
435
  foreach ( $terms as $term ) {
436
  if ( $term->slug === $slug_name ) {
@@ -695,20 +695,21 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
695
  * Filters list of attributes to only those available for a given product
696
  *
697
  * @param \WC_Product $product WooCommerce Product
698
- * @param array $all_attributes List of Enhanced Catalog attributes to match
699
  * @return array
700
  */
701
  public function get_matched_attributes_for_product( $product, $all_attributes ) {
702
  $matched_attributes = array();
703
- $sanitized_keys = array_map(
704
  function( $key ) {
705
  return \WC_Facebookcommerce_Utils::sanitize_variant_name( $key, false );
706
  },
707
  array_keys( $product->get_attributes() )
708
  );
709
 
710
- $matched_attributes = array_filter( $all_attributes,
711
- function( $attribute ) use ($sanitized_keys) {
 
712
  return in_array( $attribute['key'], $sanitized_keys );
713
  }
714
  );
430
  // Convert all slug_names in $option_values into the visible names that
431
  // advertisers have set to be the display names for a given attribute value
432
  $terms = get_the_terms( $this->id, $key );
433
+ return ! is_array( $terms ) ? array() : array_map(
434
  function ( $slug_name ) use ( $terms ) {
435
  foreach ( $terms as $term ) {
436
  if ( $term->slug === $slug_name ) {
695
  * Filters list of attributes to only those available for a given product
696
  *
697
  * @param \WC_Product $product WooCommerce Product
698
+ * @param array $all_attributes List of Enhanced Catalog attributes to match
699
  * @return array
700
  */
701
  public function get_matched_attributes_for_product( $product, $all_attributes ) {
702
  $matched_attributes = array();
703
+ $sanitized_keys = array_map(
704
  function( $key ) {
705
  return \WC_Facebookcommerce_Utils::sanitize_variant_name( $key, false );
706
  },
707
  array_keys( $product->get_attributes() )
708
  );
709
 
710
+ $matched_attributes = array_filter(
711
+ $all_attributes,
712
+ function( $attribute ) use ( $sanitized_keys ) {
713
  return in_array( $attribute['key'], $sanitized_keys );
714
  }
715
  );
includes/fbproductfeed.php CHANGED
@@ -46,9 +46,9 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
46
  /**
47
  * WC_Facebook_Product_Feed constructor.
48
  *
49
- * @param string|null $facebook_catalog_id Facebook catalog ID, if any
50
  * @param \WC_Facebookcommerce_Graph_API|null $fbgraph Facebook Graph API instance
51
- * @param string|null $feed_id Facebook feed ID, if any
52
  */
53
  public function __construct( $facebook_catalog_id = null, $fbgraph = null, $feed_id = null ) {
54
 
@@ -66,7 +66,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
66
  public function schedule_feed_generation() {
67
 
68
  // don't schedule another if one's already scheduled or in progress
69
- if ( false !== as_next_scheduled_action( 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' ) ) {
70
  return;
71
  }
72
 
@@ -74,9 +74,9 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
74
 
75
  // if async priority actions are supported (AS 3.0+)
76
  if ( function_exists( 'as_enqueue_async_action' ) ) {
77
- as_enqueue_async_action( 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' );
78
  } else {
79
- as_schedule_single_action( time(), 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' );
80
  }
81
  }
82
 
@@ -210,7 +210,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
210
  $time_estimate = $buffer_time;
211
  }
212
 
213
- WC_Facebookcommerce_Utils::log( 'Feed Generation Time Estimate: '. $time_estimate );
214
 
215
  return $time_estimate;
216
  }
@@ -381,7 +381,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
381
  if ( ! $this->generate_productfeed_file() ) {
382
  throw new Framework\SV_WC_Plugin_Exception( 'Feed file not generated' );
383
  }
384
-
385
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
386
 
387
  $this->log_feed_progress(
@@ -481,18 +480,18 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
481
 
482
  $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
483
 
484
- $files = [
485
- [
486
  'base' => $catalog_feed_directory,
487
  'file' => 'index.html',
488
  'content' => '',
489
- ],
490
- [
491
  'base' => $catalog_feed_directory,
492
  'file' => '.htaccess',
493
  'content' => 'deny from all',
494
- ],
495
- ];
496
 
497
  foreach ( $files as $file ) {
498
 
@@ -514,14 +513,13 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
514
  * @since 1.11.0
515
  *
516
  * @param int[] $wp_ids product IDs
517
- * @param bool $is_dry_run whether this is a dry run or the file should be written
518
  * @return bool
519
  */
520
  public function write_product_feed_file( $wp_ids, $is_dry_run = false ) {
521
 
522
  try {
523
 
524
-
525
  if ( ! $is_dry_run ) {
526
 
527
  $temp_file_path = $this->get_temp_file_path();
@@ -570,7 +568,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
570
 
571
  wp_reset_postdata();
572
 
573
-
574
  if ( ! empty( $temp_feed_file ) ) {
575
  fclose( $temp_feed_file );
576
  }
@@ -620,7 +617,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
620
  * Assembles product payload in feed upload for initial sync.
621
  *
622
  * @param \WC_Facebook_Product $woo_product WooCommerce product object normalized by Facebook
623
- * @param array $attribute_variants passed by reference
624
  * @return string product feed line data
625
  */
626
  private function prepare_product_for_feed( $woo_product, &$attribute_variants ) {
@@ -641,11 +638,11 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
641
  $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
642
  $variation_id = $parent_product->find_matching_product_variation();
643
  $variants_for_group = $parent_product->prepare_variants_for_group( true );
644
- $parent_attribute_values = [
645
  'gallery_urls' => $gallery_urls,
646
  'default_variant_id' => $variation_id,
647
  'item_group_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product ),
648
- ];
649
 
650
  foreach ( $variants_for_group as $variant ) {
651
  if ( isset( $variant['product_field'], $variant['options'] ) ) {
@@ -661,8 +658,8 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
661
  $parent_attribute_values = $attribute_variants[ $parent_id ];
662
  }
663
 
664
- $variants_for_item = $woo_product->prepare_variants_for_item( $product_data );
665
- $variant_feed_column = [];
666
 
667
  foreach ( $variants_for_item as $variant_array ) {
668
 
@@ -839,9 +836,9 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
839
  *
840
  * @since 2.1.0
841
  *
842
- * @param array $product_data the product data retrieved from a Woo product passed by reference
843
  * @param string $index the data index
844
- * @param mixed $return_if_not_set the value to be returned if product data has no index (default to '')
845
  * @return mixed|string the data value or an empty string
846
  */
847
  private static function get_value_from_product_data( &$product_data, $index, $return_if_not_set = '' ) {
@@ -879,7 +876,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
879
 
880
  $upload_status = 'complete';
881
 
882
- } else if ( 200 === (int) wp_remote_retrieve_response_code( $result ) ) {
883
 
884
  $upload_status = 'in progress';
885
  }
46
  /**
47
  * WC_Facebook_Product_Feed constructor.
48
  *
49
+ * @param string|null $facebook_catalog_id Facebook catalog ID, if any
50
  * @param \WC_Facebookcommerce_Graph_API|null $fbgraph Facebook Graph API instance
51
+ * @param string|null $feed_id Facebook feed ID, if any
52
  */
53
  public function __construct( $facebook_catalog_id = null, $fbgraph = null, $feed_id = null ) {
54
 
66
  public function schedule_feed_generation() {
67
 
68
  // don't schedule another if one's already scheduled or in progress
69
+ if ( false !== as_next_scheduled_action( 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' ) ) {
70
  return;
71
  }
72
 
74
 
75
  // if async priority actions are supported (AS 3.0+)
76
  if ( function_exists( 'as_enqueue_async_action' ) ) {
77
+ as_enqueue_async_action( 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' );
78
  } else {
79
+ as_schedule_single_action( time(), 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' );
80
  }
81
  }
82
 
210
  $time_estimate = $buffer_time;
211
  }
212
 
213
+ WC_Facebookcommerce_Utils::log( 'Feed Generation Time Estimate: ' . $time_estimate );
214
 
215
  return $time_estimate;
216
  }
381
  if ( ! $this->generate_productfeed_file() ) {
382
  throw new Framework\SV_WC_Plugin_Exception( 'Feed file not generated' );
383
  }
 
384
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
385
 
386
  $this->log_feed_progress(
480
 
481
  $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
482
 
483
+ $files = array(
484
+ array(
485
  'base' => $catalog_feed_directory,
486
  'file' => 'index.html',
487
  'content' => '',
488
+ ),
489
+ array(
490
  'base' => $catalog_feed_directory,
491
  'file' => '.htaccess',
492
  'content' => 'deny from all',
493
+ ),
494
+ );
495
 
496
  foreach ( $files as $file ) {
497
 
513
  * @since 1.11.0
514
  *
515
  * @param int[] $wp_ids product IDs
516
+ * @param bool $is_dry_run whether this is a dry run or the file should be written
517
  * @return bool
518
  */
519
  public function write_product_feed_file( $wp_ids, $is_dry_run = false ) {
520
 
521
  try {
522
 
 
523
  if ( ! $is_dry_run ) {
524
 
525
  $temp_file_path = $this->get_temp_file_path();
568
 
569
  wp_reset_postdata();
570
 
 
571
  if ( ! empty( $temp_feed_file ) ) {
572
  fclose( $temp_feed_file );
573
  }
617
  * Assembles product payload in feed upload for initial sync.
618
  *
619
  * @param \WC_Facebook_Product $woo_product WooCommerce product object normalized by Facebook
620
+ * @param array $attribute_variants passed by reference
621
  * @return string product feed line data
622
  */
623
  private function prepare_product_for_feed( $woo_product, &$attribute_variants ) {
638
  $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
639
  $variation_id = $parent_product->find_matching_product_variation();
640
  $variants_for_group = $parent_product->prepare_variants_for_group( true );
641
+ $parent_attribute_values = array(
642
  'gallery_urls' => $gallery_urls,
643
  'default_variant_id' => $variation_id,
644
  'item_group_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product ),
645
+ );
646
 
647
  foreach ( $variants_for_group as $variant ) {
648
  if ( isset( $variant['product_field'], $variant['options'] ) ) {
658
  $parent_attribute_values = $attribute_variants[ $parent_id ];
659
  }
660
 
661
+ $variants_for_item = $woo_product->prepare_variants_for_item( $product_data );
662
+ $variant_feed_column = array();
663
 
664
  foreach ( $variants_for_item as $variant_array ) {
665
 
836
  *
837
  * @since 2.1.0
838
  *
839
+ * @param array $product_data the product data retrieved from a Woo product passed by reference
840
  * @param string $index the data index
841
+ * @param mixed $return_if_not_set the value to be returned if product data has no index (default to '')
842
  * @return mixed|string the data value or an empty string
843
  */
844
  private static function get_value_from_product_data( &$product_data, $index, $return_if_not_set = '' ) {
876
 
877
  $upload_status = 'complete';
878
 
879
+ } elseif ( 200 === (int) wp_remote_retrieve_response_code( $result ) ) {
880
 
881
  $upload_status = 'in progress';
882
  }
includes/fbutils.php CHANGED
@@ -211,40 +211,40 @@ if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
211
  */
212
  public static function get_user_info( $aam_settings ) {
213
  $current_user = wp_get_current_user();
214
- if ( 0 === $current_user->ID || $aam_settings == null || !$aam_settings->get_enable_automatic_matching() ) {
215
  // User not logged in or pixel not configured with automatic advance matching
216
  return array();
217
  } else {
218
  // Keys documented in
219
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
220
- $user_data = array(
221
- 'em' => $current_user->user_email,
222
- 'fn' => $current_user->user_firstname,
223
- 'ln' => $current_user->user_lastname,
224
- 'external_id' => strval($current_user->ID),
225
  );
226
- $user_id = $current_user->ID;
227
- $user_data['ct'] = get_user_meta($user_id, 'billing_city', true);
228
- $user_data['zp'] = get_user_meta($user_id, 'billing_postcode', true);
229
- $user_data['country'] = get_user_meta($user_id, 'billing_country', true);
230
- $user_data['st'] = get_user_meta($user_id, 'billing_state', true);
231
- $user_data['ph'] = get_user_meta($user_id, 'billing_phone', true);
232
  // Each field that is not present in AAM settings or is empty is deleted from user data
233
- foreach ($user_data as $field => $value) {
234
- if( $value === null || $value === ''
235
- || !in_array($field, $aam_settings->get_enabled_automatic_matching_fields())
236
- ){
237
- unset($user_data[$field]);
238
  }
239
  }
240
  // Country is a special case, it is returned as country in AAM settings
241
  // But used as cn in pixel
242
- if(array_key_exists('country', $user_data)){
243
- $country = $user_data['country'];
244
  $user_data['cn'] = $country;
245
- unset($user_data['country']);
246
  }
247
- $user_data = Normalizer::normalize_array($user_data, true);
248
  return $user_data;
249
  }
250
  }
211
  */
212
  public static function get_user_info( $aam_settings ) {
213
  $current_user = wp_get_current_user();
214
+ if ( 0 === $current_user->ID || $aam_settings == null || ! $aam_settings->get_enable_automatic_matching() ) {
215
  // User not logged in or pixel not configured with automatic advance matching
216
  return array();
217
  } else {
218
  // Keys documented in
219
  // https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching
220
+ $user_data = array(
221
+ 'em' => $current_user->user_email,
222
+ 'fn' => $current_user->user_firstname,
223
+ 'ln' => $current_user->user_lastname,
224
+ 'external_id' => strval( $current_user->ID ),
225
  );
226
+ $user_id = $current_user->ID;
227
+ $user_data['ct'] = get_user_meta( $user_id, 'billing_city', true );
228
+ $user_data['zp'] = get_user_meta( $user_id, 'billing_postcode', true );
229
+ $user_data['country'] = get_user_meta( $user_id, 'billing_country', true );
230
+ $user_data['st'] = get_user_meta( $user_id, 'billing_state', true );
231
+ $user_data['ph'] = get_user_meta( $user_id, 'billing_phone', true );
232
  // Each field that is not present in AAM settings or is empty is deleted from user data
233
+ foreach ( $user_data as $field => $value ) {
234
+ if ( $value === null || $value === ''
235
+ || ! in_array( $field, $aam_settings->get_enabled_automatic_matching_fields() )
236
+ ) {
237
+ unset( $user_data[ $field ] );
238
  }
239
  }
240
  // Country is a special case, it is returned as country in AAM settings
241
  // But used as cn in pixel
242
+ if ( array_key_exists( 'country', $user_data ) ) {
243
+ $country = $user_data['country'];
244
  $user_data['cn'] = $country;
245
+ unset( $user_data['country'] );
246
  }
247
+ $user_data = Normalizer::normalize_array( $user_data, true );
248
  return $user_data;
249
  }
250
  }
includes/fbwpml.php CHANGED
@@ -35,8 +35,8 @@ if ( ! class_exists( 'WC_Facebook_WPML_Injector' ) ) :
35
  self::$default_lang = apply_filters( 'wpml_default_language', null );
36
 
37
  if ( is_admin() ) {
38
- add_action( 'icl_menu_footer', [ $this, 'wpml_support' ] );
39
- add_action( 'icl_ajx_custom_call', [ $this, 'wpml_ajax_support' ], 10, 2 );
40
  }
41
  }
42
 
35
  self::$default_lang = apply_filters( 'wpml_default_language', null );
36
 
37
  if ( is_admin() ) {
38
+ add_action( 'icl_menu_footer', array( $this, 'wpml_support' ) );
39
+ add_action( 'icl_ajx_custom_call', array( $this, 'wpml_ajax_support' ), 10, 2 );
40
  }
41
  }
42
 
includes/test/facebook-integration-test.php CHANGED
@@ -39,7 +39,7 @@ if ( ! class_exists( 'WC_Facebook_Integration_Test' ) ) :
39
  private static $instance;
40
 
41
  /** @var WC_Facebookcommerce_Integration full integration object */
42
- public static $commerce = null;
43
  /** @var WC_Facebookcommerce_Graph_API */
44
  public static $fbgraph = null;
45
  public static $test_mode = false;
39
  private static $instance;
40
 
41
  /** @var WC_Facebookcommerce_Integration full integration object */
42
+ public static $commerce = null;
43
  /** @var WC_Facebookcommerce_Graph_API */
44
  public static $fbgraph = null;
45
  public static $test_mode = false;
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: facebook, automattic, woothemes
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
  Tested up to: 5.6
6
- Stable tag: 2.4.1
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
@@ -39,7 +39,21 @@ When opening a bug on GitHub, please give us as many details as possible.
39
 
40
  == Changelog ==
41
 
42
- = 2021.04.29 - version 2.4.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  * Fix - PHP<7.1 incompatible code for Google Taxonomy Setting in products.
44
 
45
  = 2021.04.23 - version 2.4.0 =
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
  Tested up to: 5.6
6
+ Stable tag: 2.5.0
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
39
 
40
  == Changelog ==
41
 
42
+ = 2.5.0 - 2021-05-19 =
43
+ * New - Option to allow larger sites to opt-out of feed generation (product sync) job
44
+ * New - Log connection errors to allow easier troubleshooting
45
+ * Fix - Reduce default feed generation (product sync) interval to once per day to reduce overhead
46
+ * Fix - Trigger feed (product sync) job from to `admin_init` to reduce impact on front-end requests
47
+ * Fix - Ensure variable product attribute values containing comma (`,`) sync correctly
48
+ * Fix - Use existing / current tab for connection `Get Started` button
49
+ * Dev - Require PHP version 7.0 or newer
50
+ * Dev - Adopt Composer autoloader to avoid manually `require`ing PHP class files
51
+ * Dev - Adopt WooRelease release tool for deploying releases
52
+ * Dev - Use wp-scripts to build assets
53
+ * Dev - Add `phpcs` tooling to help standardise PHP code style
54
+ * Dev - Add JobRegistry engine for managing periodic background batch jobs
55
+
56
+ = 2021.04.29 - version 2.4.1 =
57
  * Fix - PHP<7.1 incompatible code for Google Taxonomy Setting in products.
58
 
59
  = 2021.04.23 - version 2.4.0 =
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInite5a9cceb09137b76082a540c9a14d271::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see https://www.php-fig.org/psr/psr-0/
41
+ * @see https://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/InstalledVersions.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer;
4
+
5
+ use Composer\Semver\VersionParser;
6
+
7
+
8
+
9
+
10
+
11
+
12
+ class InstalledVersions
13
+ {
14
+ private static $installed = array (
15
+ 'root' =>
16
+ array (
17
+ 'pretty_version' => '2.5.0',
18
+ 'version' => '2.5.0.0',
19
+ 'aliases' =>
20
+ array (
21
+ ),
22
+ 'reference' => 'c221784785ff1288e22452fea34cc2b1674e7ef8',
23
+ 'name' => 'facebookincubator/facebook-for-woocommerce',
24
+ ),
25
+ 'versions' =>
26
+ array (
27
+ 'composer/installers' =>
28
+ array (
29
+ 'pretty_version' => 'v1.11.0',
30
+ 'version' => '1.11.0.0',
31
+ 'aliases' =>
32
+ array (
33
+ ),
34
+ 'reference' => 'ae03311f45dfe194412081526be2e003960df74b',
35
+ ),
36
+ 'facebookincubator/facebook-for-woocommerce' =>
37
+ array (
38
+ 'pretty_version' => '2.5.0',
39
+ 'version' => '2.5.0.0',
40
+ 'aliases' =>
41
+ array (
42
+ ),
43
+ 'reference' => 'c221784785ff1288e22452fea34cc2b1674e7ef8',
44
+ ),
45
+ 'roundcube/plugin-installer' =>
46
+ array (
47
+ 'replaced' =>
48
+ array (
49
+ 0 => '*',
50
+ ),
51
+ ),
52
+ 'shama/baton' =>
53
+ array (
54
+ 'replaced' =>
55
+ array (
56
+ 0 => '*',
57
+ ),
58
+ ),
59
+ 'skyverge/wc-plugin-framework' =>
60
+ array (
61
+ 'pretty_version' => '5.10.0',
62
+ 'version' => '5.10.0.0',
63
+ 'aliases' =>
64
+ array (
65
+ ),
66
+ 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
67
+ ),
68
+ 'woocommerce/action-scheduler-job-framework' =>
69
+ array (
70
+ 'pretty_version' => '1.0.0',
71
+ 'version' => '1.0.0.0',
72
+ 'aliases' =>
73
+ array (
74
+ ),
75
+ 'reference' => '718594e15d7ba2d56f8a37743bda6d7a8296e1c8',
76
+ ),
77
+ ),
78
+ );
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+ public static function getInstalledPackages()
87
+ {
88
+ return array_keys(self::$installed['versions']);
89
+ }
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+ public static function isInstalled($packageName)
100
+ {
101
+ return isset(self::$installed['versions'][$packageName]);
102
+ }
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
118
+ {
119
+ $constraint = $parser->parseConstraints($constraint);
120
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
121
+
122
+ return $provided->matches($constraint);
123
+ }
124
+
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+ public static function getVersionRanges($packageName)
135
+ {
136
+ if (!isset(self::$installed['versions'][$packageName])) {
137
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
138
+ }
139
+
140
+ $ranges = array();
141
+ if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
142
+ $ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
143
+ }
144
+ if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
145
+ $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
146
+ }
147
+ if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
148
+ $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
149
+ }
150
+ if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
151
+ $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
152
+ }
153
+
154
+ return implode(' || ', $ranges);
155
+ }
156
+
157
+
158
+
159
+
160
+
161
+ public static function getVersion($packageName)
162
+ {
163
+ if (!isset(self::$installed['versions'][$packageName])) {
164
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
165
+ }
166
+
167
+ if (!isset(self::$installed['versions'][$packageName]['version'])) {
168
+ return null;
169
+ }
170
+
171
+ return self::$installed['versions'][$packageName]['version'];
172
+ }
173
+
174
+
175
+
176
+
177
+
178
+ public static function getPrettyVersion($packageName)
179
+ {
180
+ if (!isset(self::$installed['versions'][$packageName])) {
181
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
182
+ }
183
+
184
+ if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
185
+ return null;
186
+ }
187
+
188
+ return self::$installed['versions'][$packageName]['pretty_version'];
189
+ }
190
+
191
+
192
+
193
+
194
+
195
+ public static function getReference($packageName)
196
+ {
197
+ if (!isset(self::$installed['versions'][$packageName])) {
198
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
199
+ }
200
+
201
+ if (!isset(self::$installed['versions'][$packageName]['reference'])) {
202
+ return null;
203
+ }
204
+
205
+ return self::$installed['versions'][$packageName]['reference'];
206
+ }
207
+
208
+
209
+
210
+
211
+
212
+ public static function getRootPackage()
213
+ {
214
+ return self::$installed['root'];
215
+ }
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+ public static function getRawData()
224
+ {
225
+ return self::$installed;
226
+ }
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+ public static function reload($data)
247
+ {
248
+ self::$installed = $data;
249
+ }
250
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
10
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
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
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInite5a9cceb09137b76082a540c9a14d271
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
+ public static function getLoader()
20
+ {
21
+ if (null !== self::$loader) {
22
+ return self::$loader;
23
+ }
24
+
25
+ require __DIR__ . '/platform_check.php';
26
+
27
+ spl_autoload_register(array('ComposerAutoloaderInite5a9cceb09137b76082a540c9a14d271', 'loadClassLoader'), true, true);
28
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
29
+ spl_autoload_unregister(array('ComposerAutoloaderInite5a9cceb09137b76082a540c9a14d271', 'loadClassLoader'));
30
+
31
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
+ if ($useStaticLoader) {
33
+ require __DIR__ . '/autoload_static.php';
34
+
35
+ call_user_func(\Composer\Autoload\ComposerStaticInite5a9cceb09137b76082a540c9a14d271::getInitializer($loader));
36
+ } else {
37
+ $map = require __DIR__ . '/autoload_namespaces.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->set($namespace, $path);
40
+ }
41
+
42
+ $map = require __DIR__ . '/autoload_psr4.php';
43
+ foreach ($map as $namespace => $path) {
44
+ $loader->setPsr4($namespace, $path);
45
+ }
46
+
47
+ $classMap = require __DIR__ . '/autoload_classmap.php';
48
+ if ($classMap) {
49
+ $loader->addClassMap($classMap);
50
+ }
51
+ }
52
+
53
+ $loader->register(true);
54
+
55
+ return $loader;
56
+ }
57
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInite5a9cceb09137b76082a540c9a14d271
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'S' =>
11
+ array (
12
+ 'SkyVerge\\WooCommerce\\Facebook\\' => 30,
13
+ ),
14
+ 'C' =>
15
+ array (
16
+ 'Composer\\Installers\\' => 20,
17
+ ),
18
+ 'A' =>
19
+ array (
20
+ 'Automattic\\WooCommerce\\ActionSchedulerJobFramework\\' => 51,
21
+ ),
22
+ );
23
+
24
+ public static $prefixDirsPsr4 = array (
25
+ 'SkyVerge\\WooCommerce\\Facebook\\' =>
26
+ array (
27
+ 0 => __DIR__ . '/../..' . '/includes',
28
+ ),
29
+ 'Composer\\Installers\\' =>
30
+ array (
31
+ 0 => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers',
32
+ ),
33
+ 'Automattic\\WooCommerce\\ActionSchedulerJobFramework\\' =>
34
+ array (
35
+ 0 => __DIR__ . '/..' . '/woocommerce/action-scheduler-job-framework/src',
36
+ ),
37
+ );
38
+
39
+ public static $classMap = array (
40
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
41
+ );
42
+
43
+ public static function getInitializer(ClassLoader $loader)
44
+ {
45
+ return \Closure::bind(function () use ($loader) {
46
+ $loader->prefixLengthsPsr4 = ComposerStaticInite5a9cceb09137b76082a540c9a14d271::$prefixLengthsPsr4;
47
+ $loader->prefixDirsPsr4 = ComposerStaticInite5a9cceb09137b76082a540c9a14d271::$prefixDirsPsr4;
48
+ $loader->classMap = ComposerStaticInite5a9cceb09137b76082a540c9a14d271::$classMap;
49
+
50
+ }, null, ClassLoader::class);
51
+ }
52
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "packages": [
3
+ {
4
+ "name": "composer/installers",
5
+ "version": "v1.11.0",
6
+ "version_normalized": "1.11.0.0",
7
+ "source": {
8
+ "type": "git",
9
+ "url": "https://github.com/composer/installers.git",
10
+ "reference": "ae03311f45dfe194412081526be2e003960df74b"
11
+ },
12
+ "dist": {
13
+ "type": "zip",
14
+ "url": "https://api.github.com/repos/composer/installers/zipball/ae03311f45dfe194412081526be2e003960df74b",
15
+ "reference": "ae03311f45dfe194412081526be2e003960df74b",
16
+ "shasum": ""
17
+ },
18
+ "require": {
19
+ "composer-plugin-api": "^1.0 || ^2.0"
20
+ },
21
+ "replace": {
22
+ "roundcube/plugin-installer": "*",
23
+ "shama/baton": "*"
24
+ },
25
+ "require-dev": {
26
+ "composer/composer": "1.6.* || ^2.0",
27
+ "composer/semver": "^1 || ^3",
28
+ "phpstan/phpstan": "^0.12.55",
29
+ "phpstan/phpstan-phpunit": "^0.12.16",
30
+ "symfony/phpunit-bridge": "^4.2 || ^5",
31
+ "symfony/process": "^2.3"
32
+ },
33
+ "time": "2021-04-28T06:42:17+00:00",
34
+ "type": "composer-plugin",
35
+ "extra": {
36
+ "class": "Composer\\Installers\\Plugin",
37
+ "branch-alias": {
38
+ "dev-main": "1.x-dev"
39
+ }
40
+ },
41
+ "installation-source": "dist",
42
+ "autoload": {
43
+ "psr-4": {
44
+ "Composer\\Installers\\": "src/Composer/Installers"
45
+ }
46
+ },
47
+ "notification-url": "https://packagist.org/downloads/",
48
+ "license": [
49
+ "MIT"
50
+ ],
51
+ "authors": [
52
+ {
53
+ "name": "Kyle Robinson Young",
54
+ "email": "kyle@dontkry.com",
55
+ "homepage": "https://github.com/shama"
56
+ }
57
+ ],
58
+ "description": "A multi-framework Composer library installer",
59
+ "homepage": "https://composer.github.io/installers/",
60
+ "keywords": [
61
+ "Craft",
62
+ "Dolibarr",
63
+ "Eliasis",
64
+ "Hurad",
65
+ "ImageCMS",
66
+ "Kanboard",
67
+ "Lan Management System",
68
+ "MODX Evo",
69
+ "MantisBT",
70
+ "Mautic",
71
+ "Maya",
72
+ "OXID",
73
+ "Plentymarkets",
74
+ "Porto",
75
+ "RadPHP",
76
+ "SMF",
77
+ "Starbug",
78
+ "Thelia",
79
+ "Whmcs",
80
+ "WolfCMS",
81
+ "agl",
82
+ "aimeos",
83
+ "annotatecms",
84
+ "attogram",
85
+ "bitrix",
86
+ "cakephp",
87
+ "chef",
88
+ "cockpit",
89
+ "codeigniter",
90
+ "concrete5",
91
+ "croogo",
92
+ "dokuwiki",
93
+ "drupal",
94
+ "eZ Platform",
95
+ "elgg",
96
+ "expressionengine",
97
+ "fuelphp",
98
+ "grav",
99
+ "installer",
100
+ "itop",
101
+ "joomla",
102
+ "known",
103
+ "kohana",
104
+ "laravel",
105
+ "lavalite",
106
+ "lithium",
107
+ "magento",
108
+ "majima",
109
+ "mako",
110
+ "mediawiki",
111
+ "miaoxing",
112
+ "modulework",
113
+ "modx",
114
+ "moodle",
115
+ "osclass",
116
+ "phpbb",
117
+ "piwik",
118
+ "ppi",
119
+ "processwire",
120
+ "puppet",
121
+ "pxcms",
122
+ "reindex",
123
+ "roundcube",
124
+ "shopware",
125
+ "silverstripe",
126
+ "sydes",
127
+ "sylius",
128
+ "symfony",
129
+ "tastyigniter",
130
+ "typo3",
131
+ "wordpress",
132
+ "yawik",
133
+ "zend",
134
+ "zikula"
135
+ ],
136
+ "support": {
137
+ "issues": "https://github.com/composer/installers/issues",
138
+ "source": "https://github.com/composer/installers/tree/v1.11.0"
139
+ },
140
+ "funding": [
141
+ {
142
+ "url": "https://packagist.com",
143
+ "type": "custom"
144
+ },
145
+ {
146
+ "url": "https://github.com/composer",
147
+ "type": "github"
148
+ },
149
+ {
150
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
151
+ "type": "tidelift"
152
+ }
153
+ ],
154
+ "install-path": "./installers"
155
+ },
156
+ {
157
+ "name": "skyverge/wc-plugin-framework",
158
+ "version": "5.10.0",
159
+ "version_normalized": "5.10.0.0",
160
+ "source": {
161
+ "type": "git",
162
+ "url": "https://github.com/skyverge/wc-plugin-framework.git",
163
+ "reference": "e230d7c40286854e49c0cafeec3398cbf2427a64"
164
+ },
165
+ "dist": {
166
+ "type": "zip",
167
+ "url": "https://api.github.com/repos/skyverge/wc-plugin-framework/zipball/e230d7c40286854e49c0cafeec3398cbf2427a64",
168
+ "reference": "e230d7c40286854e49c0cafeec3398cbf2427a64",
169
+ "shasum": ""
170
+ },
171
+ "require-dev": {
172
+ "lucatume/wp-browser": "^2.1"
173
+ },
174
+ "time": "2020-11-06T21:25:49+00:00",
175
+ "type": "library",
176
+ "installation-source": "dist",
177
+ "description": "The official SkyVerge WooCommerce plugin framework",
178
+ "support": {
179
+ "source": "https://github.com/skyverge/wc-plugin-framework/tree/5.10.0",
180
+ "issues": "https://github.com/skyverge/wc-plugin-framework/issues"
181
+ },
182
+ "install-path": "../skyverge/wc-plugin-framework"
183
+ },
184
+ {
185
+ "name": "woocommerce/action-scheduler-job-framework",
186
+ "version": "1.0.0",
187
+ "version_normalized": "1.0.0.0",
188
+ "source": {
189
+ "type": "git",
190
+ "url": "https://github.com/woocommerce/action-scheduler-job-framework.git",
191
+ "reference": "718594e15d7ba2d56f8a37743bda6d7a8296e1c8"
192
+ },
193
+ "dist": {
194
+ "type": "zip",
195
+ "url": "https://api.github.com/repos/woocommerce/action-scheduler-job-framework/zipball/718594e15d7ba2d56f8a37743bda6d7a8296e1c8",
196
+ "reference": "718594e15d7ba2d56f8a37743bda6d7a8296e1c8",
197
+ "shasum": ""
198
+ },
199
+ "require": {
200
+ "php": ">=7.0"
201
+ },
202
+ "require-dev": {
203
+ "woocommerce/woocommerce-sniffs": "0.1.0"
204
+ },
205
+ "time": "2021-05-05T02:11:38+00:00",
206
+ "type": "library",
207
+ "installation-source": "dist",
208
+ "autoload": {
209
+ "psr-4": {
210
+ "Automattic\\WooCommerce\\ActionSchedulerJobFramework\\": "src/"
211
+ }
212
+ },
213
+ "scripts": {
214
+ "phpcs": [
215
+ "phpcs -s -p"
216
+ ]
217
+ },
218
+ "license": [
219
+ "GNU"
220
+ ],
221
+ "description": "A job framework for Action Scheduler (actionscheduler.org)",
222
+ "support": {
223
+ "source": "https://github.com/woocommerce/action-scheduler-job-framework/tree/1.0.0",
224
+ "issues": "https://github.com/woocommerce/action-scheduler-job-framework/issues"
225
+ },
226
+ "install-path": "../woocommerce/action-scheduler-job-framework"
227
+ }
228
+ ],
229
+ "dev": false,
230
+ "dev-package-names": []
231
+ }
vendor/composer/installed.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return array (
2
+ 'root' =>
3
+ array (
4
+ 'pretty_version' => '2.5.0',
5
+ 'version' => '2.5.0.0',
6
+ 'aliases' =>
7
+ array (
8
+ ),
9
+ 'reference' => 'c221784785ff1288e22452fea34cc2b1674e7ef8',
10
+ 'name' => 'facebookincubator/facebook-for-woocommerce',
11
+ ),
12
+ 'versions' =>
13
+ array (
14
+ 'composer/installers' =>
15
+ array (
16
+ 'pretty_version' => 'v1.11.0',
17
+ 'version' => '1.11.0.0',
18
+ 'aliases' =>
19
+ array (
20
+ ),
21
+ 'reference' => 'ae03311f45dfe194412081526be2e003960df74b',
22
+ ),
23
+ 'facebookincubator/facebook-for-woocommerce' =>
24
+ array (
25
+ 'pretty_version' => '2.5.0',
26
+ 'version' => '2.5.0.0',
27
+ 'aliases' =>
28
+ array (
29
+ ),
30
+ 'reference' => 'c221784785ff1288e22452fea34cc2b1674e7ef8',
31
+ ),
32
+ 'roundcube/plugin-installer' =>
33
+ array (
34
+ 'replaced' =>
35
+ array (
36
+ 0 => '*',
37
+ ),
38
+ ),
39
+ 'shama/baton' =>
40
+ array (
41
+ 'replaced' =>
42
+ array (
43
+ 0 => '*',
44
+ ),
45
+ ),
46
+ 'skyverge/wc-plugin-framework' =>
47
+ array (
48
+ 'pretty_version' => '5.10.0',
49
+ 'version' => '5.10.0.0',
50
+ 'aliases' =>
51
+ array (
52
+ ),
53
+ 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
54
+ ),
55
+ 'woocommerce/action-scheduler-job-framework' =>
56
+ array (
57
+ 'pretty_version' => '1.0.0',
58
+ 'version' => '1.0.0.0',
59
+ 'aliases' =>
60
+ array (
61
+ ),
62
+ 'reference' => '718594e15d7ba2d56f8a37743bda6d7a8296e1c8',
63
+ ),
64
+ ),
65
+ );
vendor/composer/installers/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2012 Kyle Robinson Young
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
vendor/composer/installers/phpstan.neon.dist ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ parameters:
2
+ level: 5
3
+ paths:
4
+ - src
5
+ - tests
6
+ excludes_analyse:
7
+ - tests/Composer/Installers/Test/PolyfillTestCase.php
8
+
9
+ includes:
10
+ - vendor/phpstan/phpstan-phpunit/extension.neon
vendor/composer/installers/src/Composer/Installers/AglInstaller.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class AglInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'More/{$name}/',
8
+ );
9
+
10
+ /**
11
+ * Format package name to CamelCase
12
+ */
13
+ public function inflectPackageVars($vars)
14
+ {
15
+ $vars['name'] = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) {
16
+ return strtoupper($matches[1]);
17
+ }, $vars['name']);
18
+
19
+ return $vars;
20
+ }
21
+ }
vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class AimeosInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'extension' => 'ext/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class AnnotateCmsInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'addons/modules/{$name}/',
8
+ 'component' => 'addons/components/{$name}/',
9
+ 'service' => 'addons/services/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class AsgardInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'Modules/{$name}/',
8
+ 'theme' => 'Themes/{$name}/'
9
+ );
10
+
11
+ /**
12
+ * Format package name.
13
+ *
14
+ * For package type asgard-module, cut off a trailing '-plugin' if present.
15
+ *
16
+ * For package type asgard-theme, cut off a trailing '-theme' if present.
17
+ *
18
+ */
19
+ public function inflectPackageVars($vars)
20
+ {
21
+ if ($vars['type'] === 'asgard-module') {
22
+ return $this->inflectPluginVars($vars);
23
+ }
24
+
25
+ if ($vars['type'] === 'asgard-theme') {
26
+ return $this->inflectThemeVars($vars);
27
+ }
28
+
29
+ return $vars;
30
+ }
31
+
32
+ protected function inflectPluginVars($vars)
33
+ {
34
+ $vars['name'] = preg_replace('/-module$/', '', $vars['name']);
35
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
36
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
37
+
38
+ return $vars;
39
+ }
40
+
41
+ protected function inflectThemeVars($vars)
42
+ {
43
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
44
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
45
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
46
+
47
+ return $vars;
48
+ }
49
+ }
vendor/composer/installers/src/Composer/Installers/AttogramInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class AttogramInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/BaseInstaller.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\IO\IOInterface;
5
+ use Composer\Composer;
6
+ use Composer\Package\PackageInterface;
7
+
8
+ abstract class BaseInstaller
9
+ {
10
+ protected $locations = array();
11
+ protected $composer;
12
+ protected $package;
13
+ protected $io;
14
+
15
+ /**
16
+ * Initializes base installer.
17
+ *
18
+ * @param PackageInterface $package
19
+ * @param Composer $composer
20
+ * @param IOInterface $io
21
+ */
22
+ public function __construct(PackageInterface $package = null, Composer $composer = null, IOInterface $io = null)
23
+ {
24
+ $this->composer = $composer;
25
+ $this->package = $package;
26
+ $this->io = $io;
27
+ }
28
+
29
+ /**
30
+ * Return the install path based on package type.
31
+ *
32
+ * @param PackageInterface $package
33
+ * @param string $frameworkType
34
+ * @return string
35
+ */
36
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
37
+ {
38
+ $type = $this->package->getType();
39
+
40
+ $prettyName = $this->package->getPrettyName();
41
+ if (strpos($prettyName, '/') !== false) {
42
+ list($vendor, $name) = explode('/', $prettyName);
43
+ } else {
44
+ $vendor = '';
45
+ $name = $prettyName;
46
+ }
47
+
48
+ $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type'));
49
+
50
+ $extra = $package->getExtra();
51
+ if (!empty($extra['installer-name'])) {
52
+ $availableVars['name'] = $extra['installer-name'];
53
+ }
54
+
55
+ if ($this->composer->getPackage()) {
56
+ $extra = $this->composer->getPackage()->getExtra();
57
+ if (!empty($extra['installer-paths'])) {
58
+ $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type, $vendor);
59
+ if ($customPath !== false) {
60
+ return $this->templatePath($customPath, $availableVars);
61
+ }
62
+ }
63
+ }
64
+
65
+ $packageType = substr($type, strlen($frameworkType) + 1);
66
+ $locations = $this->getLocations();
67
+ if (!isset($locations[$packageType])) {
68
+ throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
69
+ }
70
+
71
+ return $this->templatePath($locations[$packageType], $availableVars);
72
+ }
73
+
74
+ /**
75
+ * For an installer to override to modify the vars per installer.
76
+ *
77
+ * @param array<string, string> $vars This will normally receive array{name: string, vendor: string, type: string}
78
+ * @return array<string, string>
79
+ */
80
+ public function inflectPackageVars($vars)
81
+ {
82
+ return $vars;
83
+ }
84
+
85
+ /**
86
+ * Gets the installer's locations
87
+ *
88
+ * @return array<string, string> map of package types => install path
89
+ */
90
+ public function getLocations()
91
+ {
92
+ return $this->locations;
93
+ }
94
+
95
+ /**
96
+ * Replace vars in a path
97
+ *
98
+ * @param string $path
99
+ * @param array<string, string> $vars
100
+ * @return string
101
+ */
102
+ protected function templatePath($path, array $vars = array())
103
+ {
104
+ if (strpos($path, '{') !== false) {
105
+ extract($vars);
106
+ preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches);
107
+ if (!empty($matches[1])) {
108
+ foreach ($matches[1] as $var) {
109
+ $path = str_replace('{$' . $var . '}', $$var, $path);
110
+ }
111
+ }
112
+ }
113
+
114
+ return $path;
115
+ }
116
+
117
+ /**
118
+ * Search through a passed paths array for a custom install path.
119
+ *
120
+ * @param array $paths
121
+ * @param string $name
122
+ * @param string $type
123
+ * @param string $vendor = NULL
124
+ * @return string|false
125
+ */
126
+ protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = NULL)
127
+ {
128
+ foreach ($paths as $path => $names) {
129
+ $names = (array) $names;
130
+ if (in_array($name, $names) || in_array('type:' . $type, $names) || in_array('vendor:' . $vendor, $names)) {
131
+ return $path;
132
+ }
133
+ }
134
+
135
+ return false;
136
+ }
137
+ }
vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ use Composer\Util\Filesystem;
6
+
7
+ /**
8
+ * Installer for Bitrix Framework. Supported types of extensions:
9
+ * - `bitrix-d7-module` — copy the module to directory `bitrix/modules/<vendor>.<name>`.
10
+ * - `bitrix-d7-component` — copy the component to directory `bitrix/components/<vendor>/<name>`.
11
+ * - `bitrix-d7-template` — copy the template to directory `bitrix/templates/<vendor>_<name>`.
12
+ *
13
+ * You can set custom path to directory with Bitrix kernel in `composer.json`:
14
+ *
15
+ * ```json
16
+ * {
17
+ * "extra": {
18
+ * "bitrix-dir": "s1/bitrix"
19
+ * }
20
+ * }
21
+ * ```
22
+ *
23
+ * @author Nik Samokhvalov <nik@samokhvalov.info>
24
+ * @author Denis Kulichkin <onexhovia@gmail.com>
25
+ */
26
+ class BitrixInstaller extends BaseInstaller
27
+ {
28
+ protected $locations = array(
29
+ 'module' => '{$bitrix_dir}/modules/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
30
+ 'component' => '{$bitrix_dir}/components/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
31
+ 'theme' => '{$bitrix_dir}/templates/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
32
+ 'd7-module' => '{$bitrix_dir}/modules/{$vendor}.{$name}/',
33
+ 'd7-component' => '{$bitrix_dir}/components/{$vendor}/{$name}/',
34
+ 'd7-template' => '{$bitrix_dir}/templates/{$vendor}_{$name}/',
35
+ );
36
+
37
+ /**
38
+ * @var array Storage for informations about duplicates at all the time of installation packages.
39
+ */
40
+ private static $checkedDuplicates = array();
41
+
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ public function inflectPackageVars($vars)
46
+ {
47
+ if ($this->composer->getPackage()) {
48
+ $extra = $this->composer->getPackage()->getExtra();
49
+
50
+ if (isset($extra['bitrix-dir'])) {
51
+ $vars['bitrix_dir'] = $extra['bitrix-dir'];
52
+ }
53
+ }
54
+
55
+ if (!isset($vars['bitrix_dir'])) {
56
+ $vars['bitrix_dir'] = 'bitrix';
57
+ }
58
+
59
+ return parent::inflectPackageVars($vars);
60
+ }
61
+
62
+ /**
63
+ * {@inheritdoc}
64
+ */
65
+ protected function templatePath($path, array $vars = array())
66
+ {
67
+ $templatePath = parent::templatePath($path, $vars);
68
+ $this->checkDuplicates($templatePath, $vars);
69
+
70
+ return $templatePath;
71
+ }
72
+
73
+ /**
74
+ * Duplicates search packages.
75
+ *
76
+ * @param string $path
77
+ * @param array $vars
78
+ */
79
+ protected function checkDuplicates($path, array $vars = array())
80
+ {
81
+ $packageType = substr($vars['type'], strlen('bitrix') + 1);
82
+ $localDir = explode('/', $vars['bitrix_dir']);
83
+ array_pop($localDir);
84
+ $localDir[] = 'local';
85
+ $localDir = implode('/', $localDir);
86
+
87
+ $oldPath = str_replace(
88
+ array('{$bitrix_dir}', '{$name}'),
89
+ array($localDir, $vars['name']),
90
+ $this->locations[$packageType]
91
+ );
92
+
93
+ if (in_array($oldPath, static::$checkedDuplicates)) {
94
+ return;
95
+ }
96
+
97
+ if ($oldPath !== $path && file_exists($oldPath) && $this->io && $this->io->isInteractive()) {
98
+
99
+ $this->io->writeError(' <error>Duplication of packages:</error>');
100
+ $this->io->writeError(' <info>Package ' . $oldPath . ' will be called instead package ' . $path . '</info>');
101
+
102
+ while (true) {
103
+ switch ($this->io->ask(' <info>Delete ' . $oldPath . ' [y,n,?]?</info> ', '?')) {
104
+ case 'y':
105
+ $fs = new Filesystem();
106
+ $fs->removeDirectory($oldPath);
107
+ break 2;
108
+
109
+ case 'n':
110
+ break 2;
111
+
112
+ case '?':
113
+ default:
114
+ $this->io->writeError(array(
115
+ ' y - delete package ' . $oldPath . ' and to continue with the installation',
116
+ ' n - don\'t delete and to continue with the installation',
117
+ ));
118
+ $this->io->writeError(' ? - print help');
119
+ break;
120
+ }
121
+ }
122
+ }
123
+
124
+ static::$checkedDuplicates[] = $oldPath;
125
+ }
126
+ }
vendor/composer/installers/src/Composer/Installers/BonefishInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class BonefishInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'package' => 'Packages/{$vendor}/{$name}/'
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\DependencyResolver\Pool;
5
+ use Composer\Semver\Constraint\Constraint;
6
+
7
+ class CakePHPInstaller extends BaseInstaller
8
+ {
9
+ protected $locations = array(
10
+ 'plugin' => 'Plugin/{$name}/',
11
+ );
12
+
13
+ /**
14
+ * Format package name to CamelCase
15
+ */
16
+ public function inflectPackageVars($vars)
17
+ {
18
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
19
+ return $vars;
20
+ }
21
+
22
+ $nameParts = explode('/', $vars['name']);
23
+ foreach ($nameParts as &$value) {
24
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
25
+ $value = str_replace(array('-', '_'), ' ', $value);
26
+ $value = str_replace(' ', '', ucwords($value));
27
+ }
28
+ $vars['name'] = implode('/', $nameParts);
29
+
30
+ return $vars;
31
+ }
32
+
33
+ /**
34
+ * Change the default plugin location when cakephp >= 3.0
35
+ */
36
+ public function getLocations()
37
+ {
38
+ if ($this->matchesCakeVersion('>=', '3.0.0')) {
39
+ $this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/';
40
+ }
41
+ return $this->locations;
42
+ }
43
+
44
+ /**
45
+ * Check if CakePHP version matches against a version
46
+ *
47
+ * @param string $matcher
48
+ * @param string $version
49
+ * @return bool
50
+ */
51
+ protected function matchesCakeVersion($matcher, $version)
52
+ {
53
+ $repositoryManager = $this->composer->getRepositoryManager();
54
+ if (! $repositoryManager) {
55
+ return false;
56
+ }
57
+
58
+ $repos = $repositoryManager->getLocalRepository();
59
+ if (!$repos) {
60
+ return false;
61
+ }
62
+
63
+ return $repos->findPackage('cakephp/cakephp', new Constraint($matcher, $version)) !== null;
64
+ }
65
+ }
vendor/composer/installers/src/Composer/Installers/ChefInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ChefInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'cookbook' => 'Chef/{$vendor}/{$name}/',
8
+ 'role' => 'Chef/roles/{$name}/',
9
+ );
10
+ }
11
+
vendor/composer/installers/src/Composer/Installers/CiviCrmInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class CiviCrmInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'ext' => 'ext/{$name}/'
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ClanCatsFrameworkInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'ship' => 'CCF/orbit/{$name}/',
8
+ 'theme' => 'CCF/app/themes/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/CockpitInstaller.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class CockpitInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'cockpit/modules/addons/{$name}/',
8
+ );
9
+
10
+ /**
11
+ * Format module name.
12
+ *
13
+ * Strip `module-` prefix from package name.
14
+ *
15
+ * {@inheritDoc}
16
+ */
17
+ public function inflectPackageVars($vars)
18
+ {
19
+ if ($vars['type'] == 'cockpit-module') {
20
+ return $this->inflectModuleVars($vars);
21
+ }
22
+
23
+ return $vars;
24
+ }
25
+
26
+ public function inflectModuleVars($vars)
27
+ {
28
+ $vars['name'] = ucfirst(preg_replace('/cockpit-/i', '', $vars['name']));
29
+
30
+ return $vars;
31
+ }
32
+ }
vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class CodeIgniterInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'library' => 'application/libraries/{$name}/',
8
+ 'third-party' => 'application/third_party/{$name}/',
9
+ 'module' => 'application/modules/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class Concrete5Installer extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'core' => 'concrete/',
8
+ 'block' => 'application/blocks/{$name}/',
9
+ 'package' => 'packages/{$name}/',
10
+ 'theme' => 'application/themes/{$name}/',
11
+ 'update' => 'updates/{$name}/',
12
+ );
13
+ }
vendor/composer/installers/src/Composer/Installers/CraftInstaller.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Installer for Craft Plugins
6
+ */
7
+ class CraftInstaller extends BaseInstaller
8
+ {
9
+ const NAME_PREFIX = 'craft';
10
+ const NAME_SUFFIX = 'plugin';
11
+
12
+ protected $locations = array(
13
+ 'plugin' => 'craft/plugins/{$name}/',
14
+ );
15
+
16
+ /**
17
+ * Strip `craft-` prefix and/or `-plugin` suffix from package names
18
+ *
19
+ * @param array $vars
20
+ *
21
+ * @return array
22
+ */
23
+ final public function inflectPackageVars($vars)
24
+ {
25
+ return $this->inflectPluginVars($vars);
26
+ }
27
+
28
+ private function inflectPluginVars($vars)
29
+ {
30
+ $vars['name'] = preg_replace('/-' . self::NAME_SUFFIX . '$/i', '', $vars['name']);
31
+ $vars['name'] = preg_replace('/^' . self::NAME_PREFIX . '-/i', '', $vars['name']);
32
+
33
+ return $vars;
34
+ }
35
+ }
vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class CroogoInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'Plugin/{$name}/',
8
+ 'theme' => 'View/Themed/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name to CamelCase
13
+ */
14
+ public function inflectPackageVars($vars)
15
+ {
16
+ $vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name']));
17
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
18
+
19
+ return $vars;
20
+ }
21
+ }
vendor/composer/installers/src/Composer/Installers/DecibelInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class DecibelInstaller extends BaseInstaller
5
+ {
6
+ /** @var array */
7
+ protected $locations = array(
8
+ 'app' => 'app/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/DframeInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class DframeInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'module' => 'modules/{$vendor}/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class DokuWikiInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'lib/plugins/{$name}/',
8
+ 'template' => 'lib/tpl/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name.
13
+ *
14
+ * For package type dokuwiki-plugin, cut off a trailing '-plugin',
15
+ * or leading dokuwiki_ if present.
16
+ *
17
+ * For package type dokuwiki-template, cut off a trailing '-template' if present.
18
+ *
19
+ */
20
+ public function inflectPackageVars($vars)
21
+ {
22
+
23
+ if ($vars['type'] === 'dokuwiki-plugin') {
24
+ return $this->inflectPluginVars($vars);
25
+ }
26
+
27
+ if ($vars['type'] === 'dokuwiki-template') {
28
+ return $this->inflectTemplateVars($vars);
29
+ }
30
+
31
+ return $vars;
32
+ }
33
+
34
+ protected function inflectPluginVars($vars)
35
+ {
36
+ $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']);
37
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
38
+
39
+ return $vars;
40
+ }
41
+
42
+ protected function inflectTemplateVars($vars)
43
+ {
44
+ $vars['name'] = preg_replace('/-template$/', '', $vars['name']);
45
+ $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']);
46
+
47
+ return $vars;
48
+ }
49
+
50
+ }
vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Class DolibarrInstaller
6
+ *
7
+ * @package Composer\Installers
8
+ * @author Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9
+ */
10
+ class DolibarrInstaller extends BaseInstaller
11
+ {
12
+ //TODO: Add support for scripts and themes
13
+ protected $locations = array(
14
+ 'module' => 'htdocs/custom/{$name}/',
15
+ );
16
+ }
vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class DrupalInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'core' => 'core/',
8
+ 'module' => 'modules/{$name}/',
9
+ 'theme' => 'themes/{$name}/',
10
+ 'library' => 'libraries/{$name}/',
11
+ 'profile' => 'profiles/{$name}/',
12
+ 'database-driver' => 'drivers/lib/Drupal/Driver/Database/{$name}/',
13
+ 'drush' => 'drush/{$name}/',
14
+ 'custom-theme' => 'themes/custom/{$name}/',
15
+ 'custom-module' => 'modules/custom/{$name}/',
16
+ 'custom-profile' => 'profiles/custom/{$name}/',
17
+ 'drupal-multisite' => 'sites/{$name}/',
18
+ 'console' => 'console/{$name}/',
19
+ 'console-language' => 'console/language/{$name}/',
20
+ 'config' => 'config/sync/',
21
+ );
22
+ }
vendor/composer/installers/src/Composer/Installers/ElggInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ElggInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'mod/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/EliasisInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class EliasisInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'component' => 'components/{$name}/',
8
+ 'module' => 'modules/{$name}/',
9
+ 'plugin' => 'plugins/{$name}/',
10
+ 'template' => 'templates/{$name}/',
11
+ );
12
+ }
vendor/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\Package\PackageInterface;
5
+
6
+ class ExpressionEngineInstaller extends BaseInstaller
7
+ {
8
+
9
+ protected $locations = array();
10
+
11
+ private $ee2Locations = array(
12
+ 'addon' => 'system/expressionengine/third_party/{$name}/',
13
+ 'theme' => 'themes/third_party/{$name}/',
14
+ );
15
+
16
+ private $ee3Locations = array(
17
+ 'addon' => 'system/user/addons/{$name}/',
18
+ 'theme' => 'themes/user/{$name}/',
19
+ );
20
+
21
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
22
+ {
23
+
24
+ $version = "{$frameworkType}Locations";
25
+ $this->locations = $this->$version;
26
+
27
+ return parent::getInstallPath($package, $frameworkType);
28
+ }
29
+ }
vendor/composer/installers/src/Composer/Installers/EzPlatformInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class EzPlatformInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'meta-assets' => 'web/assets/ezplatform/',
8
+ 'assets' => 'web/assets/ezplatform/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/FuelInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class FuelInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'fuel/app/modules/{$name}/',
8
+ 'package' => 'fuel/packages/{$name}/',
9
+ 'theme' => 'fuel/app/themes/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class FuelphpInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'component' => 'components/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/GravInstaller.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class GravInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'user/plugins/{$name}/',
8
+ 'theme' => 'user/themes/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name
13
+ *
14
+ * @param array $vars
15
+ *
16
+ * @return array
17
+ */
18
+ public function inflectPackageVars($vars)
19
+ {
20
+ $restrictedWords = implode('|', array_keys($this->locations));
21
+
22
+ $vars['name'] = strtolower($vars['name']);
23
+ $vars['name'] = preg_replace('/^(?:grav-)?(?:(?:'.$restrictedWords.')-)?(.*?)(?:-(?:'.$restrictedWords.'))?$/ui',
24
+ '$1',
25
+ $vars['name']
26
+ );
27
+
28
+ return $vars;
29
+ }
30
+ }
vendor/composer/installers/src/Composer/Installers/HuradInstaller.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class HuradInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'plugins/{$name}/',
8
+ 'theme' => 'plugins/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name to CamelCase
13
+ */
14
+ public function inflectPackageVars($vars)
15
+ {
16
+ $nameParts = explode('/', $vars['name']);
17
+ foreach ($nameParts as &$value) {
18
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
19
+ $value = str_replace(array('-', '_'), ' ', $value);
20
+ $value = str_replace(' ', '', ucwords($value));
21
+ }
22
+ $vars['name'] = implode('/', $nameParts);
23
+ return $vars;
24
+ }
25
+ }
vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ImageCMSInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'template' => 'templates/{$name}/',
8
+ 'module' => 'application/modules/{$name}/',
9
+ 'library' => 'application/libraries/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/Installer.php ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ use Composer\Composer;
6
+ use Composer\Installer\BinaryInstaller;
7
+ use Composer\Installer\LibraryInstaller;
8
+ use Composer\IO\IOInterface;
9
+ use Composer\Package\PackageInterface;
10
+ use Composer\Repository\InstalledRepositoryInterface;
11
+ use Composer\Util\Filesystem;
12
+ use React\Promise\PromiseInterface;
13
+
14
+ class Installer extends LibraryInstaller
15
+ {
16
+
17
+ /**
18
+ * Package types to installer class map
19
+ *
20
+ * @var array
21
+ */
22
+ private $supportedTypes = array(
23
+ 'aimeos' => 'AimeosInstaller',
24
+ 'asgard' => 'AsgardInstaller',
25
+ 'attogram' => 'AttogramInstaller',
26
+ 'agl' => 'AglInstaller',
27
+ 'annotatecms' => 'AnnotateCmsInstaller',
28
+ 'bitrix' => 'BitrixInstaller',
29
+ 'bonefish' => 'BonefishInstaller',
30
+ 'cakephp' => 'CakePHPInstaller',
31
+ 'chef' => 'ChefInstaller',
32
+ 'civicrm' => 'CiviCrmInstaller',
33
+ 'ccframework' => 'ClanCatsFrameworkInstaller',
34
+ 'cockpit' => 'CockpitInstaller',
35
+ 'codeigniter' => 'CodeIgniterInstaller',
36
+ 'concrete5' => 'Concrete5Installer',
37
+ 'craft' => 'CraftInstaller',
38
+ 'croogo' => 'CroogoInstaller',
39
+ 'dframe' => 'DframeInstaller',
40
+ 'dokuwiki' => 'DokuWikiInstaller',
41
+ 'dolibarr' => 'DolibarrInstaller',
42
+ 'decibel' => 'DecibelInstaller',
43
+ 'drupal' => 'DrupalInstaller',
44
+ 'elgg' => 'ElggInstaller',
45
+ 'eliasis' => 'EliasisInstaller',
46
+ 'ee3' => 'ExpressionEngineInstaller',
47
+ 'ee2' => 'ExpressionEngineInstaller',
48
+ 'ezplatform' => 'EzPlatformInstaller',
49
+ 'fuel' => 'FuelInstaller',
50
+ 'fuelphp' => 'FuelphpInstaller',
51
+ 'grav' => 'GravInstaller',
52
+ 'hurad' => 'HuradInstaller',
53
+ 'tastyigniter' => 'TastyIgniterInstaller',
54
+ 'imagecms' => 'ImageCMSInstaller',
55
+ 'itop' => 'ItopInstaller',
56
+ 'joomla' => 'JoomlaInstaller',
57
+ 'kanboard' => 'KanboardInstaller',
58
+ 'kirby' => 'KirbyInstaller',
59
+ 'known' => 'KnownInstaller',
60
+ 'kodicms' => 'KodiCMSInstaller',
61
+ 'kohana' => 'KohanaInstaller',
62
+ 'lms' => 'LanManagementSystemInstaller',
63
+ 'laravel' => 'LaravelInstaller',
64
+ 'lavalite' => 'LavaLiteInstaller',
65
+ 'lithium' => 'LithiumInstaller',
66
+ 'magento' => 'MagentoInstaller',
67
+ 'majima' => 'MajimaInstaller',
68
+ 'mantisbt' => 'MantisBTInstaller',
69
+ 'mako' => 'MakoInstaller',
70
+ 'maya' => 'MayaInstaller',
71
+ 'mautic' => 'MauticInstaller',
72
+ 'mediawiki' => 'MediaWikiInstaller',
73
+ 'miaoxing' => 'MiaoxingInstaller',
74
+ 'microweber' => 'MicroweberInstaller',
75
+ 'modulework' => 'MODULEWorkInstaller',
76
+ 'modx' => 'ModxInstaller',
77
+ 'modxevo' => 'MODXEvoInstaller',
78
+ 'moodle' => 'MoodleInstaller',
79
+ 'october' => 'OctoberInstaller',
80
+ 'ontowiki' => 'OntoWikiInstaller',
81
+ 'oxid' => 'OxidInstaller',
82
+ 'osclass' => 'OsclassInstaller',
83
+ 'pxcms' => 'PxcmsInstaller',
84
+ 'phpbb' => 'PhpBBInstaller',
85
+ 'pimcore' => 'PimcoreInstaller',
86
+ 'piwik' => 'PiwikInstaller',
87
+ 'plentymarkets'=> 'PlentymarketsInstaller',
88
+ 'ppi' => 'PPIInstaller',
89
+ 'puppet' => 'PuppetInstaller',
90
+ 'radphp' => 'RadPHPInstaller',
91
+ 'phifty' => 'PhiftyInstaller',
92
+ 'porto' => 'PortoInstaller',
93
+ 'processwire' => 'ProcessWireInstaller',
94
+ 'redaxo' => 'RedaxoInstaller',
95
+ 'redaxo5' => 'Redaxo5Installer',
96
+ 'reindex' => 'ReIndexInstaller',
97
+ 'roundcube' => 'RoundcubeInstaller',
98
+ 'shopware' => 'ShopwareInstaller',
99
+ 'sitedirect' => 'SiteDirectInstaller',
100
+ 'silverstripe' => 'SilverStripeInstaller',
101
+ 'smf' => 'SMFInstaller',
102
+ 'starbug' => 'StarbugInstaller',
103
+ 'sydes' => 'SyDESInstaller',
104
+ 'sylius' => 'SyliusInstaller',
105
+ 'symfony1' => 'Symfony1Installer',
106
+ 'tao' => 'TaoInstaller',
107
+ 'thelia' => 'TheliaInstaller',
108
+ 'tusk' => 'TuskInstaller',
109
+ 'typo3-cms' => 'TYPO3CmsInstaller',
110
+ 'typo3-flow' => 'TYPO3FlowInstaller',
111
+ 'userfrosting' => 'UserFrostingInstaller',
112
+ 'vanilla' => 'VanillaInstaller',
113
+ 'whmcs' => 'WHMCSInstaller',
114
+ 'winter' => 'WinterInstaller',
115
+ 'wolfcms' => 'WolfCMSInstaller',
116
+ 'wordpress' => 'WordPressInstaller',
117
+ 'yawik' => 'YawikInstaller',
118
+ 'zend' => 'ZendInstaller',
119
+ 'zikula' => 'ZikulaInstaller',
120
+ 'prestashop' => 'PrestashopInstaller'
121
+ );
122
+
123
+ /**
124
+ * Installer constructor.
125
+ *
126
+ * Disables installers specified in main composer extra installer-disable
127
+ * list
128
+ *
129
+ * @param IOInterface $io
130
+ * @param Composer $composer
131
+ * @param string $type
132
+ * @param Filesystem|null $filesystem
133
+ * @param BinaryInstaller|null $binaryInstaller
134
+ */
135
+ public function __construct(
136
+ IOInterface $io,
137
+ Composer $composer,
138
+ $type = 'library',
139
+ Filesystem $filesystem = null,
140
+ BinaryInstaller $binaryInstaller = null
141
+ ) {
142
+ parent::__construct($io, $composer, $type, $filesystem,
143
+ $binaryInstaller);
144
+ $this->removeDisabledInstallers();
145
+ }
146
+
147
+ /**
148
+ * {@inheritDoc}
149
+ */
150
+ public function getInstallPath(PackageInterface $package)
151
+ {
152
+ $type = $package->getType();
153
+ $frameworkType = $this->findFrameworkType($type);
154
+
155
+ if ($frameworkType === false) {
156
+ throw new \InvalidArgumentException(
157
+ 'Sorry the package type of this package is not yet supported.'
158
+ );
159
+ }
160
+
161
+ $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
162
+ $installer = new $class($package, $this->composer, $this->getIO());
163
+
164
+ return $installer->getInstallPath($package, $frameworkType);
165
+ }
166
+
167
+ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
168
+ {
169
+ $installPath = $this->getPackageBasePath($package);
170
+ $io = $this->io;
171
+ $outputStatus = function () use ($io, $installPath) {
172
+ $io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>'));
173
+ };
174
+
175
+ $promise = parent::uninstall($repo, $package);
176
+
177
+ // Composer v2 might return a promise here
178
+ if ($promise instanceof PromiseInterface) {
179
+ return $promise->then($outputStatus);
180
+ }
181
+
182
+ // If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async)
183
+ $outputStatus();
184
+
185
+ return null;
186
+ }
187
+
188
+ /**
189
+ * {@inheritDoc}
190
+ */
191
+ public function supports($packageType)
192
+ {
193
+ $frameworkType = $this->findFrameworkType($packageType);
194
+
195
+ if ($frameworkType === false) {
196
+ return false;
197
+ }
198
+
199
+ $locationPattern = $this->getLocationPattern($frameworkType);
200
+
201
+ return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1;
202
+ }
203
+
204
+ /**
205
+ * Finds a supported framework type if it exists and returns it
206
+ *
207
+ * @param string $type
208
+ * @return string|false
209
+ */
210
+ protected function findFrameworkType($type)
211
+ {
212
+ krsort($this->supportedTypes);
213
+
214
+ foreach ($this->supportedTypes as $key => $val) {
215
+ if ($key === substr($type, 0, strlen($key))) {
216
+ return substr($type, 0, strlen($key));
217
+ }
218
+ }
219
+
220
+ return false;
221
+ }
222
+
223
+ /**
224
+ * Get the second part of the regular expression to check for support of a
225
+ * package type
226
+ *
227
+ * @param string $frameworkType
228
+ * @return string
229
+ */
230
+ protected function getLocationPattern($frameworkType)
231
+ {
232
+ $pattern = false;
233
+ if (!empty($this->supportedTypes[$frameworkType])) {
234
+ $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType];
235
+ /** @var BaseInstaller $framework */
236
+ $framework = new $frameworkClass(null, $this->composer, $this->getIO());
237
+ $locations = array_keys($framework->getLocations());
238
+ $pattern = $locations ? '(' . implode('|', $locations) . ')' : false;
239
+ }
240
+
241
+ return $pattern ? : '(\w+)';
242
+ }
243
+
244
+ /**
245
+ * Get I/O object
246
+ *
247
+ * @return IOInterface
248
+ */
249
+ private function getIO()
250
+ {
251
+ return $this->io;
252
+ }
253
+
254
+ /**
255
+ * Look for installers set to be disabled in composer's extra config and
256
+ * remove them from the list of supported installers.
257
+ *
258
+ * Globals:
259
+ * - true, "all", and "*" - disable all installers.
260
+ * - false - enable all installers (useful with
261
+ * wikimedia/composer-merge-plugin or similar)
262
+ *
263
+ * @return void
264
+ */
265
+ protected function removeDisabledInstallers()
266
+ {
267
+ $extra = $this->composer->getPackage()->getExtra();
268
+
269
+ if (!isset($extra['installer-disable']) || $extra['installer-disable'] === false) {
270
+ // No installers are disabled
271
+ return;
272
+ }
273
+
274
+ // Get installers to disable
275
+ $disable = $extra['installer-disable'];
276
+
277
+ // Ensure $disabled is an array
278
+ if (!is_array($disable)) {
279
+ $disable = array($disable);
280
+ }
281
+
282
+ // Check which installers should be disabled
283
+ $all = array(true, "all", "*");
284
+ $intersect = array_intersect($all, $disable);
285
+ if (!empty($intersect)) {
286
+ // Disable all installers
287
+ $this->supportedTypes = array();
288
+ } else {
289
+ // Disable specified installers
290
+ foreach ($disable as $key => $installer) {
291
+ if (is_string($installer) && key_exists($installer, $this->supportedTypes)) {
292
+ unset($this->supportedTypes[$installer]);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ }
vendor/composer/installers/src/Composer/Installers/ItopInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ItopInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'extension' => 'extensions/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class JoomlaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'component' => 'components/{$name}/',
8
+ 'module' => 'modules/{$name}/',
9
+ 'template' => 'templates/{$name}/',
10
+ 'plugin' => 'plugins/{$name}/',
11
+ 'library' => 'libraries/{$name}/',
12
+ );
13
+
14
+ // TODO: Add inflector for mod_ and com_ names
15
+ }
vendor/composer/installers/src/Composer/Installers/KanboardInstaller.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ *
6
+ * Installer for kanboard plugins
7
+ *
8
+ * kanboard.net
9
+ *
10
+ * Class KanboardInstaller
11
+ * @package Composer\Installers
12
+ */
13
+ class KanboardInstaller extends BaseInstaller
14
+ {
15
+ protected $locations = array(
16
+ 'plugin' => 'plugins/{$name}/',
17
+ );
18
+ }
vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class KirbyInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'site/plugins/{$name}/',
8
+ 'field' => 'site/fields/{$name}/',
9
+ 'tag' => 'site/tags/{$name}/'
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/KnownInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class KnownInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'IdnoPlugins/{$name}/',
8
+ 'theme' => 'Themes/{$name}/',
9
+ 'console' => 'ConsolePlugins/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/KodiCMSInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class KodiCMSInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'cms/plugins/{$name}/',
8
+ 'media' => 'cms/media/vendor/{$name}/'
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class KohanaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class LanManagementSystemInstaller extends BaseInstaller
6
+ {
7
+
8
+ protected $locations = array(
9
+ 'plugin' => 'plugins/{$name}/',
10
+ 'template' => 'templates/{$name}/',
11
+ 'document-template' => 'documents/templates/{$name}/',
12
+ 'userpanel-module' => 'userpanel/modules/{$name}/',
13
+ );
14
+
15
+ /**
16
+ * Format package name to CamelCase
17
+ */
18
+ public function inflectPackageVars($vars)
19
+ {
20
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
21
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
22
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
23
+
24
+ return $vars;
25
+ }
26
+
27
+ }
vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class LaravelInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'library' => 'libraries/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/LavaLiteInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class LavaLiteInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'package' => 'packages/{$vendor}/{$name}/',
8
+ 'theme' => 'public/themes/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class LithiumInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'library' => 'libraries/{$name}/',
8
+ 'source' => 'libraries/_source/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MODULEWorkInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * An installer to handle MODX Evolution specifics when installing packages.
6
+ */
7
+ class MODXEvoInstaller extends BaseInstaller
8
+ {
9
+ protected $locations = array(
10
+ 'snippet' => 'assets/snippets/{$name}/',
11
+ 'plugin' => 'assets/plugins/{$name}/',
12
+ 'module' => 'assets/modules/{$name}/',
13
+ 'template' => 'assets/templates/{$name}/',
14
+ 'lib' => 'assets/lib/{$name}/'
15
+ );
16
+ }
vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MagentoInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'theme' => 'app/design/frontend/{$name}/',
8
+ 'skin' => 'skin/frontend/default/{$name}/',
9
+ 'library' => 'lib/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/MajimaInstaller.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Plugin/theme installer for majima
6
+ * @author David Neustadt
7
+ */
8
+ class MajimaInstaller extends BaseInstaller
9
+ {
10
+ protected $locations = array(
11
+ 'plugin' => 'plugins/{$name}/',
12
+ );
13
+
14
+ /**
15
+ * Transforms the names
16
+ * @param array $vars
17
+ * @return array
18
+ */
19
+ public function inflectPackageVars($vars)
20
+ {
21
+ return $this->correctPluginName($vars);
22
+ }
23
+
24
+ /**
25
+ * Change hyphenated names to camelcase
26
+ * @param array $vars
27
+ * @return array
28
+ */
29
+ private function correctPluginName($vars)
30
+ {
31
+ $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) {
32
+ return strtoupper($matches[0][1]);
33
+ }, $vars['name']);
34
+ $vars['name'] = ucfirst($camelCasedName);
35
+ return $vars;
36
+ }
37
+ }
vendor/composer/installers/src/Composer/Installers/MakoInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MakoInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'package' => 'app/packages/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/MantisBTInstaller.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\DependencyResolver\Pool;
5
+
6
+ class MantisBTInstaller extends BaseInstaller
7
+ {
8
+ protected $locations = array(
9
+ 'plugin' => 'plugins/{$name}/',
10
+ );
11
+
12
+ /**
13
+ * Format package name to CamelCase
14
+ */
15
+ public function inflectPackageVars($vars)
16
+ {
17
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
18
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
19
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
20
+
21
+ return $vars;
22
+ }
23
+ }
vendor/composer/installers/src/Composer/Installers/MauticInstaller.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\Package\PackageInterface;
5
+
6
+ class MauticInstaller extends BaseInstaller
7
+ {
8
+ protected $locations = array(
9
+ 'plugin' => 'plugins/{$name}/',
10
+ 'theme' => 'themes/{$name}/',
11
+ 'core' => 'app/',
12
+ );
13
+
14
+ private function getDirectoryName()
15
+ {
16
+ $extra = $this->package->getExtra();
17
+ if (!empty($extra['install-directory-name'])) {
18
+ return $extra['install-directory-name'];
19
+ }
20
+
21
+ return $this->toCamelCase($this->package->getPrettyName());
22
+ }
23
+
24
+ /**
25
+ * @param string $packageName
26
+ *
27
+ * @return string
28
+ */
29
+ private function toCamelCase($packageName)
30
+ {
31
+ return str_replace(' ', '', ucwords(str_replace('-', ' ', basename($packageName))));
32
+ }
33
+
34
+ /**
35
+ * Format package name of mautic-plugins to CamelCase
36
+ */
37
+ public function inflectPackageVars($vars)
38
+ {
39
+
40
+ if ($vars['type'] == 'mautic-plugin' || $vars['type'] == 'mautic-theme') {
41
+ $directoryName = $this->getDirectoryName();
42
+ $vars['name'] = $directoryName;
43
+ }
44
+
45
+ return $vars;
46
+ }
47
+
48
+ }
vendor/composer/installers/src/Composer/Installers/MayaInstaller.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MayaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ );
9
+
10
+ /**
11
+ * Format package name.
12
+ *
13
+ * For package type maya-module, cut off a trailing '-module' if present.
14
+ *
15
+ */
16
+ public function inflectPackageVars($vars)
17
+ {
18
+ if ($vars['type'] === 'maya-module') {
19
+ return $this->inflectModuleVars($vars);
20
+ }
21
+
22
+ return $vars;
23
+ }
24
+
25
+ protected function inflectModuleVars($vars)
26
+ {
27
+ $vars['name'] = preg_replace('/-module$/', '', $vars['name']);
28
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
29
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
30
+
31
+ return $vars;
32
+ }
33
+ }
vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MediaWikiInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'core' => 'core/',
8
+ 'extension' => 'extensions/{$name}/',
9
+ 'skin' => 'skins/{$name}/',
10
+ );
11
+
12
+ /**
13
+ * Format package name.
14
+ *
15
+ * For package type mediawiki-extension, cut off a trailing '-extension' if present and transform
16
+ * to CamelCase keeping existing uppercase chars.
17
+ *
18
+ * For package type mediawiki-skin, cut off a trailing '-skin' if present.
19
+ *
20
+ */
21
+ public function inflectPackageVars($vars)
22
+ {
23
+
24
+ if ($vars['type'] === 'mediawiki-extension') {
25
+ return $this->inflectExtensionVars($vars);
26
+ }
27
+
28
+ if ($vars['type'] === 'mediawiki-skin') {
29
+ return $this->inflectSkinVars($vars);
30
+ }
31
+
32
+ return $vars;
33
+ }
34
+
35
+ protected function inflectExtensionVars($vars)
36
+ {
37
+ $vars['name'] = preg_replace('/-extension$/', '', $vars['name']);
38
+ $vars['name'] = str_replace('-', ' ', $vars['name']);
39
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
40
+
41
+ return $vars;
42
+ }
43
+
44
+ protected function inflectSkinVars($vars)
45
+ {
46
+ $vars['name'] = preg_replace('/-skin$/', '', $vars['name']);
47
+
48
+ return $vars;
49
+ }
50
+
51
+ }
vendor/composer/installers/src/Composer/Installers/MiaoxingInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class MiaoxingInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'plugin' => 'plugins/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MicroweberInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'userfiles/modules/{$install_item_dir}/',
8
+ 'module-skin' => 'userfiles/modules/{$install_item_dir}/templates/',
9
+ 'template' => 'userfiles/templates/{$install_item_dir}/',
10
+ 'element' => 'userfiles/elements/{$install_item_dir}/',
11
+ 'vendor' => 'vendor/{$install_item_dir}/',
12
+ 'components' => 'components/{$install_item_dir}/'
13
+ );
14
+
15
+ /**
16
+ * Format package name.
17
+ *
18
+ * For package type microweber-module, cut off a trailing '-module' if present
19
+ *
20
+ * For package type microweber-template, cut off a trailing '-template' if present.
21
+ *
22
+ */
23
+ public function inflectPackageVars($vars)
24
+ {
25
+
26
+
27
+ if ($this->package->getTargetDir()) {
28
+ $vars['install_item_dir'] = $this->package->getTargetDir();
29
+ } else {
30
+ $vars['install_item_dir'] = $vars['name'];
31
+ if ($vars['type'] === 'microweber-template') {
32
+ return $this->inflectTemplateVars($vars);
33
+ }
34
+ if ($vars['type'] === 'microweber-templates') {
35
+ return $this->inflectTemplatesVars($vars);
36
+ }
37
+ if ($vars['type'] === 'microweber-core') {
38
+ return $this->inflectCoreVars($vars);
39
+ }
40
+ if ($vars['type'] === 'microweber-adapter') {
41
+ return $this->inflectCoreVars($vars);
42
+ }
43
+ if ($vars['type'] === 'microweber-module') {
44
+ return $this->inflectModuleVars($vars);
45
+ }
46
+ if ($vars['type'] === 'microweber-modules') {
47
+ return $this->inflectModulesVars($vars);
48
+ }
49
+ if ($vars['type'] === 'microweber-skin') {
50
+ return $this->inflectSkinVars($vars);
51
+ }
52
+ if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') {
53
+ return $this->inflectElementVars($vars);
54
+ }
55
+ }
56
+
57
+
58
+ return $vars;
59
+ }
60
+
61
+ protected function inflectTemplateVars($vars)
62
+ {
63
+ $vars['install_item_dir'] = preg_replace('/-template$/', '', $vars['install_item_dir']);
64
+ $vars['install_item_dir'] = preg_replace('/template-$/', '', $vars['install_item_dir']);
65
+
66
+ return $vars;
67
+ }
68
+
69
+ protected function inflectTemplatesVars($vars)
70
+ {
71
+ $vars['install_item_dir'] = preg_replace('/-templates$/', '', $vars['install_item_dir']);
72
+ $vars['install_item_dir'] = preg_replace('/templates-$/', '', $vars['install_item_dir']);
73
+
74
+ return $vars;
75
+ }
76
+
77
+ protected function inflectCoreVars($vars)
78
+ {
79
+ $vars['install_item_dir'] = preg_replace('/-providers$/', '', $vars['install_item_dir']);
80
+ $vars['install_item_dir'] = preg_replace('/-provider$/', '', $vars['install_item_dir']);
81
+ $vars['install_item_dir'] = preg_replace('/-adapter$/', '', $vars['install_item_dir']);
82
+
83
+ return $vars;
84
+ }
85
+
86
+ protected function inflectModuleVars($vars)
87
+ {
88
+ $vars['install_item_dir'] = preg_replace('/-module$/', '', $vars['install_item_dir']);
89
+ $vars['install_item_dir'] = preg_replace('/module-$/', '', $vars['install_item_dir']);
90
+
91
+ return $vars;
92
+ }
93
+
94
+ protected function inflectModulesVars($vars)
95
+ {
96
+ $vars['install_item_dir'] = preg_replace('/-modules$/', '', $vars['install_item_dir']);
97
+ $vars['install_item_dir'] = preg_replace('/modules-$/', '', $vars['install_item_dir']);
98
+
99
+ return $vars;
100
+ }
101
+
102
+ protected function inflectSkinVars($vars)
103
+ {
104
+ $vars['install_item_dir'] = preg_replace('/-skin$/', '', $vars['install_item_dir']);
105
+ $vars['install_item_dir'] = preg_replace('/skin-$/', '', $vars['install_item_dir']);
106
+
107
+ return $vars;
108
+ }
109
+
110
+ protected function inflectElementVars($vars)
111
+ {
112
+ $vars['install_item_dir'] = preg_replace('/-elements$/', '', $vars['install_item_dir']);
113
+ $vars['install_item_dir'] = preg_replace('/elements-$/', '', $vars['install_item_dir']);
114
+ $vars['install_item_dir'] = preg_replace('/-element$/', '', $vars['install_item_dir']);
115
+ $vars['install_item_dir'] = preg_replace('/element-$/', '', $vars['install_item_dir']);
116
+
117
+ return $vars;
118
+ }
119
+ }
vendor/composer/installers/src/Composer/Installers/ModxInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * An installer to handle MODX specifics when installing packages.
6
+ */
7
+ class ModxInstaller extends BaseInstaller
8
+ {
9
+ protected $locations = array(
10
+ 'extra' => 'core/packages/{$name}/'
11
+ );
12
+ }
vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class MoodleInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'mod' => 'mod/{$name}/',
8
+ 'admin_report' => 'admin/report/{$name}/',
9
+ 'atto' => 'lib/editor/atto/plugins/{$name}/',
10
+ 'tool' => 'admin/tool/{$name}/',
11
+ 'assignment' => 'mod/assignment/type/{$name}/',
12
+ 'assignsubmission' => 'mod/assign/submission/{$name}/',
13
+ 'assignfeedback' => 'mod/assign/feedback/{$name}/',
14
+ 'auth' => 'auth/{$name}/',
15
+ 'availability' => 'availability/condition/{$name}/',
16
+ 'block' => 'blocks/{$name}/',
17
+ 'booktool' => 'mod/book/tool/{$name}/',
18
+ 'cachestore' => 'cache/stores/{$name}/',
19
+ 'cachelock' => 'cache/locks/{$name}/',
20
+ 'calendartype' => 'calendar/type/{$name}/',
21
+ 'fileconverter' => 'files/converter/{$name}/',
22
+ 'format' => 'course/format/{$name}/',
23
+ 'coursereport' => 'course/report/{$name}/',
24
+ 'customcertelement' => 'mod/customcert/element/{$name}/',
25
+ 'datafield' => 'mod/data/field/{$name}/',
26
+ 'datapreset' => 'mod/data/preset/{$name}/',
27
+ 'editor' => 'lib/editor/{$name}/',
28
+ 'enrol' => 'enrol/{$name}/',
29
+ 'filter' => 'filter/{$name}/',
30
+ 'gradeexport' => 'grade/export/{$name}/',
31
+ 'gradeimport' => 'grade/import/{$name}/',
32
+ 'gradereport' => 'grade/report/{$name}/',
33
+ 'gradingform' => 'grade/grading/form/{$name}/',
34
+ 'local' => 'local/{$name}/',
35
+ 'logstore' => 'admin/tool/log/store/{$name}/',
36
+ 'ltisource' => 'mod/lti/source/{$name}/',
37
+ 'ltiservice' => 'mod/lti/service/{$name}/',
38
+ 'message' => 'message/output/{$name}/',
39
+ 'mnetservice' => 'mnet/service/{$name}/',
40
+ 'plagiarism' => 'plagiarism/{$name}/',
41
+ 'portfolio' => 'portfolio/{$name}/',
42
+ 'qbehaviour' => 'question/behaviour/{$name}/',
43
+ 'qformat' => 'question/format/{$name}/',
44
+ 'qtype' => 'question/type/{$name}/',
45
+ 'quizaccess' => 'mod/quiz/accessrule/{$name}/',
46
+ 'quiz' => 'mod/quiz/report/{$name}/',
47
+ 'report' => 'report/{$name}/',
48
+ 'repository' => 'repository/{$name}/',
49
+ 'scormreport' => 'mod/scorm/report/{$name}/',
50
+ 'search' => 'search/engine/{$name}/',
51
+ 'theme' => 'theme/{$name}/',
52
+ 'tinymce' => 'lib/editor/tinymce/plugins/{$name}/',
53
+ 'profilefield' => 'user/profile/field/{$name}/',
54
+ 'webservice' => 'webservice/{$name}/',
55
+ 'workshopallocation' => 'mod/workshop/allocation/{$name}/',
56
+ 'workshopeval' => 'mod/workshop/eval/{$name}/',
57
+ 'workshopform' => 'mod/workshop/form/{$name}/'
58
+ );
59
+ }
vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class OctoberInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ 'plugin' => 'plugins/{$vendor}/{$name}/',
9
+ 'theme' => 'themes/{$vendor}-{$name}/'
10
+ );
11
+
12
+ /**
13
+ * Format package name.
14
+ *
15
+ * For package type october-plugin, cut off a trailing '-plugin' if present.
16
+ *
17
+ * For package type october-theme, cut off a trailing '-theme' if present.
18
+ *
19
+ */
20
+ public function inflectPackageVars($vars)
21
+ {
22
+ if ($vars['type'] === 'october-plugin') {
23
+ return $this->inflectPluginVars($vars);
24
+ }
25
+
26
+ if ($vars['type'] === 'october-theme') {
27
+ return $this->inflectThemeVars($vars);
28
+ }
29
+
30
+ return $vars;
31
+ }
32
+
33
+ protected function inflectPluginVars($vars)
34
+ {
35
+ $vars['name'] = preg_replace('/^oc-|-plugin$/', '', $vars['name']);
36
+ $vars['vendor'] = preg_replace('/[^a-z0-9_]/i', '', $vars['vendor']);
37
+
38
+ return $vars;
39
+ }
40
+
41
+ protected function inflectThemeVars($vars)
42
+ {
43
+ $vars['name'] = preg_replace('/^oc-|-theme$/', '', $vars['name']);
44
+ $vars['vendor'] = preg_replace('/[^a-z0-9_]/i', '', $vars['vendor']);
45
+
46
+ return $vars;
47
+ }
48
+ }
vendor/composer/installers/src/Composer/Installers/OntoWikiInstaller.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class OntoWikiInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'extension' => 'extensions/{$name}/',
8
+ 'theme' => 'extensions/themes/{$name}/',
9
+ 'translation' => 'extensions/translations/{$name}/',
10
+ );
11
+
12
+ /**
13
+ * Format package name to lower case and remove ".ontowiki" suffix
14
+ */
15
+ public function inflectPackageVars($vars)
16
+ {
17
+ $vars['name'] = strtolower($vars['name']);
18
+ $vars['name'] = preg_replace('/.ontowiki$/', '', $vars['name']);
19
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
20
+ $vars['name'] = preg_replace('/-translation$/', '', $vars['name']);
21
+
22
+ return $vars;
23
+ }
24
+ }
vendor/composer/installers/src/Composer/Installers/OsclassInstaller.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+
5
+ class OsclassInstaller extends BaseInstaller
6
+ {
7
+
8
+ protected $locations = array(
9
+ 'plugin' => 'oc-content/plugins/{$name}/',
10
+ 'theme' => 'oc-content/themes/{$name}/',
11
+ 'language' => 'oc-content/languages/{$name}/',
12
+ );
13
+
14
+ }
vendor/composer/installers/src/Composer/Installers/OxidInstaller.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\Package\PackageInterface;
5
+
6
+ class OxidInstaller extends BaseInstaller
7
+ {
8
+ const VENDOR_PATTERN = '/^modules\/(?P<vendor>.+)\/.+/';
9
+
10
+ protected $locations = array(
11
+ 'module' => 'modules/{$name}/',
12
+ 'theme' => 'application/views/{$name}/',
13
+ 'out' => 'out/{$name}/',
14
+ );
15
+
16
+ /**
17
+ * getInstallPath
18
+ *
19
+ * @param PackageInterface $package
20
+ * @param string $frameworkType
21
+ * @return string
22
+ */
23
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
24
+ {
25
+ $installPath = parent::getInstallPath($package, $frameworkType);
26
+ $type = $this->package->getType();
27
+ if ($type === 'oxid-module') {
28
+ $this->prepareVendorDirectory($installPath);
29
+ }
30
+ return $installPath;
31
+ }
32
+
33
+ /**
34
+ * prepareVendorDirectory
35
+ *
36
+ * Makes sure there is a vendormetadata.php file inside
37
+ * the vendor folder if there is a vendor folder.
38
+ *
39
+ * @param string $installPath
40
+ * @return void
41
+ */
42
+ protected function prepareVendorDirectory($installPath)
43
+ {
44
+ $matches = '';
45
+ $hasVendorDirectory = preg_match(self::VENDOR_PATTERN, $installPath, $matches);
46
+ if (!$hasVendorDirectory) {
47
+ return;
48
+ }
49
+
50
+ $vendorDirectory = $matches['vendor'];
51
+ $vendorPath = getcwd() . '/modules/' . $vendorDirectory;
52
+ if (!file_exists($vendorPath)) {
53
+ mkdir($vendorPath, 0755, true);
54
+ }
55
+
56
+ $vendorMetaDataPath = $vendorPath . '/vendormetadata.php';
57
+ touch($vendorMetaDataPath);
58
+ }
59
+ }
vendor/composer/installers/src/Composer/Installers/PPIInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PPIInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/PhiftyInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PhiftyInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'bundle' => 'bundles/{$name}/',
8
+ 'library' => 'libraries/{$name}/',
9
+ 'framework' => 'frameworks/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PhpBBInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'extension' => 'ext/{$vendor}/{$name}/',
8
+ 'language' => 'language/{$name}/',
9
+ 'style' => 'styles/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PimcoreInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'plugins/{$name}/',
8
+ );
9
+
10
+ /**
11
+ * Format package name to CamelCase
12
+ */
13
+ public function inflectPackageVars($vars)
14
+ {
15
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
16
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
17
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
18
+
19
+ return $vars;
20
+ }
21
+ }
vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Class PiwikInstaller
6
+ *
7
+ * @package Composer\Installers
8
+ */
9
+ class PiwikInstaller extends BaseInstaller
10
+ {
11
+ /**
12
+ * @var array
13
+ */
14
+ protected $locations = array(
15
+ 'plugin' => 'plugins/{$name}/',
16
+ );
17
+
18
+ /**
19
+ * Format package name to CamelCase
20
+ * @param array $vars
21
+ *
22
+ * @return array
23
+ */
24
+ public function inflectPackageVars($vars)
25
+ {
26
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
27
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
28
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
29
+
30
+ return $vars;
31
+ }
32
+ }
vendor/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PlentymarketsInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => '{$name}/'
8
+ );
9
+
10
+ /**
11
+ * Remove hyphen, "plugin" and format to camelcase
12
+ * @param array $vars
13
+ *
14
+ * @return array
15
+ */
16
+ public function inflectPackageVars($vars)
17
+ {
18
+ $vars['name'] = explode("-", $vars['name']);
19
+ foreach ($vars['name'] as $key => $name) {
20
+ $vars['name'][$key] = ucfirst($vars['name'][$key]);
21
+ if (strcasecmp($name, "Plugin") == 0) {
22
+ unset($vars['name'][$key]);
23
+ }
24
+ }
25
+ $vars['name'] = implode("",$vars['name']);
26
+
27
+ return $vars;
28
+ }
29
+ }
vendor/composer/installers/src/Composer/Installers/Plugin.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ use Composer\Composer;
6
+ use Composer\IO\IOInterface;
7
+ use Composer\Plugin\PluginInterface;
8
+
9
+ class Plugin implements PluginInterface
10
+ {
11
+ private $installer;
12
+
13
+ public function activate(Composer $composer, IOInterface $io)
14
+ {
15
+ $this->installer = new Installer($io, $composer);
16
+ $composer->getInstallationManager()->addInstaller($this->installer);
17
+ }
18
+
19
+ public function deactivate(Composer $composer, IOInterface $io)
20
+ {
21
+ $composer->getInstallationManager()->removeInstaller($this->installer);
22
+ }
23
+
24
+ public function uninstall(Composer $composer, IOInterface $io)
25
+ {
26
+ }
27
+ }
vendor/composer/installers/src/Composer/Installers/PortoInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PortoInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'container' => 'app/Containers/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PrestashopInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/ProcessWireInstaller.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class ProcessWireInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'module' => 'site/modules/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name to CamelCase
13
+ */
14
+ public function inflectPackageVars($vars)
15
+ {
16
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
17
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
18
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
19
+
20
+ return $vars;
21
+ }
22
+ }
vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class PuppetInstaller extends BaseInstaller
6
+ {
7
+
8
+ protected $locations = array(
9
+ 'module' => 'modules/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/PxcmsInstaller.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class PxcmsInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'app/Modules/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format package name.
13
+ *
14
+ * @param array $vars
15
+ *
16
+ * @return array
17
+ */
18
+ public function inflectPackageVars($vars)
19
+ {
20
+ if ($vars['type'] === 'pxcms-module') {
21
+ return $this->inflectModuleVars($vars);
22
+ }
23
+
24
+ if ($vars['type'] === 'pxcms-theme') {
25
+ return $this->inflectThemeVars($vars);
26
+ }
27
+
28
+ return $vars;
29
+ }
30
+
31
+ /**
32
+ * For package type pxcms-module, cut off a trailing '-plugin' if present.
33
+ *
34
+ * return string
35
+ */
36
+ protected function inflectModuleVars($vars)
37
+ {
38
+ $vars['name'] = str_replace('pxcms-', '', $vars['name']); // strip out pxcms- just incase (legacy)
39
+ $vars['name'] = str_replace('module-', '', $vars['name']); // strip out module-
40
+ $vars['name'] = preg_replace('/-module$/', '', $vars['name']); // strip out -module
41
+ $vars['name'] = str_replace('-', '_', $vars['name']); // make -'s be _'s
42
+ $vars['name'] = ucwords($vars['name']); // make module name camelcased
43
+
44
+ return $vars;
45
+ }
46
+
47
+
48
+ /**
49
+ * For package type pxcms-module, cut off a trailing '-plugin' if present.
50
+ *
51
+ * return string
52
+ */
53
+ protected function inflectThemeVars($vars)
54
+ {
55
+ $vars['name'] = str_replace('pxcms-', '', $vars['name']); // strip out pxcms- just incase (legacy)
56
+ $vars['name'] = str_replace('theme-', '', $vars['name']); // strip out theme-
57
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']); // strip out -theme
58
+ $vars['name'] = str_replace('-', '_', $vars['name']); // make -'s be _'s
59
+ $vars['name'] = ucwords($vars['name']); // make module name camelcased
60
+
61
+ return $vars;
62
+ }
63
+ }
vendor/composer/installers/src/Composer/Installers/RadPHPInstaller.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class RadPHPInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'bundle' => 'src/{$name}/'
8
+ );
9
+
10
+ /**
11
+ * Format package name to CamelCase
12
+ */
13
+ public function inflectPackageVars($vars)
14
+ {
15
+ $nameParts = explode('/', $vars['name']);
16
+ foreach ($nameParts as &$value) {
17
+ $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
18
+ $value = str_replace(array('-', '_'), ' ', $value);
19
+ $value = str_replace(' ', '', ucwords($value));
20
+ }
21
+ $vars['name'] = implode('/', $nameParts);
22
+ return $vars;
23
+ }
24
+ }
vendor/composer/installers/src/Composer/Installers/ReIndexInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ReIndexInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'theme' => 'themes/{$name}/',
8
+ 'plugin' => 'plugins/{$name}/'
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class Redaxo5Installer extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'addon' => 'redaxo/src/addons/{$name}/',
8
+ 'bestyle-plugin' => 'redaxo/src/addons/be_style/plugins/{$name}/'
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class RedaxoInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'addon' => 'redaxo/include/addons/{$name}/',
8
+ 'bestyle-plugin' => 'redaxo/include/addons/be_style/plugins/{$name}/'
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class RoundcubeInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'plugins/{$name}/',
8
+ );
9
+
10
+ /**
11
+ * Lowercase name and changes the name to a underscores
12
+ *
13
+ * @param array $vars
14
+ * @return array
15
+ */
16
+ public function inflectPackageVars($vars)
17
+ {
18
+ $vars['name'] = strtolower(str_replace('-', '_', $vars['name']));
19
+
20
+ return $vars;
21
+ }
22
+ }
vendor/composer/installers/src/Composer/Installers/SMFInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class SMFInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'Sources/{$name}/',
8
+ 'theme' => 'Themes/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Plugin/theme installer for shopware
6
+ * @author Benjamin Boit
7
+ */
8
+ class ShopwareInstaller extends BaseInstaller
9
+ {
10
+ protected $locations = array(
11
+ 'backend-plugin' => 'engine/Shopware/Plugins/Local/Backend/{$name}/',
12
+ 'core-plugin' => 'engine/Shopware/Plugins/Local/Core/{$name}/',
13
+ 'frontend-plugin' => 'engine/Shopware/Plugins/Local/Frontend/{$name}/',
14
+ 'theme' => 'templates/{$name}/',
15
+ 'plugin' => 'custom/plugins/{$name}/',
16
+ 'frontend-theme' => 'themes/Frontend/{$name}/',
17
+ );
18
+
19
+ /**
20
+ * Transforms the names
21
+ * @param array $vars
22
+ * @return array
23
+ */
24
+ public function inflectPackageVars($vars)
25
+ {
26
+ if ($vars['type'] === 'shopware-theme') {
27
+ return $this->correctThemeName($vars);
28
+ }
29
+
30
+ return $this->correctPluginName($vars);
31
+ }
32
+
33
+ /**
34
+ * Changes the name to a camelcased combination of vendor and name
35
+ * @param array $vars
36
+ * @return array
37
+ */
38
+ private function correctPluginName($vars)
39
+ {
40
+ $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) {
41
+ return strtoupper($matches[0][1]);
42
+ }, $vars['name']);
43
+
44
+ $vars['name'] = ucfirst($vars['vendor']) . ucfirst($camelCasedName);
45
+
46
+ return $vars;
47
+ }
48
+
49
+ /**
50
+ * Changes the name to a underscore separated name
51
+ * @param array $vars
52
+ * @return array
53
+ */
54
+ private function correctThemeName($vars)
55
+ {
56
+ $vars['name'] = str_replace('-', '_', $vars['name']);
57
+
58
+ return $vars;
59
+ }
60
+ }
vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ use Composer\Package\PackageInterface;
5
+
6
+ class SilverStripeInstaller extends BaseInstaller
7
+ {
8
+ protected $locations = array(
9
+ 'module' => '{$name}/',
10
+ 'theme' => 'themes/{$name}/',
11
+ );
12
+
13
+ /**
14
+ * Return the install path based on package type.
15
+ *
16
+ * Relies on built-in BaseInstaller behaviour with one exception: silverstripe/framework
17
+ * must be installed to 'sapphire' and not 'framework' if the version is <3.0.0
18
+ *
19
+ * @param PackageInterface $package
20
+ * @param string $frameworkType
21
+ * @return string
22
+ */
23
+ public function getInstallPath(PackageInterface $package, $frameworkType = '')
24
+ {
25
+ if (
26
+ $package->getName() == 'silverstripe/framework'
27
+ && preg_match('/^\d+\.\d+\.\d+/', $package->getVersion())
28
+ && version_compare($package->getVersion(), '2.999.999') < 0
29
+ ) {
30
+ return $this->templatePath($this->locations['module'], array('name' => 'sapphire'));
31
+ }
32
+
33
+ return parent::getInstallPath($package, $frameworkType);
34
+ }
35
+ }
vendor/composer/installers/src/Composer/Installers/SiteDirectInstaller.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class SiteDirectInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'module' => 'modules/{$vendor}/{$name}/',
9
+ 'plugin' => 'plugins/{$vendor}/{$name}/'
10
+ );
11
+
12
+ public function inflectPackageVars($vars)
13
+ {
14
+ return $this->parseVars($vars);
15
+ }
16
+
17
+ protected function parseVars($vars)
18
+ {
19
+ $vars['vendor'] = strtolower($vars['vendor']) == 'sitedirect' ? 'SiteDirect' : $vars['vendor'];
20
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
21
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
22
+
23
+ return $vars;
24
+ }
25
+ }
vendor/composer/installers/src/Composer/Installers/StarbugInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class StarbugInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ 'custom-module' => 'app/modules/{$name}/',
10
+ 'custom-theme' => 'app/themes/{$name}/'
11
+ );
12
+ }
vendor/composer/installers/src/Composer/Installers/SyDESInstaller.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class SyDESInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'app/modules/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ );
10
+
11
+ /**
12
+ * Format module name.
13
+ *
14
+ * Strip `sydes-` prefix and a trailing '-theme' or '-module' from package name if present.
15
+ *
16
+ * {@inerhitDoc}
17
+ */
18
+ public function inflectPackageVars($vars)
19
+ {
20
+ if ($vars['type'] == 'sydes-module') {
21
+ return $this->inflectModuleVars($vars);
22
+ }
23
+
24
+ if ($vars['type'] === 'sydes-theme') {
25
+ return $this->inflectThemeVars($vars);
26
+ }
27
+
28
+ return $vars;
29
+ }
30
+
31
+ public function inflectModuleVars($vars)
32
+ {
33
+ $vars['name'] = preg_replace('/(^sydes-|-module$)/i', '', $vars['name']);
34
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
35
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
36
+
37
+ return $vars;
38
+ }
39
+
40
+ protected function inflectThemeVars($vars)
41
+ {
42
+ $vars['name'] = preg_replace('/(^sydes-|-theme$)/', '', $vars['name']);
43
+ $vars['name'] = strtolower($vars['name']);
44
+
45
+ return $vars;
46
+ }
47
+ }
vendor/composer/installers/src/Composer/Installers/SyliusInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class SyliusInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'theme' => 'themes/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Plugin installer for symfony 1.x
6
+ *
7
+ * @author Jérôme Tamarelle <jerome@tamarelle.net>
8
+ */
9
+ class Symfony1Installer extends BaseInstaller
10
+ {
11
+ protected $locations = array(
12
+ 'plugin' => 'plugins/{$name}/',
13
+ );
14
+
15
+ /**
16
+ * Format package name to CamelCase
17
+ */
18
+ public function inflectPackageVars($vars)
19
+ {
20
+ $vars['name'] = preg_replace_callback('/(-[a-z])/', function ($matches) {
21
+ return strtoupper($matches[0][1]);
22
+ }, $vars['name']);
23
+
24
+ return $vars;
25
+ }
26
+ }
vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * Extension installer for TYPO3 CMS
6
+ *
7
+ * @deprecated since 1.0.25, use https://packagist.org/packages/typo3/cms-composer-installers instead
8
+ *
9
+ * @author Sascha Egerer <sascha.egerer@dkd.de>
10
+ */
11
+ class TYPO3CmsInstaller extends BaseInstaller
12
+ {
13
+ protected $locations = array(
14
+ 'extension' => 'typo3conf/ext/{$name}/',
15
+ );
16
+ }
vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * An installer to handle TYPO3 Flow specifics when installing packages.
6
+ */
7
+ class TYPO3FlowInstaller extends BaseInstaller
8
+ {
9
+ protected $locations = array(
10
+ 'package' => 'Packages/Application/{$name}/',
11
+ 'framework' => 'Packages/Framework/{$name}/',
12
+ 'plugin' => 'Packages/Plugins/{$name}/',
13
+ 'site' => 'Packages/Sites/{$name}/',
14
+ 'boilerplate' => 'Packages/Boilerplates/{$name}/',
15
+ 'build' => 'Build/{$name}/',
16
+ );
17
+
18
+ /**
19
+ * Modify the package name to be a TYPO3 Flow style key.
20
+ *
21
+ * @param array $vars
22
+ * @return array
23
+ */
24
+ public function inflectPackageVars($vars)
25
+ {
26
+ $autoload = $this->package->getAutoload();
27
+ if (isset($autoload['psr-0']) && is_array($autoload['psr-0'])) {
28
+ $namespace = key($autoload['psr-0']);
29
+ $vars['name'] = str_replace('\\', '.', $namespace);
30
+ }
31
+ if (isset($autoload['psr-4']) && is_array($autoload['psr-4'])) {
32
+ $namespace = key($autoload['psr-4']);
33
+ $vars['name'] = rtrim(str_replace('\\', '.', $namespace), '.');
34
+ }
35
+
36
+ return $vars;
37
+ }
38
+ }
vendor/composer/installers/src/Composer/Installers/TaoInstaller.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ /**
5
+ * An installer to handle TAO extensions.
6
+ */
7
+ class TaoInstaller extends BaseInstaller
8
+ {
9
+ const EXTRA_TAO_EXTENSION_NAME = 'tao-extension-name';
10
+
11
+ protected $locations = array(
12
+ 'extension' => '{$name}'
13
+ );
14
+
15
+ public function inflectPackageVars($vars)
16
+ {
17
+ $extra = $this->package->getExtra();
18
+
19
+ if (array_key_exists(self::EXTRA_TAO_EXTENSION_NAME, $extra)) {
20
+ $vars['name'] = $extra[self::EXTRA_TAO_EXTENSION_NAME];
21
+ return $vars;
22
+ }
23
+
24
+ $vars['name'] = str_replace('extension-', '', $vars['name']);
25
+ $vars['name'] = str_replace('-', ' ', $vars['name']);
26
+ $vars['name'] = lcfirst(str_replace(' ', '', ucwords($vars['name'])));
27
+
28
+ return $vars;
29
+ }
30
+ }
vendor/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class TastyIgniterInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'extension' => 'extensions/{$vendor}/{$name}/',
9
+ 'theme' => 'themes/{$name}/',
10
+ );
11
+
12
+ /**
13
+ * Format package name.
14
+ *
15
+ * Cut off leading 'ti-ext-' or 'ti-theme-' if present.
16
+ * Strip vendor name of characters that is not alphanumeric or an underscore
17
+ *
18
+ */
19
+ public function inflectPackageVars($vars)
20
+ {
21
+ if ($vars['type'] === 'tastyigniter-extension') {
22
+ $vars['vendor'] = preg_replace('/[^a-z0-9_]/i', '', $vars['vendor']);
23
+ $vars['name'] = preg_replace('/^ti-ext-/', '', $vars['name']);
24
+ }
25
+
26
+ if ($vars['type'] === 'tastyigniter-theme') {
27
+ $vars['name'] = preg_replace('/^ti-theme-/', '', $vars['name']);
28
+ }
29
+
30
+ return $vars;
31
+ }
32
+ }
vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class TheliaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'local/modules/{$name}/',
8
+ 'frontoffice-template' => 'templates/frontOffice/{$name}/',
9
+ 'backoffice-template' => 'templates/backOffice/{$name}/',
10
+ 'email-template' => 'templates/email/{$name}/',
11
+ );
12
+ }
vendor/composer/installers/src/Composer/Installers/TuskInstaller.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+ /**
4
+ * Composer installer for 3rd party Tusk utilities
5
+ * @author Drew Ewing <drew@phenocode.com>
6
+ */
7
+ class TuskInstaller extends BaseInstaller
8
+ {
9
+ protected $locations = array(
10
+ 'task' => '.tusk/tasks/{$name}/',
11
+ 'command' => '.tusk/commands/{$name}/',
12
+ 'asset' => 'assets/tusk/{$name}/',
13
+ );
14
+ }
vendor/composer/installers/src/Composer/Installers/UserFrostingInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class UserFrostingInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'sprinkle' => 'app/sprinkles/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/VanillaInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class VanillaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'plugins/{$name}/',
8
+ 'theme' => 'themes/{$name}/',
9
+ );
10
+ }
vendor/composer/installers/src/Composer/Installers/VgmcpInstaller.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class VgmcpInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'bundle' => 'src/{$vendor}/{$name}/',
8
+ 'theme' => 'themes/{$name}/'
9
+ );
10
+
11
+ /**
12
+ * Format package name.
13
+ *
14
+ * For package type vgmcp-bundle, cut off a trailing '-bundle' if present.
15
+ *
16
+ * For package type vgmcp-theme, cut off a trailing '-theme' if present.
17
+ *
18
+ */
19
+ public function inflectPackageVars($vars)
20
+ {
21
+ if ($vars['type'] === 'vgmcp-bundle') {
22
+ return $this->inflectPluginVars($vars);
23
+ }
24
+
25
+ if ($vars['type'] === 'vgmcp-theme') {
26
+ return $this->inflectThemeVars($vars);
27
+ }
28
+
29
+ return $vars;
30
+ }
31
+
32
+ protected function inflectPluginVars($vars)
33
+ {
34
+ $vars['name'] = preg_replace('/-bundle$/', '', $vars['name']);
35
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
36
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
37
+
38
+ return $vars;
39
+ }
40
+
41
+ protected function inflectThemeVars($vars)
42
+ {
43
+ $vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
44
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
45
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
46
+
47
+ return $vars;
48
+ }
49
+ }
vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class WHMCSInstaller extends BaseInstaller
6
+ {
7
+ protected $locations = array(
8
+ 'addons' => 'modules/addons/{$vendor}_{$name}/',
9
+ 'fraud' => 'modules/fraud/{$vendor}_{$name}/',
10
+ 'gateways' => 'modules/gateways/{$vendor}_{$name}/',
11
+ 'notifications' => 'modules/notifications/{$vendor}_{$name}/',
12
+ 'registrars' => 'modules/registrars/{$vendor}_{$name}/',
13
+ 'reports' => 'modules/reports/{$vendor}_{$name}/',
14
+ 'security' => 'modules/security/{$vendor}_{$name}/',
15
+ 'servers' => 'modules/servers/{$vendor}_{$name}/',
16
+ 'social' => 'modules/social/{$vendor}_{$name}/',
17
+ 'support' => 'modules/support/{$vendor}_{$name}/',
18
+ 'templates' => 'templates/{$vendor}_{$name}/',
19
+ 'includes' => 'includes/{$vendor}_{$name}/'
20
+ );
21
+ }
vendor/composer/installers/src/Composer/Installers/WinterInstaller.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class WinterInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$name}/',
8
+ 'plugin' => 'plugins/{$vendor}/{$name}/',
9
+ 'theme' => 'themes/{$name}/'
10
+ );
11
+
12
+ /**
13
+ * Format package name.
14
+ *
15
+ * For package type winter-plugin, cut off a trailing '-plugin' if present.
16
+ *
17
+ * For package type winter-theme, cut off a trailing '-theme' if present.
18
+ *
19
+ */
20
+ public function inflectPackageVars($vars)
21
+ {
22
+ if ($vars['type'] === 'winter-module') {
23
+ return $this->inflectModuleVars($vars);
24
+ }
25
+
26
+ if ($vars['type'] === 'winter-plugin') {
27
+ return $this->inflectPluginVars($vars);
28
+ }
29
+
30
+ if ($vars['type'] === 'winter-theme') {
31
+ return $this->inflectThemeVars($vars);
32
+ }
33
+
34
+ return $vars;
35
+ }
36
+
37
+ protected function inflectModuleVars($vars)
38
+ {
39
+ $vars['name'] = preg_replace('/^wn-|-module$/', '', $vars['name']);
40
+
41
+ return $vars;
42
+ }
43
+
44
+ protected function inflectPluginVars($vars)
45
+ {
46
+ $vars['name'] = preg_replace('/^wn-|-plugin$/', '', $vars['name']);
47
+ $vars['vendor'] = preg_replace('/[^a-z0-9_]/i', '', $vars['vendor']);
48
+
49
+ return $vars;
50
+ }
51
+
52
+ protected function inflectThemeVars($vars)
53
+ {
54
+ $vars['name'] = preg_replace('/^wn-|-theme$/', '', $vars['name']);
55
+
56
+ return $vars;
57
+ }
58
+ }
vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class WolfCMSInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'wolf/plugins/{$name}/',
8
+ );
9
+ }
vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class WordPressInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'plugin' => 'wp-content/plugins/{$name}/',
8
+ 'theme' => 'wp-content/themes/{$name}/',
9
+ 'muplugin' => 'wp-content/mu-plugins/{$name}/',
10
+ 'dropin' => 'wp-content/{$name}/',
11
+ );
12
+ }
vendor/composer/installers/src/Composer/Installers/YawikInstaller.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: cbleek
5
+ * Date: 25.03.16
6
+ * Time: 20:55
7
+ */
8
+
9
+ namespace Composer\Installers;
10
+
11
+
12
+ class YawikInstaller extends BaseInstaller
13
+ {
14
+ protected $locations = array(
15
+ 'module' => 'module/{$name}/',
16
+ );
17
+
18
+ /**
19
+ * Format package name to CamelCase
20
+ * @param array $vars
21
+ *
22
+ * @return array
23
+ */
24
+ public function inflectPackageVars($vars)
25
+ {
26
+ $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name']));
27
+ $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
28
+ $vars['name'] = str_replace(' ', '', ucwords($vars['name']));
29
+
30
+ return $vars;
31
+ }
32
+ }
vendor/composer/installers/src/Composer/Installers/ZendInstaller.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ZendInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'library' => 'library/{$name}/',
8
+ 'extra' => 'extras/library/{$name}/',
9
+ 'module' => 'module/{$name}/',
10
+ );
11
+ }
vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Composer\Installers;
3
+
4
+ class ZikulaInstaller extends BaseInstaller
5
+ {
6
+ protected $locations = array(
7
+ 'module' => 'modules/{$vendor}-{$name}/',
8
+ 'theme' => 'themes/{$vendor}-{$name}/'
9
+ );
10
+ }
vendor/composer/installers/src/bootstrap.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function includeIfExists($file)
3
+ {
4
+ if (file_exists($file)) {
5
+ return include $file;
6
+ }
7
+ }
8
+ if ((!$loader = includeIfExists(__DIR__ . '/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__ . '/../../../autoload.php'))) {
9
+ die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
10
+ 'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
11
+ 'php composer.phar install'.PHP_EOL);
12
+ }
13
+ return $loader;
vendor/composer/platform_check.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // platform_check.php @generated by Composer
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) {
12
+ if (!headers_sent()) {
13
+ header('HTTP/1.1 500 Internal Server Error');
14
+ }
15
+ if (!ini_get('display_errors')) {
16
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18
+ } elseif (!headers_sent()) {
19
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20
+ }
21
+ }
22
+ trigger_error(
23
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
24
+ E_USER_ERROR
25
+ );
26
+ }
vendor/woocommerce/action-scheduler-job-framework/LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
vendor/woocommerce/action-scheduler-job-framework/README.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Action Scheduler Job Framework
2
+
3
+ ## Requirements
4
+
5
+ - PHP 7.0+
6
+
7
+ ## Chained Jobs
8
+
9
+ A "chained job" is a kind of batched job that creates follow-up actions in a chain until a set of items has been processed.
10
+
11
+ Each "batch" in the job is a separate "scheduled action". Each batch is numbered and should be limited to process a set number of items.
12
+
13
+ ### Methods
14
+
15
+ - `::handle_start()` - Runs before a job instance starts.
16
+ - `::handle_end()` - Runs after a job instance ends.
17
+ - `::get_items_for_batch()` - Gets a set of items for the batch to process.
18
+ - `::process_item()` - Processes a single item.
19
+ - `::get_name()` - Get the unique name/ID of the job.
20
+ - `::get_number_of_items_processed()` - Get the number of items that have been processed by the currently running instance of the job.
21
+ - `::is_running()` - Check if this job is currently running. Checks if there is any "start" or "batch" actions `pending` or `in-progress` for the job.
22
+
23
+ ### Example:
24
+
25
+ ```php
26
+
27
+ class GenerateProductFeed extends Automattic\WooCommerce\ActionSchedulerJobFramework\AbstractChainedJob {
28
+
29
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities\BatchQueryOffset;
30
+
31
+ /**
32
+ * Runs before starting the job.
33
+ */
34
+ protected function handle_start() {
35
+ // Optionally do something when starting the job.
36
+ }
37
+
38
+ /**
39
+ * Runs after the finishing the job.
40
+ */
41
+ protected function handle_end() {
42
+ // Optionally do something when ending the job.
43
+ }
44
+
45
+ /**
46
+ * Get a set of items for the batch.
47
+ *
48
+ * NOTE: when using an OFFSET based query to retrieve items it's recommended to order by the item ID while
49
+ * ASCENDING. This is so that any newly added items will not disrupt the query offset.
50
+ *
51
+ * @param int $batch_number The batch number increments for each new batch in the job cycle.
52
+ * @param array $args The args for the job.
53
+ *
54
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
55
+ */
56
+ protected function get_items_for_batch( int $batch_number, array $args ): array {
57
+ $product_args = [
58
+ 'fields' => 'ids',
59
+ 'post_status' => 'publish',
60
+ 'post_type' => [ 'product', 'product_variation' ],
61
+ 'posts_per_page' => $this->get_batch_size(),
62
+ 'offset' => $this->get_query_offset( $batch_number ),
63
+ 'orderby' => 'ID',
64
+ 'order' => 'ASC',
65
+ ];
66
+
67
+ $query = new WP_Query( $product_args );
68
+ return $query->posts;
69
+ }
70
+
71
+ /**
72
+ * Process a single item.
73
+ *
74
+ * @param string|int|array $item A single item from the get_items_for_batch() method.
75
+ * @param array $args The args for the job.
76
+ *
77
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
78
+ */
79
+ protected function process_item( $item, array $args ) {
80
+ // Process each item here.
81
+ }
82
+
83
+ /**
84
+ * Get the name/slug of the job.
85
+ *
86
+ * @return string
87
+ */
88
+ public function get_name(): string {
89
+ return 'generate_feed';
90
+ }
91
+
92
+ /**
93
+ * Get the name/slug of the plugin that owns the job.
94
+ *
95
+ * @return string
96
+ */
97
+ public function get_plugin_name(): string {
98
+ return 'facebook_for_woocommerce';
99
+ }
100
+
101
+ }
102
+
103
+
104
+ add_action( 'init', function() {
105
+ $job = new GenerateProductFeed(
106
+ new \Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies\ActionScheduler()
107
+ );
108
+ $job->init();
109
+
110
+ // Start the job if it's not already running
111
+ if ( ! $job->is_running() ) {
112
+ $job->queue_start();
113
+ }
114
+ });
115
+
116
+ ```
vendor/woocommerce/action-scheduler-job-framework/phpcs.xml.dist ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <ruleset name="WordPress Coding Standards">
3
+ <!-- See https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml -->
4
+ <!-- See https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/blob/develop/WordPress-Core/ruleset.xml -->
5
+
6
+ <description>WooCommerce dev PHP_CodeSniffer ruleset.</description>
7
+
8
+ <!-- Exclude paths -->
9
+ <exclude-pattern>*/node_modules/*</exclude-pattern>
10
+ <exclude-pattern>*/vendor/*</exclude-pattern>
11
+ <exclude-pattern>bin/*</exclude-pattern>
12
+
13
+ <!-- Configs -->
14
+ <config name="minimum_supported_wp_version" value="4.7" />
15
+ <config name="testVersion" value="5.6-"/>
16
+
17
+ <!-- Rules -->
18
+ <rule ref="WooCommerce-Core" />
19
+
20
+ <rule ref="WordPress.WP.I18n">
21
+ <properties>
22
+ <property name="text_domain" type="array" value="action-scheduler-job-framework" />
23
+ </properties>
24
+ </rule>
25
+
26
+ <rule ref="PHPCompatibility">
27
+ <exclude-pattern>tests/</exclude-pattern>
28
+ </rule>
29
+
30
+ <rule ref="WordPress.Files.FileName.InvalidClassFileName">
31
+ <exclude-pattern>src/*</exclude-pattern>
32
+ <exclude-pattern>tests/*</exclude-pattern>
33
+ </rule>
34
+
35
+ <rule ref="WordPress.Files.FileName.NotHyphenatedLowercase">
36
+ <exclude-pattern>src/*</exclude-pattern>
37
+ </rule>
38
+
39
+ <rule ref="Generic.Arrays.DisallowShortArraySyntax.Found">
40
+ <exclude-pattern>src/*</exclude-pattern>
41
+ <exclude-pattern>tests/*</exclude-pattern>
42
+ </rule>
43
+
44
+ <rule ref="Generic.Commenting">
45
+ <exclude-pattern>tests/</exclude-pattern>
46
+ </rule>
47
+ <rule ref="Squiz.Commenting.FileComment.MissingPackageTag">
48
+ <exclude-pattern>src/</exclude-pattern>
49
+ <exclude-pattern>tests/</exclude-pattern>
50
+ </rule>
51
+ <rule ref="Squiz.Commenting.FileComment.Missing">
52
+ <exclude-pattern>src/</exclude-pattern>
53
+ <exclude-pattern>tests/</exclude-pattern>
54
+ </rule>
55
+
56
+ <arg name="basepath" value="."/>
57
+ </ruleset>
vendor/woocommerce/action-scheduler-job-framework/src/AbstractChainedJob.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework;
5
+
6
+ use ActionScheduler_Action;
7
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities\BatchSize;
8
+ use Exception;
9
+
10
+ defined( 'ABSPATH' ) || exit;
11
+
12
+ /**
13
+ * Class AbstractChainedJob.
14
+ *
15
+ * A "chained job" is a kind of batched job that creates follow-up actions until all items in the job have been processed.
16
+ *
17
+ * Each "batch" in the job is a separate "scheduled action". Each batch is numbered and should be limited to process a
18
+ * set number of items.
19
+ *
20
+ * Only a single chained job can run at any one time.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ abstract class AbstractChainedJob extends AbstractJob implements ChainedJobInterface {
25
+
26
+ use BatchSize;
27
+
28
+ /**
29
+ * Get a set of items for the batch.
30
+ *
31
+ * NOTE: when using an OFFSET based query to retrieve items it's recommended to order by the item ID while
32
+ * ASCENDING. This is so that any newly added items will not disrupt the query offset.
33
+ *
34
+ * @param int $batch_number The batch number increments for each new batch in the job cycle.
35
+ * @param array $args The args for the job.
36
+ *
37
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
38
+ */
39
+ abstract protected function get_items_for_batch( int $batch_number, array $args ): array;
40
+
41
+ /**
42
+ * Process a single item.
43
+ *
44
+ * @param string|int|array $item A single item from the get_items_for_batch() method.
45
+ * @param array $args The args for the job.
46
+ *
47
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
48
+ */
49
+ abstract protected function process_item( $item, array $args );
50
+
51
+ /**
52
+ * Called before starting the job.
53
+ */
54
+ protected function handle_start() {
55
+ // Optionally override this method in child class.
56
+ }
57
+
58
+ /**
59
+ * Called after the finishing the job.
60
+ */
61
+ protected function handle_end() {
62
+ // Optionally override this method in child class.
63
+ }
64
+
65
+ /**
66
+ * Init the job, register necessary WP actions.
67
+ */
68
+ public function init() {
69
+ add_action( $this->get_action_full_name( self::CHAIN_START ), [ $this, 'handle_start_action' ] );
70
+ add_action( $this->get_action_full_name( self::CHAIN_BATCH ), [ $this, 'handle_batch_action' ], 10, 2 );
71
+ add_action( $this->get_action_full_name( self::CHAIN_END ), [ $this, 'handle_end_action' ] );
72
+ }
73
+
74
+ /**
75
+ * Queue the job to be started in the background.
76
+ *
77
+ * @param array $args The args for the job.
78
+ */
79
+ public function queue_start( array $args = [] ) {
80
+ $this->schedule_immediate_action( self::CHAIN_START, [ $args ] );
81
+ }
82
+
83
+ /**
84
+ * Queue a batch to be processed immediately.
85
+ *
86
+ * @param int $batch_number The batch number for the new batch.
87
+ * @param array $args The args for the job.
88
+ */
89
+ protected function queue_batch( int $batch_number, array $args ) {
90
+ $this->schedule_immediate_action( self::CHAIN_BATCH, [ $batch_number, $args ] );
91
+ }
92
+
93
+ /**
94
+ * Queue the job to be ended.
95
+ *
96
+ * Should be called once all items are processed.
97
+ *
98
+ * @param array $args The args for the job.
99
+ */
100
+ protected function queue_end( array $args = [] ) {
101
+ $this->schedule_immediate_action( self::CHAIN_END, [ $args ] );
102
+ }
103
+
104
+ /**
105
+ * Handles job start action.
106
+ *
107
+ * @hooked {plugin_name}/jobs/{job_name}/chain_start
108
+ *
109
+ * @param array $args The args for the job.
110
+ *
111
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
112
+ */
113
+ public function handle_start_action( array $args ) {
114
+ // Prevent starting if a job already has scheduled batch actions
115
+ $batch_action_name = $this->get_action_full_name( self::CHAIN_BATCH );
116
+ if ( $this->action_scheduler->next_scheduled_action( $batch_action_name, null, $this->get_group_name() ) ) {
117
+ throw new Exception( 'This job is already running.' );
118
+ }
119
+
120
+ $this->handle_start();
121
+ $this->queue_batch( 1, $args );
122
+ }
123
+
124
+ /**
125
+ * Handle processing a chain batch.
126
+ *
127
+ * @hooked {plugin_name}/jobs/{job_name}/chain_batch
128
+ *
129
+ * @param int $batch_number The batch number for the new batch.
130
+ * @param array $args The args for the job.
131
+ *
132
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
133
+ */
134
+ public function handle_batch_action( int $batch_number, array $args ) {
135
+ $items = $this->get_items_for_batch( $batch_number, $args );
136
+
137
+ if ( empty( $items ) ) {
138
+ // No more items to process so end the job chain
139
+ $this->queue_end( $args );
140
+ } else {
141
+ $items = $this->filter_items_before_processing( $items );
142
+
143
+ foreach ( $items as $item ) {
144
+ $this->process_item( $item, $args );
145
+ }
146
+
147
+ // If there were items, queue another batch.
148
+ $this->queue_batch( $batch_number + 1, $args );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Filter-like function that runs before items in a batch are processed.
154
+ *
155
+ * This could be useful if the results from the initial batch query need to be filtered in an additional query.
156
+ *
157
+ * @param array $items
158
+ *
159
+ * @return array
160
+ */
161
+ protected function filter_items_before_processing( array $items ): array {
162
+ return $items;
163
+ }
164
+
165
+ /**
166
+ * Handles job end action.
167
+ *
168
+ * @hooked {plugin_name}/jobs/{job_name}/chain_end
169
+ *
170
+ * @param array $args The args for the job.
171
+ *
172
+ * @throws Exception On error. The failure will be logged by Action Scheduler.
173
+ */
174
+ public function handle_end_action( array $args ) {
175
+ $this->handle_end();
176
+ }
177
+
178
+ /**
179
+ * Check if this job is running.
180
+ *
181
+ * Checks if there is any "start" or "batch" actions pending or in-progress for this job.
182
+ *
183
+ * @return bool
184
+ */
185
+ public function is_running(): bool {
186
+ $start_action = $this->get_action_full_name( self::CHAIN_START );
187
+ $batch_action = $this->get_action_full_name( self::CHAIN_BATCH );
188
+
189
+ if ( $this->action_scheduler->next_scheduled_action( $start_action, null, $this->get_group_name() ) ) {
190
+ return true;
191
+ }
192
+ if ( $this->action_scheduler->next_scheduled_action( $batch_action, null, $this->get_group_name() ) ) {
193
+ return true;
194
+ }
195
+
196
+ return false;
197
+ }
198
+
199
+ /**
200
+ * Get the number of items processed by the currently running job.
201
+ *
202
+ * @return int Returns the number of items processed. Will return zero if the job isn't running.
203
+ */
204
+ public function get_number_of_items_processed(): int {
205
+ $batch_action_name = $this->get_action_full_name( self::CHAIN_BATCH );
206
+
207
+ $in_progress_actions = $this->action_scheduler->search(
208
+ [
209
+ 'hook' => $batch_action_name,
210
+ 'per_page' => 1,
211
+ 'status' => $this->action_scheduler::STATUS_RUNNING,
212
+ ]
213
+ );
214
+
215
+ if ( $in_progress_actions ) {
216
+ return $this->calculate_items_processed_from_batch_action( current( $in_progress_actions ) );
217
+ }
218
+
219
+ $pending_actions = $this->action_scheduler->search(
220
+ [
221
+ 'hook' => $batch_action_name,
222
+ 'per_page' => 1,
223
+ 'status' => $this->action_scheduler::STATUS_PENDING,
224
+ ]
225
+ );
226
+
227
+ if ( $pending_actions ) {
228
+ return $this->calculate_items_processed_from_batch_action( current( $pending_actions ) );
229
+ }
230
+
231
+ return 0;
232
+ }
233
+
234
+ /**
235
+ * Calculate the number of items processed by the job based on a given scheduled batch action.
236
+ *
237
+ * @param ActionScheduler_Action $action The most recent batch action.
238
+ *
239
+ * @return int
240
+ */
241
+ protected function calculate_items_processed_from_batch_action( ActionScheduler_Action $action ): int {
242
+ $args = $action->get_args();
243
+
244
+ // The batch number is the first action arg, take 1 because it's not been fully processed yet
245
+ $number_of_batches_processed = $args[0] - 1;
246
+
247
+ // Use max() to not allow a negative value
248
+ return max( 0, $this->get_batch_size() * $number_of_batches_processed );
249
+ }
250
+
251
+ }
vendor/woocommerce/action-scheduler-job-framework/src/AbstractJob.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework;
5
+
6
+ use Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies\ActionSchedulerInterface;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * AbstractJob class.
12
+ *
13
+ * Abstract class for jobs that use ActionScheduler.
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ abstract class AbstractJob implements JobInterface {
18
+
19
+ /**
20
+ * @var ActionSchedulerInterface
21
+ */
22
+ protected $action_scheduler;
23
+
24
+ /**
25
+ * AbstractJob constructor.
26
+ *
27
+ * @param ActionSchedulerInterface $action_scheduler
28
+ */
29
+ public function __construct( ActionSchedulerInterface $action_scheduler ) {
30
+ $this->action_scheduler = $action_scheduler;
31
+ }
32
+
33
+ /**
34
+ * Get the base name for the job's scheduled actions.
35
+ *
36
+ * @return string
37
+ */
38
+ protected function get_action_basename(): string {
39
+ return "{$this->get_plugin_name()}/jobs/{$this->get_name()}/";
40
+ }
41
+
42
+ /**
43
+ * Get a job action's full name.
44
+ *
45
+ * @param string $short_name The name of the action without the basename.
46
+ *
47
+ * @return string The full hook name with the basename prepended.
48
+ */
49
+ protected function get_action_full_name( string $short_name ): string {
50
+ return $this->get_action_basename() . $short_name;
51
+ }
52
+
53
+ /**
54
+ * Schedule an immediate action for the job.
55
+ *
56
+ * @param string $action_short_name
57
+ * @param array $args
58
+ */
59
+ protected function schedule_immediate_action( string $action_short_name, array $args ) {
60
+ $this->action_scheduler->schedule_immediate(
61
+ $this->get_action_full_name( $action_short_name ),
62
+ $args,
63
+ $this->get_group_name()
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Get the action group name/slug for the job.
69
+ *
70
+ * @return string
71
+ */
72
+ public function get_group_name(): string {
73
+ return '';
74
+ }
75
+
76
+ }
vendor/woocommerce/action-scheduler-job-framework/src/ChainedJobInterface.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework;
5
+
6
+ use Exception;
7
+
8
+ defined( 'ABSPATH' ) || exit;
9
+
10
+ /**
11
+ * Interface ChainedJobInterface.
12
+ *
13
+ * @see AbstractChainedJob
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ interface ChainedJobInterface extends JobInterface {
18
+
19
+ const CHAIN_START = 'chain_start';
20
+ const CHAIN_BATCH = 'chain_batch';
21
+ const CHAIN_END = 'chain_end';
22
+
23
+ /**
24
+ * Queue the job to be started in the background.
25
+ *
26
+ * @param array $args Set args to be available during the job.
27
+ */
28
+ public function queue_start( array $args = [] );
29
+
30
+ /**
31
+ * Handles job start action.
32
+ *
33
+ * @hooked {plugin_name}/jobs/{job_name}/chain_start
34
+ *
35
+ * @param array $args The args for the job.
36
+ *
37
+ * @throws Exception If an error occurs. Exceptions will be logged by Action Scheduler.
38
+ */
39
+ public function handle_start_action( array $args );
40
+
41
+ /**
42
+ * Handles job process batch action.
43
+ *
44
+ * @hooked {plugin_name}/jobs/{job_name}/chain_batch
45
+ *
46
+ * @param int $batch_number The batch number for the new batch.
47
+ * @param array $args The args for the job.
48
+ *
49
+ * @throws Exception If an error occurs. Exceptions will be logged by Action Scheduler.
50
+ */
51
+ public function handle_batch_action( int $batch_number, array $args );
52
+
53
+ /**
54
+ * Handles job end action.
55
+ *
56
+ * @hooked {plugin_name}/jobs/{job_name}/chain_end
57
+ *
58
+ * @param array $args The args for the job.
59
+ *
60
+ * @throws Exception If an error occurs. Exceptions will be logged by Action Scheduler.
61
+ */
62
+ public function handle_end_action( array $args );
63
+
64
+ /**
65
+ * Get the number of items processed by the currently running job.
66
+ *
67
+ * @return int Returns the number of items processed. Will return zero if the job isn't running.
68
+ */
69
+ public function get_number_of_items_processed(): int;
70
+
71
+
72
+ }
vendor/woocommerce/action-scheduler-job-framework/src/JobInterface.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework;
5
+
6
+ defined( 'ABSPATH' ) || exit;
7
+
8
+ /**
9
+ * Interface JobInterface.
10
+ *
11
+ * @since 1.0.0
12
+ */
13
+ interface JobInterface {
14
+
15
+ /**
16
+ * Init the job, register necessary WP actions.
17
+ */
18
+ public function init();
19
+
20
+ /**
21
+ * Get the name/slug of the job.
22
+ *
23
+ * @return string
24
+ */
25
+ public function get_name(): string;
26
+
27
+ /**
28
+ * Get the name/slug of the plugin that owns the job.
29
+ *
30
+ * @return string
31
+ */
32
+ public function get_plugin_name(): string;
33
+
34
+ /**
35
+ * Get the action group name/slug for the job.
36
+ *
37
+ * @return string
38
+ */
39
+ public function get_group_name(): string;
40
+
41
+ /**
42
+ * Check if this job is running.
43
+ *
44
+ * @return bool
45
+ */
46
+ public function is_running(): bool;
47
+
48
+ }
vendor/woocommerce/action-scheduler-job-framework/src/Proxies/ActionScheduler.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies;
5
+
6
+ defined( 'ABSPATH' ) || exit;
7
+
8
+ /**
9
+ * Class ActionScheduler
10
+ *
11
+ * Proxy for ActionScheduler's public functions.
12
+ *
13
+ * @since 1.0.0
14
+ */
15
+ class ActionScheduler implements ActionSchedulerInterface {
16
+
17
+ /**
18
+ * Schedule an action to run once at some time in the future
19
+ *
20
+ * @param int $timestamp When the job will run.
21
+ * @param string $hook The hook to trigger.
22
+ * @param array $args Arguments to pass when the hook triggers.
23
+ * @param string $group The group to assign this job to.
24
+ *
25
+ * @return string The action ID.
26
+ */
27
+ public function schedule_single( $timestamp, $hook, $args = [], string $group = '' ) {
28
+ return as_schedule_single_action( $timestamp, $hook, $args, $group );
29
+ }
30
+
31
+ /**
32
+ * Schedule an action to run now i.e. in the next available batch.
33
+ *
34
+ * This differs from async actions by having a scheduled time rather than being set for '0000-00-00 00:00:00'.
35
+ * We could use an async action instead but they can't be viewed easily in the admin area
36
+ * because the table is sorted by schedule date.
37
+ *
38
+ * @param string $hook The hook to trigger.
39
+ * @param array $args Arguments to pass when the hook triggers.
40
+ * @param string $group The group to assign this job to.
41
+ *
42
+ * @return string The action ID.
43
+ */
44
+ public function schedule_immediate( string $hook, $args = [], string $group = '' ) {
45
+ return as_schedule_single_action( gmdate( 'U' ) - 1, $hook, $args, $group );
46
+ }
47
+
48
+ /**
49
+ * Check if there is an existing action in the queue with a given hook, args and group combination.
50
+ *
51
+ * An action in the queue could be pending, in-progress or async. If the is pending for a time in
52
+ * future, its scheduled date will be returned as a timestamp. If it is currently being run, or an
53
+ * async action sitting in the queue waiting to be processed, in which case boolean true will be
54
+ * returned. Or there may be no async, in-progress or pending action for this hook, in which case,
55
+ * boolean false will be the return value.
56
+ *
57
+ * @param string $hook
58
+ * @param array $args
59
+ * @param string $group The group to check for jobs.
60
+ *
61
+ * @return int|bool The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action.
62
+ */
63
+ public function next_scheduled_action( $hook, $args = null, string $group = '' ) {
64
+ return as_next_scheduled_action( $hook, $args, $group );
65
+ }
66
+
67
+ /**
68
+ * Search for scheduled actions.
69
+ *
70
+ * @param array $args See as_get_scheduled_actions() for possible arguments.
71
+ * @param string $return_format OBJECT, ARRAY_A, or ids.
72
+ * @param string $group The group to search for jobs.
73
+ *
74
+ * @return array
75
+ */
76
+ public function search( $args = [], $return_format = OBJECT, string $group = '' ) {
77
+ $args['group'] = $group;
78
+
79
+ return as_get_scheduled_actions( $args, $return_format );
80
+ }
81
+
82
+ /**
83
+ * Cancel the next scheduled instance of an action with a matching hook (and optionally matching args and group).
84
+ *
85
+ * Any recurring actions with a matching hook should also be cancelled, not just the next scheduled action.
86
+ *
87
+ * @param string $hook The hook that the job will trigger.
88
+ * @param array $args Args that would have been passed to the job.
89
+ * @param string $group The group the job is assigned to.
90
+ *
91
+ * @return string|null The scheduled action ID if a scheduled action was found, or null if no matching action found.
92
+ */
93
+ public function cancel( string $hook, $args = [], string $group = '' ) {
94
+ return as_unschedule_action( $hook, $args, $group );
95
+ }
96
+
97
+ }
vendor/woocommerce/action-scheduler-job-framework/src/Proxies/ActionSchedulerInterface.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework\Proxies;
5
+
6
+ /**
7
+ * Interface ActionSchedulerInterface
8
+ *
9
+ * Proxy for ActionScheduler's public functions.
10
+ *
11
+ * @since 1.0.0
12
+ */
13
+ interface ActionSchedulerInterface {
14
+
15
+ const STATUS_COMPLETE = 'complete';
16
+ const STATUS_PENDING = 'pending';
17
+ const STATUS_RUNNING = 'in-progress';
18
+ const STATUS_FAILED = 'failed';
19
+ const STATUS_CANCELED = 'canceled';
20
+
21
+ /**
22
+ * Schedule an action to run once at some time in the future
23
+ *
24
+ * @param int $timestamp When the job will run.
25
+ * @param string $hook The hook to trigger.
26
+ * @param array $args Arguments to pass when the hook triggers.
27
+ * @param string $group The group to assign this job to.
28
+ *
29
+ * @return string The action ID.
30
+ */
31
+ public function schedule_single( $timestamp, $hook, $args = [], string $group = '' );
32
+
33
+ /**
34
+ * Schedule an action to run now i.e. in the next available batch.
35
+ *
36
+ * This differs from async actions by having a scheduled time rather than being set for '0000-00-00 00:00:00'.
37
+ * We could use an async action instead but they can't be viewed easily in the admin area
38
+ * because the table is sorted by schedule date.
39
+ *
40
+ * @param string $hook The hook to trigger.
41
+ * @param array $args Arguments to pass when the hook triggers.
42
+ * @param string $group The group to assign this job to.
43
+ *
44
+ * @return string The action ID.
45
+ */
46
+ public function schedule_immediate( string $hook, $args = [], string $group = '' );
47
+
48
+ /**
49
+ * Check if there is an existing action in the queue with a given hook, args and group combination.
50
+ *
51
+ * An action in the queue could be pending, in-progress or async. If the is pending for a time in
52
+ * future, its scheduled date will be returned as a timestamp. If it is currently being run, or an
53
+ * async action sitting in the queue waiting to be processed, in which case boolean true will be
54
+ * returned. Or there may be no async, in-progress or pending action for this hook, in which case,
55
+ * boolean false will be the return value.
56
+ *
57
+ * @param string $hook
58
+ * @param array $args
59
+ * @param string $group The group to check for jobs.
60
+ *
61
+ * @return int|bool The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action.
62
+ */
63
+ public function next_scheduled_action( $hook, $args = null, string $group = '' );
64
+
65
+ /**
66
+ * Search for scheduled actions.
67
+ *
68
+ * @param array $args See as_get_scheduled_actions() for possible arguments.
69
+ * @param string $return_format OBJECT, ARRAY_A, or ids.
70
+ * @param string $group The group to search for jobs.
71
+ *
72
+ * @return array
73
+ */
74
+ public function search( $args = [], $return_format = OBJECT, string $group = '' );
75
+
76
+ /**
77
+ * Cancel the next scheduled instance of an action with a matching hook (and optionally matching args and group).
78
+ *
79
+ * Any recurring actions with a matching hook should also be cancelled, not just the next scheduled action.
80
+ *
81
+ * @param string $hook The hook that the job will trigger.
82
+ * @param array $args Args that would have been passed to the job.
83
+ * @param string $group The group the job is assigned to.
84
+ *
85
+ * @return string|null The scheduled action ID if a scheduled action was found, or null if no matching action found.
86
+ */
87
+ public function cancel( string $hook, $args = [], string $group = '' );
88
+
89
+ }
vendor/woocommerce/action-scheduler-job-framework/src/Utilities/BatchQueryOffset.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities;
5
+
6
+ /**
7
+ * Trait BatchQueryOffset
8
+ *
9
+ * @since 1.0.0
10
+ */
11
+ trait BatchQueryOffset {
12
+
13
+ use BatchSize;
14
+
15
+ /**
16
+ * Get the query offset based on a given batch number and the specified batch size.
17
+ *
18
+ * @param int $batch_number
19
+ *
20
+ * @return int
21
+ */
22
+ protected function get_query_offset( int $batch_number ): int {
23
+ return $this->get_batch_size() * ( $batch_number - 1 );
24
+ }
25
+
26
+ }
vendor/woocommerce/action-scheduler-job-framework/src/Utilities/BatchSize.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ declare( strict_types=1 );
3
+
4
+ namespace Automattic\WooCommerce\ActionSchedulerJobFramework\Utilities;
5
+
6
+ /**
7
+ * Trait BatchSize
8
+ *
9
+ * @since 1.0.0
10
+ */
11
+ trait BatchSize {
12
+
13
+ /**
14
+ * Get the job's batch size.
15
+ *
16
+ * @return int
17
+ */
18
+ protected function get_batch_size(): int {
19
+ return 10;
20
+ }
21
+
22
+ }
webpack.config.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Load the default @wordpress/scripts config object
2
+ const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
3
+
4
+ // Legacy jQuery UI powered admin files
5
+ const jQueryUIAdminFileNames = [
6
+ 'google-product-category-fields',
7
+ 'infobanner',
8
+ 'metabox',
9
+ 'modal',
10
+ 'orders',
11
+ 'product-categories',
12
+ 'product-sets-admin',
13
+ 'products-admin',
14
+ 'settings-commerce',
15
+ 'settings-sync',
16
+ ];
17
+
18
+ const jQueryUIAdminFileEntries = {};
19
+
20
+ jQueryUIAdminFileNames.forEach( ( name ) => {
21
+ jQueryUIAdminFileEntries[ `admin/${ name }` ] = `./assets/js/admin/${ name }.js`;
22
+ } );
23
+
24
+ module.exports = {
25
+ ...defaultConfig,
26
+ entry: {
27
+ // Use admin/index.js for any new React-powered UI
28
+ 'admin/index': './assets/js/admin/index.js',
29
+ ...jQueryUIAdminFileEntries,
30
+ },
31
+ output: {
32
+ filename: '[name].js',
33
+ path: __dirname + '/assets/build',
34
+ },
35
+ };