Facebook for WooCommerce - Version 1.10.2

Version Description

Download this release

Release Info

Developer SkyVerge
Plugin Icon Facebook for WooCommerce
Version 1.10.2
Comparing to
See all releases

Code changes from version 1.10.1 to 1.10.2

assets/css/facebook.css CHANGED
@@ -274,9 +274,7 @@
274
  }
275
 
276
  #sync_progress {
277
- font-size: 0.9em;
278
  font-weight: normal;
279
- margin-left: 10px;
280
  }
281
 
282
  #sync_progress .spinner {
274
  }
275
 
276
  #sync_progress {
 
277
  font-weight: normal;
 
278
  }
279
 
280
  #sync_progress .spinner {
assets/js/admin/facebook-for-woocommerce-settings-sync.js CHANGED
@@ -22,7 +22,7 @@ jQuery( document ).ready( function( $ ) {
22
  */
23
  function getExcludedCategoriesAdded() {
24
 
25
- const newCategoryIDs = $( '#woocommerce_facebookcommerce_fb_sync_exclude_categories' ).val();
26
  let oldCategoryIDs = [];
27
 
28
  if ( window.facebookAdsToolboxConfig && window.facebookAdsToolboxConfig.excludedCategoryIDs ) {
@@ -41,7 +41,7 @@ jQuery( document ).ready( function( $ ) {
41
  */
42
  function getExcludedTagsAdded() {
43
 
44
- const newTagIDs = $( '#woocommerce_facebookcommerce_fb_sync_exclude_tags' ).val();
45
  let oldTagIDs = [];
46
 
47
  if ( window.facebookAdsToolboxConfig && window.facebookAdsToolboxConfig.excludedTagIDs ) {
@@ -134,14 +134,13 @@ jQuery( document ).ready( function( $ ) {
134
  categoriesAdded = getExcludedCategoriesAdded(),
135
  tagsAdded = getExcludedTagsAdded();
136
 
137
-
138
  if ( categoriesAdded.length > 0 || tagsAdded.length > 0 ) {
139
 
140
  $.post( facebook_for_woocommerce_settings_sync.ajax_url, {
141
- action: 'facebook_for_woocommerce_set_excluded_terms_prompt',
142
- security: facebook_for_woocommerce_settings_sync.set_excluded_terms_prompt_nonce,
143
  categories: categoriesAdded,
144
- tags: tagsAdded,
145
  }, function ( response ) {
146
 
147
  if ( response && ! response.success ) {
@@ -156,12 +155,51 @@ jQuery( document ).ready( function( $ ) {
156
  } );
157
 
158
  // exclude products: submit form as normal
159
- $( '#facebook-for-woocommerce-confirm-settings-change' ).on( 'click', function () {
160
 
161
  blockModal();
162
 
163
- submitSettingsSave = true;
164
- $submitButton.trigger( 'click' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  } );
166
 
167
  } else {
22
  */
23
  function getExcludedCategoriesAdded() {
24
 
25
+ const newCategoryIDs = $( '#woocommerce_facebookcommerce_excluded_product_category_ids' ).val();
26
  let oldCategoryIDs = [];
27
 
28
  if ( window.facebookAdsToolboxConfig && window.facebookAdsToolboxConfig.excludedCategoryIDs ) {
41
  */
42
  function getExcludedTagsAdded() {
43
 
44
+ const newTagIDs = $( '#woocommerce_facebookcommerce_excluded_product_tag_ids' ).val();
45
  let oldTagIDs = [];
46
 
47
  if ( window.facebookAdsToolboxConfig && window.facebookAdsToolboxConfig.excludedTagIDs ) {
134
  categoriesAdded = getExcludedCategoriesAdded(),
135
  tagsAdded = getExcludedTagsAdded();
136
 
 
137
  if ( categoriesAdded.length > 0 || tagsAdded.length > 0 ) {
138
 
139
  $.post( facebook_for_woocommerce_settings_sync.ajax_url, {
140
+ action: 'facebook_for_woocommerce_set_excluded_terms_prompt',
141
+ security: facebook_for_woocommerce_settings_sync.set_excluded_terms_prompt_nonce,
142
  categories: categoriesAdded,
143
+ tags: tagsAdded,
144
  }, function ( response ) {
145
 
146
  if ( response && ! response.success ) {
155
  } );
156
 
157
  // exclude products: submit form as normal
158
+ $( '.facebook-for-woocommerce-confirm-settings-change' ).on( 'click', function () {
159
 
160
  blockModal();
161
 
162
+ // the user has an option to hide all the affected products from Facebook while adding the exclusion though
163
+ if ( $( this ).hasClass( 'hide-products' ) ) {
164
+
165
+ let product_cats = [], product_tags = [];
166
+
167
+ $( categoriesAdded ).each( function() {
168
+ product_cats.push( {
169
+ term_id: this,
170
+ taxonomy: 'product_cat',
171
+ visibility: false
172
+ } );
173
+ } );
174
+
175
+ $( tagsAdded ).each( function() {
176
+ product_tags.push( {
177
+ term_id: this,
178
+ taxonomy: 'product_tag',
179
+ visibility: false
180
+ } );
181
+ } );
182
+
183
+ $.post( facebook_for_woocommerce_settings_sync.ajax_url, {
184
+ action: 'facebook_for_woocommerce_set_products_visibility',
185
+ security: facebook_for_woocommerce_settings_sync.set_product_visibility_nonce,
186
+ product_categories: product_cats,
187
+ product_tags: product_tags,
188
+ }, function ( response ) {
189
+
190
+ if ( ! response || ! response.success ) {
191
+ console.log( response )
192
+ }
193
+
194
+ submitSettingsSave = true;
195
+ $submitButton.trigger( 'click' );
196
+ } );
197
+
198
+ } else {
199
+
200
+ submitSettingsSave = true;
201
+ $submitButton.trigger( 'click' );
202
+ }
203
  } );
204
 
205
  } else {
assets/js/admin/facebook-for-woocommerce-settings-sync.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";jQuery(document).ready(function(r){if(0!==r('input[name="section"][value="facebookcommerce"]').closest("form").length){r('input[type="checkbox"].toggle-fields-group').on("change",function(e){r(this).hasClass("product-sync-field")?o(r(".product-sync-field").not(".toggle-fields-group"),r(this).is(":checked")):r(this).hasClass("messenger-field")?o(r(".messenger-field").not(".toggle-fields-group"),r(this).is(":checked")):r(this).hasClass("resync-schedule-field")&&o(r(".resync-schedule-field").not(".toggle-fields-group"),r(this).is(":checked"))}).trigger("change"),r("#woocommerce_facebookcommerce_scheduled_resync_hours, #woocommerce_facebookcommerce_scheduled_resync_minutes").on("input change keyup keydown keypress click",function(){var e=r(this).val();!isNaN(e)&&1===e.length&&e<10&&r(this).val(e.padStart(2,"0"))}).trigger("change"),r("textarea#woocommerce_facebookcommerce_messenger_greeting").on("focus change keyup keydown keypress",function(){var e=parseInt(window.facebookAdsToolboxConfig.messengerGreetingMaxCharacters,10),o=r(this).val().length,c=r("span.characters-counter"),s=c.find("span");c.html(o+" / "+e+"<br/>").append(s).css("display","block"),e<o?c.css("color","#DC3232").find("span").show():c.css("color","#999999").find("span").hide()});var l=!1;r(".woocommerce-save-button").on("click",function(e){if(l)return!0;e.preventDefault();var o,c,s,n,t=r(this),i=(s=r("#woocommerce_facebookcommerce_fb_sync_exclude_categories").val(),n=[],window.facebookAdsToolboxConfig&&window.facebookAdsToolboxConfig.excludedCategoryIDs&&(n=window.facebookAdsToolboxConfig.excludedCategoryIDs),r(s).not(n).get()),a=(o=r("#woocommerce_facebookcommerce_fb_sync_exclude_tags").val(),c=[],window.facebookAdsToolboxConfig&&window.facebookAdsToolboxConfig.excludedTagIDs&&(c=window.facebookAdsToolboxConfig.excludedTagIDs),r(o).not(c).get());0<i.length||0<a.length?r.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(e){e&&!e.success?(r("#wc-backbone-modal-dialog .modal-close").trigger("click"),new r.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:e.data}),r("#facebook-for-woocommerce-confirm-settings-change").on("click",function(){blockModal(),l=!0,t.trigger("click")})):(l=!0,t.trigger("click"))}):(l=!0,t.trigger("click"))})}function o(e,o){r(e).each(function(){var e=r(this);r(this).hasClass("wc-enhanced-select")&&(e=r(this).next("span.select2-container")),o?e.css("pointer-events","all").css("opacity","1.0"):e.css("pointer-events","none").css("opacity","0.4")})}});
1
+ "use strict";jQuery(document).ready(function(a){if(0!==a('input[name="section"][value="facebookcommerce"]').closest("form").length){a('input[type="checkbox"].toggle-fields-group').on("change",function(e){a(this).hasClass("product-sync-field")?o(a(".product-sync-field").not(".toggle-fields-group"),a(this).is(":checked")):a(this).hasClass("messenger-field")?o(a(".messenger-field").not(".toggle-fields-group"),a(this).is(":checked")):a(this).hasClass("resync-schedule-field")&&o(a(".resync-schedule-field").not(".toggle-fields-group"),a(this).is(":checked"))}).trigger("change"),a("#woocommerce_facebookcommerce_scheduled_resync_hours, #woocommerce_facebookcommerce_scheduled_resync_minutes").on("input change keyup keydown keypress click",function(){var e=a(this).val();!isNaN(e)&&1===e.length&&e<10&&a(this).val(e.padStart(2,"0"))}).trigger("change"),a("textarea#woocommerce_facebookcommerce_messenger_greeting").on("focus change keyup keydown keypress",function(){var e=parseInt(window.facebookAdsToolboxConfig.messengerGreetingMaxCharacters,10),o=a(this).val().length,c=a("span.characters-counter"),s=c.find("span");c.html(o+" / "+e+"<br/>").append(s).css("display","block"),e<o?c.css("color","#DC3232").find("span").show():c.css("color","#999999").find("span").hide()});var l=!1;a(".woocommerce-save-button").on("click",function(e){if(l)return!0;e.preventDefault();var o,c,s,t,i=a(this),n=(s=a("#woocommerce_facebookcommerce_excluded_product_category_ids").val(),t=[],window.facebookAdsToolboxConfig&&window.facebookAdsToolboxConfig.excludedCategoryIDs&&(t=window.facebookAdsToolboxConfig.excludedCategoryIDs),a(s).not(t).get()),r=(o=a("#woocommerce_facebookcommerce_excluded_product_tag_ids").val(),c=[],window.facebookAdsToolboxConfig&&window.facebookAdsToolboxConfig.excludedTagIDs&&(c=window.facebookAdsToolboxConfig.excludedTagIDs),a(o).not(c).get());0<n.length||0<r.length?a.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:n,tags:r},function(e){e&&!e.success?(a("#wc-backbone-modal-dialog .modal-close").trigger("click"),new a.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:e.data}),a(".facebook-for-woocommerce-confirm-settings-change").on("click",function(){if(blockModal(),a(this).hasClass("hide-products")){var e=[],o=[];a(n).each(function(){e.push({term_id:this,taxonomy:"product_cat",visibility:!1})}),a(r).each(function(){o.push({term_id:this,taxonomy:"product_tag",visibility:!1})}),a.post(facebook_for_woocommerce_settings_sync.ajax_url,{action:"facebook_for_woocommerce_set_products_visibility",security:facebook_for_woocommerce_settings_sync.set_product_visibility_nonce,product_categories:e,product_tags:o},function(e){e&&e.success||console.log(e),l=!0,i.trigger("click")})}else l=!0,i.trigger("click")})):(l=!0,i.trigger("click"))}):(l=!0,i.trigger("click"))})}function o(e,o){a(e).each(function(){var e=a(this);a(this).hasClass("wc-enhanced-select")&&(e=a(this).next("span.select2-container")),o?e.css("pointer-events","all").css("opacity","1.0"):e.css("pointer-events","none").css("opacity","0.4")})}});
assets/js/facebook-settings.js CHANGED
@@ -10,17 +10,6 @@
10
  var fb_sync_no_response_count = 0;
11
  var fb_show_advanced_options = false;
12
 
13
- function toggleAdvancedOptions() {
14
- var opts = document.getElementById( "fbAdvancedOptions" );
15
- if ( ! fb_show_advanced_options) {
16
- opts.style.display = "block";
17
- document.getElementById( 'fbAdvancedOptionsText' ).innerHTML = 'Hide Advanced Settings';
18
- } else {
19
- opts.style.display = "none";
20
- document.getElementById( 'fbAdvancedOptionsText' ).innerHTML = 'Show Advanced Settings';
21
- }
22
- fb_show_advanced_options = ! fb_show_advanced_options;
23
- }
24
 
25
  function openPopup() {
26
  var width = 1153;
@@ -145,32 +134,39 @@ function fb_flush(){
145
  );
146
  }
147
 
 
 
 
 
 
 
148
  function sync_confirm(verbose = null) {
149
- var msg = '';
 
 
150
  switch (verbose) {
 
151
  case 'fb_force_resync':
152
- msg = 'Your products will now be resynced with Facebook, ' +
153
- 'this may take some time.';
154
  break;
 
155
  case 'fb_test_product_sync':
156
- msg = 'Launch Test?';
157
  break;
 
158
  default:
159
- msg = 'Facebook for WooCommerce automatically syncs your products on ' +
160
- 'create/update. Are you sure you want to force product resync?\n\n' +
161
- 'This will query all published products and may take some time. ' +
162
- 'You only need to do this if your products are out of sync ' +
163
- 'or some of your products did not sync.';
164
- }
165
- if (confirm( msg )) {
166
- sync_all_products(
167
- window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload,
168
- verbose == 'fb_test_product_sync'
169
- );
170
  window.fb_sync_start_time = new Date().getTime();
171
  }
172
  }
173
 
 
174
  // Launch the confirm dialog immediately if the param is in the URL.
175
  if (window.location.href.includes( "fb_force_resync" )) {
176
  window.onload = function() { sync_confirm( "fb_force_resync" ); };
@@ -180,34 +176,77 @@ if (window.location.href.includes( "fb_force_resync" )) {
180
  window.onload = function() { sync_confirm( "fb_test_product_sync" ); };
181
  }
182
 
 
 
 
 
 
 
 
183
  function sync_all_products($using_feed = false, $is_test = false) {
184
 
185
  window.fb_connected = true;
186
  sync_in_progress();
187
- if ($using_feed) {
 
 
 
 
188
  window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload = true;
189
  window.feed_upload = true;
 
190
  ping_feed_status_queue();
191
- return $is_test ? ajax( 'ajax_test_sync_products_using_feed' )
192
- : ajax(
193
- 'ajax_sync_all_fb_products_using_feed',
194
- {
195
- "_ajax_nonce": wc_facebook_settings_jsx.nonce,
196
- },
197
- );
 
 
 
 
 
 
 
198
  } else {
199
 
200
  check_background_processor_status();
201
 
202
- return ajax(
203
- 'ajax_sync_all_fb_products',
204
- {
205
- "_ajax_nonce": wc_facebook_settings_jsx.nonce,
206
- }
207
- );
208
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
 
 
211
  // Reset all state
212
  function delete_all_settings(callback = null, failcallback = null) {
213
 
@@ -222,6 +261,16 @@ function delete_all_settings(callback = null, failcallback = null) {
222
  get_page_id_box().value = '';
223
  }
224
 
 
 
 
 
 
 
 
 
 
 
225
  window.facebookAdsToolboxConfig.pixel.pixelId = '';
226
  window.facebookAdsToolboxConfig.diaSettingId = '';
227
  window.fb_connected = false;
@@ -320,66 +369,23 @@ function save_settings_and_sync(message) {
320
  function sync_in_progress() {
321
 
322
  // temporarily disable Manage connection and Sync products buttons
323
- jQuery( '#woocommerce-facebook-settings-manage-connection' ).css( 'pointer-events', 'none' );
324
- jQuery( '#woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'none' );
325
 
326
  // set products sync status
327
- if ( document.querySelector( '#sync_progress' ) ) {
328
- document.querySelector( '#sync_progress' ).innerHTML = 'Syncing... Keep this browser open until sync is complete.<span class="spinner is-active"></span>';
329
- }
330
- }
331
-
332
-
333
- function sync_not_in_progress(){
334
- // Reset to pre-setup state.
335
- if (document.querySelector( '#cta_button' )) {
336
- var cta_element = document.querySelector( '#cta_button' );
337
- cta_element.innerHTML = 'Create Ad';
338
- cta_element.style['font-size'] = '12px';
339
- cta_element.style.width = '60px';
340
- if (window.facebookAdsToolboxConfig.diaSettingId) {
341
- cta_element.onclick = function() {
342
- window.open(
343
- 'https://www.facebook.com/ads/dia/redirect/?settings_id=' +
344
- window.facebookAdsToolboxConfig.diaSettingId + '&version=2' +
345
- '&entry_point=admin_panel'
346
- );
347
- };
348
- } else {
349
- cta_element.style['pointer-events'] = 'none';
350
- }
351
- }
352
- if (document.querySelector( '#learnmore_button' )) {
353
- var learnmore_element = document.querySelector( '#learnmore_button' );
354
- if (window.facebookAdsToolboxConfig.diaSettingId) {
355
- learnmore_element.style.display = '';
356
- }
357
- }
358
- if (document.querySelector( '#setup_h1' )) {
359
- document.querySelector( '#setup_h1' ).innerHTML =
360
- 'Reach the right people and sell more products';
361
- }
362
- if (document.querySelector( '#setup_l1' )) {
363
- document.querySelector( '#setup_l1' ).innerHTML =
364
- 'Create an ad in a few steps';
365
- }
366
- if (document.querySelector( '#setup_l2' )) {
367
- document.querySelector( '#setup_l2' ).innerHTML =
368
- 'Use built-in best practice for online sales';
369
- }
370
- if (document.querySelector( '#setup_l3' )) {
371
- document.querySelector( '#setup_l3' ).innerHTML =
372
- 'Get reporting on sales and revenue';
373
- }
374
 
375
  // enable Manage connection and Sync products buttons when sync is complete
376
- jQuery( '#woocommerce-facebook-settings-manage-connection' ).css( 'pointer-events', 'auto' );
377
- jQuery( '#woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'auto' );
378
 
379
  // Remove sync progress.
380
- if (document.querySelector( '#sync_progress' )) {
381
- document.querySelector( '#sync_progress' ).innerHTML = '';
382
- }
383
  }
384
 
385
 
@@ -499,11 +505,15 @@ function setAccessTokenAndPageId(message) {
499
  }
500
  }
501
 
502
- function setMsgerChatSetup(data) {
503
- if (data.hasOwnProperty( 'is_messenger_chat_plugin_enabled' )) {
504
- settings.is_messenger_chat_plugin_enabled =
505
- data.is_messenger_chat_plugin_enabled;
 
 
 
506
  }
 
507
  if (data.hasOwnProperty( 'facebook_jssdk_version' )) {
508
  settings.facebook_jssdk_version =
509
  data.facebook_jssdk_version;
@@ -512,20 +522,29 @@ function setMsgerChatSetup(data) {
512
  settings.fb_page_id = data.page_id;
513
  }
514
 
515
- if (data.hasOwnProperty( 'customization' )) {
516
- var customization = data.customization;
 
 
 
 
 
517
 
518
- if (customization.hasOwnProperty( 'greetingTextCode' )) {
519
- settings.msger_chat_customization_greeting_text_code =
520
- customization.greetingTextCode;
521
  }
522
- if (customization.hasOwnProperty( 'locale' )) {
523
- settings.msger_chat_customization_locale =
524
- customization.locale;
 
 
 
525
  }
526
- if (customization.hasOwnProperty( 'themeColorCode' )) {
527
- settings.msger_chat_customization_theme_color_code =
528
- customization.themeColorCode;
 
 
 
529
  }
530
  }
531
  }
@@ -653,13 +672,12 @@ function ping_feed_status_queue(count = 0) {
653
  );
654
  }
655
 
656
- function product_sync_complete(sync_progress_element) {
657
 
658
  sync_not_in_progress();
659
 
660
- if (sync_progress_element) {
661
- sync_progress_element.innerHTML = '';
662
- }
663
  clearInterval( window.fb_pings );
664
  }
665
 
@@ -679,7 +697,9 @@ function check_queues() {
679
  clearInterval( window.fb_pings );
680
  return;
681
  }
682
- var sync_progress_element = document.querySelector( '#sync_progress' );
 
 
683
  var res = parse_response_check_connection( response );
684
  if ( !res ) {
685
  if ( fb_sync_no_response_count++ > 5 ) {
@@ -697,13 +717,23 @@ function check_queues() {
697
 
698
  var processing = !!res.processing; // explicit boolean conversion
699
  var remaining = res.remaining;
 
700
  if ( processing ) {
701
- if ( sync_progress_element ) {
702
- sync_progress_element.innerHTML = '<strong>Progress:</strong> ' + remaining + ' item' + ( remaining > 1 ? 's' : '' ) + ' remaining.<span class="spinner is-active"></span>';
 
 
 
 
 
703
  }
 
 
 
704
  if ( remaining === 0 ) {
705
- product_sync_complete( sync_progress_element );
706
  }
 
707
  } else {
708
  // Not processing, none remaining. Either long complete, or just completed
709
  if ( window.fb_sync_start_time && res.request_time ) {
@@ -716,7 +746,7 @@ function check_queues() {
716
  }
717
 
718
  if ( remaining === 0 ) {
719
- product_sync_complete( sync_progress_element );
720
  }
721
  }
722
  }
@@ -745,8 +775,9 @@ function check_feed_upload_queue(check_num) {
745
  "_ajax_nonce": wc_facebook_settings_jsx.nonce,
746
  },
747
  function(response) {
748
- var sync_progress_element = document.querySelector( '#sync_progress' );
749
- var res = parse_response_check_connection( response );
 
750
 
751
  clearInterval( window.fb_feed_pings );
752
 
@@ -758,20 +789,22 @@ function check_feed_upload_queue(check_num) {
758
  if (window.is_test) {
759
  display_test_result();
760
  } else {
761
- product_sync_complete( sync_progress_element );
762
  }
763
  break;
764
  case 'in progress':
765
- if (sync_progress_element) {
766
- sync_progress_element.innerHTML = 'Syncing... Keep this browser open until sync is complete.<span class="spinner is-active"></span>';
767
- }
768
  ping_feed_status_queue( check_num + 1 );
769
- break;
770
 
771
  default:
772
- if ( sync_progress_element ) {
773
- sync_progress_element.innerHTML = '<strong>Something wrong when uploading, please try again.</strong>';
774
- }
 
 
775
 
776
  window.feed_upload = false;
777
  if (window.is_test) {
@@ -790,8 +823,9 @@ function display_test_result() {
790
  "_ajax_nonce": wc_facebook_settings_jsx.nonce
791
  },
792
  function(response) {
 
 
793
  var sync_complete_element = document.querySelector( '#sync_complete' );
794
- var sync_progress_element = document.querySelector( '#sync_progress' );
795
  var res = parse_response_check_connection( response );
796
  if (res) {
797
  var status = res.pass;
@@ -800,31 +834,28 @@ function display_test_result() {
800
  sync_not_in_progress();
801
  if (sync_complete_element) {
802
  sync_complete_element.style.display = '';
803
- sync_complete_element.innerHTML =
804
- '<strong>Status: </strong>Test Pass.';
805
- }
806
- if (sync_progress_element) {
807
- sync_progress_element.innerHTML = '';
808
  }
 
 
 
809
  window.is_test = false;
810
  break;
811
  case 'in progress':
812
- if (sync_progress_element) {
813
- sync_progress_element.innerHTML =
814
- '<strong>Integration test in progress...</strong>';
815
- }
816
  ping_feed_status_queue();
817
- break;
818
  default:
819
  window.debug_info = res.debug_info + '<br/>' + res.stack_trace;
820
  if (sync_complete_element) {
821
  sync_complete_element.style.display = '';
822
- sync_complete_element.innerHTML =
823
- '<strong>Status: </strong>Test Fail.';
824
- }
825
- if (sync_progress_element) {
826
- sync_progress_element.innerHTML = '';
827
  }
 
 
 
828
  if (document.querySelector( '#debug_info' )) {
829
  document.querySelector( '#debug_info' ).style.display = '';
830
  }
10
  var fb_sync_no_response_count = 0;
11
  var fb_show_advanced_options = false;
12
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  function openPopup() {
15
  var width = 1153;
134
  );
135
  }
136
 
137
+
138
+ /**
139
+ * Shows a confirm dialog and starts product sync if the user selectes OK.
140
+ *
141
+ * @param {String} verbose an identifier for the confirmation message to display
142
+ */
143
  function sync_confirm(verbose = null) {
144
+
145
+ let msg = '';
146
+
147
  switch (verbose) {
148
+
149
  case 'fb_force_resync':
150
+ msg = facebook_for_woocommerce_settings_sync.i18n.confirm_resync;
 
151
  break;
152
+
153
  case 'fb_test_product_sync':
154
+ msg = facebook_for_woocommerce_settings_sync.i18n.confirm_sync_test;
155
  break;
156
+
157
  default:
158
+ msg = facebook_for_woocommerce_settings_sync.i18n.confirm_sync;
159
+ }
160
+
161
+ if ( confirm( msg ) ) {
162
+
163
+ sync_all_products( window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload, verbose == 'fb_test_product_sync' );
164
+
 
 
 
 
165
  window.fb_sync_start_time = new Date().getTime();
166
  }
167
  }
168
 
169
+
170
  // Launch the confirm dialog immediately if the param is in the URL.
171
  if (window.location.href.includes( "fb_force_resync" )) {
172
  window.onload = function() { sync_confirm( "fb_force_resync" ); };
176
  window.onload = function() { sync_confirm( "fb_test_product_sync" ); };
177
  }
178
 
179
+
180
+ /**
181
+ * Sends Ajax request to the backend to initiate product sync.
182
+ *
183
+ * @param {boolean} feed whether products should be synced using feed or not
184
+ * @param {boolean} test whether this is a sync test
185
+ */
186
  function sync_all_products($using_feed = false, $is_test = false) {
187
 
188
  window.fb_connected = true;
189
  sync_in_progress();
190
+
191
+ let data = {};
192
+
193
+ if ( $using_feed ) {
194
+
195
  window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload = true;
196
  window.feed_upload = true;
197
+
198
  ping_feed_status_queue();
199
+
200
+ if ( $is_test ) {
201
+
202
+ data = { action: 'ajax_test_sync_products_using_feed' };
203
+
204
+ } else {
205
+
206
+ data = {
207
+ action: 'ajax_sync_all_fb_products_using_feed',
208
+ _ajax_nonce: wc_facebook_settings_jsx.nonce,
209
+ };
210
+
211
+ }
212
+
213
  } else {
214
 
215
  check_background_processor_status();
216
 
217
+ data = {
218
+ action: 'ajax_sync_all_fb_products',
219
+ _ajax_nonce: wc_facebook_settings_jsx.nonce,
220
+ };
 
 
221
  }
222
+
223
+ jQuery.post( ajaxurl, data ).then( function( response ) {
224
+
225
+ // something is wrong if we are syncing products using feed and the response is empty or indicates a failure
226
+ // we ignore empty responses if using the background processor because in those cases the request does not return a response when the operation is successful
227
+ if ( ( ! response && $using_feed ) || ( response && false === response.success ) ) {
228
+
229
+ // no need to check the queue or upload status
230
+ clearInterval( window.fb_pings );
231
+ clearInterval( window.fb_feed_pings );
232
+
233
+ // enable Manage connection and Sync products buttons when sync stops
234
+ jQuery( '#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'auto' );
235
+
236
+ let message;
237
+
238
+ if ( response && response.data && response.data.error ) {
239
+ message = response.data.error;
240
+ } else {
241
+ message = facebook_for_woocommerce_settings_sync.i18n.general_error;
242
+ }
243
+
244
+ $( '#sync_progress' ).show().html( '<span style="color: #DC3232">' + message + '</span>' );
245
+ }
246
+ } );
247
  }
248
 
249
+
250
  // Reset all state
251
  function delete_all_settings(callback = null, failcallback = null) {
252
 
261
  get_page_id_box().value = '';
262
  }
263
 
264
+ // reset messenger settings to their default values
265
+ jQuery( '#woocommerce_facebookcommerce_enable_messenger' ).prop( 'checked', false ).trigger( 'change' );
266
+
267
+ jQuery( '.messenger-field' ).each( function () {
268
+
269
+ if ( typeof $( this ).data( 'default' ) !== 'undefined' ) {
270
+ $( this ).val( $( this ).data( 'default' ) ).trigger( 'change' );
271
+ }
272
+ } );
273
+
274
  window.facebookAdsToolboxConfig.pixel.pixelId = '';
275
  window.facebookAdsToolboxConfig.diaSettingId = '';
276
  window.fb_connected = false;
369
  function sync_in_progress() {
370
 
371
  // temporarily disable Manage connection and Sync products buttons
372
+ jQuery( '#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'none' );
 
373
 
374
  // set products sync status
375
+ jQuery( '#sync_progress' ).show().html( facebook_for_woocommerce_settings_sync.i18n.sync_in_progress );
376
+ }
377
+
378
+
379
+ /**
380
+ * Hides sync progress and enable Manage connection and Sync products buttons.
381
+ */
382
+ function sync_not_in_progress() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
 
384
  // enable Manage connection and Sync products buttons when sync is complete
385
+ jQuery( '#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'auto' );
 
386
 
387
  // Remove sync progress.
388
+ jQuery( '#sync_progress' ).empty().hide();
 
 
389
  }
390
 
391
 
505
  }
506
  }
507
 
508
+ function setMsgerChatSetup( data ) {
509
+
510
+ if ( data.hasOwnProperty( 'is_messenger_chat_plugin_enabled' ) ) {
511
+
512
+ settings.is_messenger_chat_plugin_enabled = data.is_messenger_chat_plugin_enabled;
513
+
514
+ jQuery( '#woocommerce_facebookcommerce_enable_messenger' ).prop( 'checked', data.is_messenger_chat_plugin_enabled ).trigger( 'change' );
515
  }
516
+
517
  if (data.hasOwnProperty( 'facebook_jssdk_version' )) {
518
  settings.facebook_jssdk_version =
519
  data.facebook_jssdk_version;
522
  settings.fb_page_id = data.page_id;
523
  }
524
 
525
+ if ( data.hasOwnProperty( 'customization' ) ) {
526
+
527
+ const customization = data.customization;
528
+
529
+ if ( customization.hasOwnProperty( 'greetingTextCode' ) ) {
530
+
531
+ settings.msger_chat_customization_greeting_text_code = customization.greetingTextCode;
532
 
533
+ jQuery( '#woocommerce_facebookcommerce_messenger_greeting' ).val( customization.greetingTextCode ).trigger( 'change' );
 
 
534
  }
535
+
536
+ if ( customization.hasOwnProperty( 'locale' ) ) {
537
+
538
+ settings.msger_chat_customization_locale = customization.locale;
539
+
540
+ jQuery( '#woocommerce_facebookcommerce_messenger_locale' ).val( customization.locale ).trigger( 'change' );
541
  }
542
+
543
+ if ( customization.hasOwnProperty( 'themeColorCode' ) ) {
544
+
545
+ settings.msger_chat_customization_theme_color_code = customization.themeColorCode;
546
+
547
+ jQuery( '#woocommerce_facebookcommerce_messenger_color_hex' ).val( customization.themeColorCode ).trigger( 'change' );
548
  }
549
  }
550
  }
672
  );
673
  }
674
 
675
+ function product_sync_complete( $sync_progress_element ) {
676
 
677
  sync_not_in_progress();
678
 
679
+ $sync_progress_element.empty().hide();
680
+
 
681
  clearInterval( window.fb_pings );
682
  }
683
 
697
  clearInterval( window.fb_pings );
698
  return;
699
  }
700
+
701
+ const $sync_progress_element = jQuery( '#sync_progress' );
702
+
703
  var res = parse_response_check_connection( response );
704
  if ( !res ) {
705
  if ( fb_sync_no_response_count++ > 5 ) {
717
 
718
  var processing = !!res.processing; // explicit boolean conversion
719
  var remaining = res.remaining;
720
+
721
  if ( processing ) {
722
+
723
+ let message = '';
724
+
725
+ if ( 1 === remaining ) {
726
+ message = facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_singular;
727
+ } else {
728
+ message = facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_plural;
729
  }
730
+
731
+ $sync_progress_element.show().html( message.replace( '{count}', remaining ) );
732
+
733
  if ( remaining === 0 ) {
734
+ product_sync_complete( $sync_progress_element );
735
  }
736
+
737
  } else {
738
  // Not processing, none remaining. Either long complete, or just completed
739
  if ( window.fb_sync_start_time && res.request_time ) {
746
  }
747
 
748
  if ( remaining === 0 ) {
749
+ product_sync_complete( $sync_progress_element );
750
  }
751
  }
752
  }
775
  "_ajax_nonce": wc_facebook_settings_jsx.nonce,
776
  },
777
  function(response) {
778
+ const $sync_progress_element = jQuery( '#sync_progress' );
779
+
780
+ var res = parse_response_check_connection( response );
781
 
782
  clearInterval( window.fb_feed_pings );
783
 
789
  if (window.is_test) {
790
  display_test_result();
791
  } else {
792
+ product_sync_complete( $sync_progress_element );
793
  }
794
  break;
795
  case 'in progress':
796
+
797
+ $sync_progress_element.show().html( facebook_for_woocommerce_settings_sync.i18n.sync_in_progress );
798
+
799
  ping_feed_status_queue( check_num + 1 );
800
+ break;
801
 
802
  default:
803
+
804
+ // enable Manage connection and Sync products buttons when sync stops
805
+ jQuery( '#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'auto' );
806
+
807
+ $( '#sync_progress' ).show().html( '<span style="color: #DC3232">' + facebook_for_woocommerce_settings_sync.i18n.feed_upload_error + '</span>' );
808
 
809
  window.feed_upload = false;
810
  if (window.is_test) {
823
  "_ajax_nonce": wc_facebook_settings_jsx.nonce
824
  },
825
  function(response) {
826
+ const $sync_progress_element = jQuery( '#sync_progress' );
827
+
828
  var sync_complete_element = document.querySelector( '#sync_complete' );
 
829
  var res = parse_response_check_connection( response );
830
  if (res) {
831
  var status = res.pass;
834
  sync_not_in_progress();
835
  if (sync_complete_element) {
836
  sync_complete_element.style.display = '';
837
+ sync_complete_element.innerHTML = facebook_for_woocommerce_settings_sync.i18n.integration_test_sucessful;
 
 
 
 
838
  }
839
+
840
+ $sync_progress_element.empty().hide();
841
+
842
  window.is_test = false;
843
  break;
844
  case 'in progress':
845
+
846
+ $sync_progress_element.show().html( facebook_for_woocommerce_settings_sync.i18n.integration_test_in_progress );
847
+
 
848
  ping_feed_status_queue();
849
+ break;
850
  default:
851
  window.debug_info = res.debug_info + '<br/>' + res.stack_trace;
852
  if (sync_complete_element) {
853
  sync_complete_element.style.display = '';
854
+ sync_complete_element.innerHTML = facebook_for_woocommerce_settings_sync.i18n.integration_test_failed;
 
 
 
 
855
  }
856
+
857
+ $sync_progress_element.empty().hide();
858
+
859
  if (document.querySelector( '#debug_info' )) {
860
  document.querySelector( '#debug_info' ).style.display = '';
861
  }
assets/js/facebook-settings.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";var fb_sync_no_response_count=0,fb_show_advanced_options=!1;function toggleAdvancedOptions(){var e=document.getElementById("fbAdvancedOptions");fb_show_advanced_options?(e.style.display="none",document.getElementById("fbAdvancedOptionsText").innerHTML="Show Advanced Settings"):(e.style.display="block",document.getElementById("fbAdvancedOptionsText").innerHTML="Hide Advanced Settings"),fb_show_advanced_options=!fb_show_advanced_options}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="Your products will now be resynced with Facebook, this may take some time.";break;case"fb_test_product_sync":o="Launch Test?";break;default:o="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."}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 e=0<arguments.length&&void 0!==arguments[0]&&arguments[0],o=1<arguments.length&&void 0!==arguments[1]&&arguments[1];return window.fb_connected=!0,sync_in_progress(),e?(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload=!0,window.feed_upload=!0,ping_feed_status_queue(),o?ajax("ajax_test_sync_products_using_feed"):ajax("ajax_sync_all_fb_products_using_feed",{_ajax_nonce:wc_facebook_settings_jsx.nonce})):(check_background_processor_status(),ajax("ajax_sync_all_fb_products",{_ajax_nonce:wc_facebook_settings_jsx.nonce}))}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=""),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&&e.includes("settings_saved")?(console.log(e),o(e)):(console.log("Fail response on save_settings_and_sync"),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),sync_all_products(!0)):(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").css("pointer-events","none"),jQuery("#woocommerce-facebook-settings-sync-products").css("pointer-events","none"),document.querySelector("#sync_progress")&&(document.querySelector("#sync_progress").innerHTML='Syncing... Keep this browser open until sync is complete.<span class="spinner is-active"></span>')}function sync_not_in_progress(){if(document.querySelector("#cta_button")){var e=document.querySelector("#cta_button");e.innerHTML="Create Ad",e.style["font-size"]="12px",e.style.width="60px",window.facebookAdsToolboxConfig.diaSettingId?e.onclick=function(){window.open("https://www.facebook.com/ads/dia/redirect/?settings_id="+window.facebookAdsToolboxConfig.diaSettingId+"&version=2&entry_point=admin_panel")}:e.style["pointer-events"]="none"}if(document.querySelector("#learnmore_button")){var o=document.querySelector("#learnmore_button");window.facebookAdsToolboxConfig.diaSettingId&&(o.style.display="")}document.querySelector("#setup_h1")&&(document.querySelector("#setup_h1").innerHTML="Reach the right people and sell more products"),document.querySelector("#setup_l1")&&(document.querySelector("#setup_l1").innerHTML="Create an ad in a few steps"),document.querySelector("#setup_l2")&&(document.querySelector("#setup_l2").innerHTML="Use built-in best practice for online sales"),document.querySelector("#setup_l3")&&(document.querySelector("#setup_l3").innerHTML="Get reporting on sales and revenue"),jQuery("#woocommerce-facebook-settings-manage-connection").css("pointer-events","auto"),jQuery("#woocommerce-facebook-settings-sync-products").css("pointer-events","auto"),document.querySelector("#sync_progress")&&(document.querySelector("#sync_progress").innerHTML="")}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&&e.includes("settings_saved")&&window.sendToFacebook("ack set pixel",o.params)},function(e){console.log(e),window.sendToFacebook("fail set pixel",e)},pixel_settings)}function genFeed(e){}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),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),o.hasOwnProperty("locale")&&(settings.msger_chat_customization_locale=o.locale),o.hasOwnProperty("themeColorCode")&&(settings.msger_chat_customization_theme_color_code=o.themeColorCode)}}function iFrameListener(o){var e=o.origin||o.originalEvent.origin;switch(e!=window.facebookAdsToolboxConfig.popupOrigin&&urlFromSameDomain(e,window.facebookAdsToolboxConfig.popupOrigin)&&(window.facebookAdsToolboxConfig.popupOrigin=e),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 merchant settings":setMerchantSettings(o.data);break;case"set catalog":setCatalog(o.data);break;case"set pixel":setPixel(o.data);break;case"gen feed":genFeed();break;case"set page access token":setAccessTokenAndPageId(o.data),save_settings_and_sync(o.data),jQuery("#fbsetup").hide(),jQuery("#integration-settings").show(),jQuery(".woocommerce-save-button").show();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 ack msger chat",o.data)})}}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&&(e.innerHTML=""),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=document.querySelector("#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)o&&(o.innerHTML="<strong>Progress:</strong> "+s+" item"+(1<s?"s":"")+' remaining.<span class="spinner is-active"></span>'),0===s&&product_sync_complete(o);else{if(window.fb_sync_start_time&&n.request_time){var i=new Date(parseInt(n.request_time));if(window.fb_sync_start_time>i)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=document.querySelector("#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&&(o.innerHTML='Syncing... Keep this browser open until sync is complete.<span class="spinner is-active"></span>'),ping_feed_status_queue(t+1);break;default:o&&(o.innerHTML="<strong>Something wrong when uploading, please try again.</strong>"),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=document.querySelector("#sync_complete"),n=document.querySelector("#sync_progress"),t=parse_response_check_connection(e);if(t)switch(t.pass){case"true":sync_not_in_progress(),o&&(o.style.display="",o.innerHTML="<strong>Status: </strong>Test Pass."),n&&(n.innerHTML=""),window.is_test=!1;break;case"in progress":n&&(n.innerHTML="<strong>Integration test in progress...</strong>"),ping_feed_status_queue();break;default:window.debug_info=t.debug_info+"<br/>"+t.stack_trace,o&&(o.style.display="",o.innerHTML="<strong>Status: </strong>Test Fail."),n&&(n.innerHTML=""),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")})}window.location.href.includes("fb_force_resync")?window.onload=function(){sync_confirm("fb_force_resync")}:window.location.href.includes("fb_test_product_sync")&&(window.is_test=!0,window.onload=function(){sync_confirm("fb_test_product_sync")}),addAnEventListener(window,"message",iFrameListener),jQuery(document).ready(function(e){check_background_processor_status(),e("#woocommerce-facebook-settings-sync-products").click(function(e){e.preventDefault(),sync_confirm()})});
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,$("#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!==$(this).data("default")&&$(this).val($(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&&e.includes("settings_saved")?(console.log(e),o(e)):(console.log("Fail response on save_settings_and_sync"),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),sync_all_products(!0)):(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&&e.includes("settings_saved")&&window.sendToFacebook("ack set pixel",o.params)},function(e){console.log(e),window.sendToFacebook("fail set pixel",e)},pixel_settings)}function genFeed(e){}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 iFrameListener(o){var e=o.origin||o.originalEvent.origin;switch(e!=window.facebookAdsToolboxConfig.popupOrigin&&urlFromSameDomain(e,window.facebookAdsToolboxConfig.popupOrigin)&&(window.facebookAdsToolboxConfig.popupOrigin=e),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 merchant settings":setMerchantSettings(o.data);break;case"set catalog":setCatalog(o.data);break;case"set pixel":setPixel(o.data);break;case"gen feed":genFeed();break;case"set page access token":setAccessTokenAndPageId(o.data),save_settings_and_sync(o.data),jQuery("#fbsetup").hide(),jQuery("#integration-settings").show(),jQuery(".woocommerce-save-button").show();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 ack msger chat",o.data)})}}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 c=new Date(parseInt(n.request_time));if(window.fb_sync_start_time>c)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"),$("#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(),c=s.height()/2,a=s.outerWidth();n.css({top:Math.ceil(i.top+c)+"px",left:Math.ceil(i.left+a)+"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")})}window.location.href.includes("fb_force_resync")?window.onload=function(){sync_confirm("fb_force_resync")}:window.location.href.includes("fb_test_product_sync")&&(window.is_test=!0,window.onload=function(){sync_confirm("fb_test_product_sync")}),addAnEventListener(window,"message",iFrameListener),jQuery(document).ready(function(e){check_background_processor_status(),e("#woocommerce-facebook-settings-sync-products").click(function(e){e.preventDefault(),sync_confirm()})});
changelog.txt CHANGED
@@ -1,8 +1,24 @@
1
  *** Facebook for WooCommerce Changelog ***
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  2020.03.10 - version 1.10.1
4
  * Fix - Prevent Fatal error during the upgrade routine introduced in version 1.10.0
5
  * Fix - Only load the admin settings JavaScript on the Facebook settings page to prevent conflicts with other scripts
 
6
 
7
  2020.03.03 - version 1.10.0
8
  * Feature - Exclude specific products, variations, product categories, and product tags from syncing to Facebook
@@ -10,8 +26,8 @@
10
  * Feature - Revamped settings screen with on-site control over pixel, product sync, and Messenger behavior
11
  * Tweak - Use Action Scheduler for the daily forced re-sync, if enabled
12
  * Fix - Improve pixel tracking accuracy for add-to-cart events
13
- * Misc. - Add the SkyVerge plugin framework as the plugin base
14
- * Misc. - Require WooCommerce 3.5 and above
15
 
16
  2019-06-27 - Version 1.9.15
17
  * CSRF handling for Ajax calls like ajax_woo_infobanner_post_click, ajax_woo_infobanner_post_xout, ajax_fb_toggle_visibility
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
+ 2020.03.17 - version 1.10.2
4
+ * Tweak - Add a setting to easily enable debug logging
5
+ * Tweak - Allow third party plugins and themes to track an add-to-cart event on added_to_cart JS event
6
+ * Tweak - When excluding a product term from syncing in the plugin settings page, offer an option to hide excluded synced products from Facebook
7
+ * Tweak - When excluding product terms from syncing in the plugin settings page, and settings are saved, exclude corresponding products from sync
8
+ * Tweak - Improve error messages shown when a problem occurs during products sync
9
+ * Tweak - Log Graph API communication if logging is enabled
10
+ * Fix - When excluding a product term from syncing in the plugin settings page, ensure a modal opens to warn about possible conflicts with already synced products
11
+ * Fix - Messenger settings fields will correctly reflect the values selected during initial setup
12
+ * Fix - Fix a bug that caused newly added gallery images not to be synced immediately after they were added
13
+ * Fix - Fix a bug that prevented gallery images from being removed from products on Facebook
14
+ * Fix - Fix AddToCart Pixel event tracking when adding products from archive with AJAX and redirect to cart enabled
15
+ * Fix - Fix undefined index and undefined property notices
16
+ * Dev - Make Pixel script attributes and event parameters filterable
17
+
18
  2020.03.10 - version 1.10.1
19
  * Fix - Prevent Fatal error during the upgrade routine introduced in version 1.10.0
20
  * Fix - Only load the admin settings JavaScript on the Facebook settings page to prevent conflicts with other scripts
21
+ * Misc - Add support for WooCommerce 4.0
22
 
23
  2020.03.03 - version 1.10.0
24
  * Feature - Exclude specific products, variations, product categories, and product tags from syncing to Facebook
26
  * Feature - Revamped settings screen with on-site control over pixel, product sync, and Messenger behavior
27
  * Tweak - Use Action Scheduler for the daily forced re-sync, if enabled
28
  * Fix - Improve pixel tracking accuracy for add-to-cart events
29
+ * Misc - Add the SkyVerge plugin framework as the plugin base
30
+ * Misc - Require WooCommerce 3.5 and above
31
 
32
  2019-06-27 - Version 1.9.15
33
  * CSRF handling for Ajax calls like ajax_woo_infobanner_post_click, ajax_woo_infobanner_post_xout, ajax_fb_toggle_visibility
class-wc-facebookcommerce.php CHANGED
@@ -19,7 +19,7 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
19
 
20
 
21
  /** @var string the plugin version */
22
- const VERSION = '1.10.1';
23
 
24
  /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
25
  const PLUGIN_VERSION = self::VERSION;
19
 
20
 
21
  /** @var string the plugin version */
22
+ const VERSION = '1.10.2';
23
 
24
  /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
25
  const PLUGIN_VERSION = self::VERSION;
facebook-commerce-events-tracker.php CHANGED
@@ -58,6 +58,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
58
  // AddToCart while using redirect to cart page
59
  if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
60
  add_filter( 'woocommerce_add_to_cart_redirect', [ $this, 'set_last_product_added_to_cart_upon_redirect' ], 10, 2 );
 
61
  add_action( 'woocommerce_after_cart', [ $this, 'inject_add_to_cart_redirect_event' ], 10, 2 );
62
  }
63
 
@@ -281,7 +282,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
281
  *
282
  * @internal
283
  *
284
- * @since x.y.z
285
  */
286
  public function add_filter_for_add_to_cart_fragments() {
287
 
@@ -296,7 +297,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
296
  *
297
  * @internal
298
  *
299
- * @since x.y.z
300
  *
301
  * @param array $fragments add to cart fragments
302
  * @return array
@@ -320,15 +321,65 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
320
  }
321
 
322
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  /**
324
  * Sends a JSON response with the JavaScript code to track an AddToCart event.
325
  *
326
  * @internal
327
- * @deprecated since x.y.z
328
  */
329
  public function inject_ajax_add_to_cart_event() {
330
 
331
- wc_deprecated_function( __METHOD__, 'x.y.z' );
332
  }
333
 
334
 
@@ -337,7 +388,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
337
  *
338
  * @internal
339
  *
340
- * @since x.y.z
341
  *
342
  * @param string $redirect URL redirecting to (usually cart)
343
  * @param \WC_Product $product the product just added to the cart
@@ -353,6 +404,25 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
353
  }
354
 
355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  /**
357
  * Triggers an AddToCart event when redirecting to the cart page.
358
  *
@@ -494,7 +564,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
494
  /**
495
  * Gets the cart content items count.
496
  *
497
- * @since x.y.z
498
  *
499
  * @return int
500
  */
@@ -507,7 +577,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
507
  /**
508
  * Gets all content IDs from cart.
509
  *
510
- * @since x.y.z
511
  *
512
  * @return string JSON data
513
  */
@@ -533,7 +603,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
533
  /**
534
  * Gets the cart content data.
535
  *
536
- * @since x.y.z
537
  *
538
  * @return string JSON data
539
  */
58
  // AddToCart while using redirect to cart page
59
  if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
60
  add_filter( 'woocommerce_add_to_cart_redirect', [ $this, 'set_last_product_added_to_cart_upon_redirect' ], 10, 2 );
61
+ add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'set_last_product_added_to_cart_upon_ajax_redirect' ] );
62
  add_action( 'woocommerce_after_cart', [ $this, 'inject_add_to_cart_redirect_event' ], 10, 2 );
63
  }
64
 
282
  *
283
  * @internal
284
  *
285
+ * @since 1.10.2
286
  */
287
  public function add_filter_for_add_to_cart_fragments() {
288
 
297
  *
298
  * @internal
299
  *
300
+ * @since 1.10.2
301
  *
302
  * @param array $fragments add to cart fragments
303
  * @return array
321
  }
322
 
323
 
324
+ /**
325
+ * Setups a filter to add an add to cart fragment to trigger an AddToCart event on added_to_cart JS event.
326
+ *
327
+ * This method is used by code snippets and should not be removed.
328
+ *
329
+ * @see \WC_Facebookcommerce_EventsTracker::add_conditional_add_to_cart_event_fragment
330
+ *
331
+ * @internal
332
+ *
333
+ * @since 1.10.2
334
+ */
335
+ public function add_filter_for_conditional_add_to_cart_fragment() {
336
+
337
+ if ( 'no' === get_option( 'woocommerce_cart_redirect_after_add' ) ) {
338
+ add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'add_conditional_add_to_cart_event_fragment' ] );
339
+ }
340
+ }
341
+
342
+
343
+ /**
344
+ * Adds an add to cart fragment to trigger an AddToCart event on added_to_cart JS event.
345
+ *
346
+ * @internal
347
+ *
348
+ * @since 1.10.2
349
+ *
350
+ * @param array $fragments add to cart fragments
351
+ * @return array
352
+ */
353
+ public function add_conditional_add_to_cart_event_fragment( $fragments ) {
354
+
355
+ if ( self::$isEnabled ) {
356
+
357
+ $params = [
358
+ 'content_ids' => $this->get_cart_content_ids(),
359
+ 'content_type' => 'product',
360
+ 'contents' => $this->get_cart_contents(),
361
+ 'value' => $this->get_cart_total(),
362
+ 'currency' => get_woocommerce_currency(),
363
+ ];
364
+
365
+ $script = $this->pixel->get_conditional_one_time_event_script( 'AddToCart', $params, 'added_to_cart' );
366
+
367
+ $fragments['div.wc-facebook-pixel-event-placeholder'] = '<div class="wc-facebook-pixel-event-placeholder">' . $script . '</div>';
368
+ }
369
+
370
+ return $fragments;
371
+ }
372
+
373
+
374
  /**
375
  * Sends a JSON response with the JavaScript code to track an AddToCart event.
376
  *
377
  * @internal
378
+ * @deprecated since 1.10.2
379
  */
380
  public function inject_ajax_add_to_cart_event() {
381
 
382
+ wc_deprecated_function( __METHOD__, '1.10.2' );
383
  }
384
 
385
 
388
  *
389
  * @internal
390
  *
391
+ * @since 1.10.2
392
  *
393
  * @param string $redirect URL redirecting to (usually cart)
394
  * @param \WC_Product $product the product just added to the cart
404
  }
405
 
406
 
407
+ /**
408
+ * Sets last product added to cart to session when adding a product to cart from an archive page and both AJAX adding and redirection to cart are enabled.
409
+ *
410
+ * @internal
411
+ *
412
+ * @since 1.10.2
413
+ *
414
+ * @param int $product_id the ID of the product just added to the cart
415
+ */
416
+ public function set_last_product_added_to_cart_upon_ajax_redirect( $product_id ) {
417
+
418
+ $product = wc_get_product( $product_id );
419
+
420
+ if ( $product instanceof \WC_Product ) {
421
+ WC()->session->set( 'facebook_for_woocommerce_last_product_added_to_cart', $product->get_id() );
422
+ }
423
+ }
424
+
425
+
426
  /**
427
  * Triggers an AddToCart event when redirecting to the cart page.
428
  *
564
  /**
565
  * Gets the cart content items count.
566
  *
567
+ * @since 1.10.2
568
  *
569
  * @return int
570
  */
577
  /**
578
  * Gets all content IDs from cart.
579
  *
580
+ * @since 1.10.2
581
  *
582
  * @return string JSON data
583
  */
603
  /**
604
  * Gets the cart content data.
605
  *
606
+ * @since 1.10.2
607
  *
608
  * @return string JSON data
609
  */
facebook-commerce-pixel-event.php CHANGED
@@ -12,28 +12,27 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
12
 
13
 
14
  class WC_Facebookcommerce_Pixel {
 
 
15
  const SETTINGS_KEY = 'facebook_config';
16
  const PIXEL_ID_KEY = 'pixel_id';
17
  const USE_PII_KEY = 'use_pii';
18
 
 
19
  const PIXEL_RENDER = 'pixel_render';
 
20
  const NO_SCRIPT_RENDER = 'no_script_render';
21
 
 
 
 
22
  private $user_info;
 
23
  private $last_event;
24
- static $render_cache = array();
25
-
26
- static $default_pixel_basecode = "
27
- <script type='text/javascript'>
28
- !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
29
- n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
30
- n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
31
- t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
32
- document,'script','https://connect.facebook.net/en_US/fbevents.js');
33
- </script>
34
- ";
35
 
36
  public function __construct( $user_info = array() ) {
 
37
  $this->user_info = $user_info;
38
  $this->last_event = '';
39
  }
@@ -68,53 +67,122 @@ document,'script','https://connect.facebook.net/en_US/fbevents.js');
68
  }
69
  }
70
 
 
71
  /**
72
- * Returns FB pixel code script part
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  */
74
  public function pixel_base_code() {
 
75
  $pixel_id = self::get_pixel_id();
76
- if (
77
- (
78
- isset( self::$render_cache[ self::PIXEL_RENDER ] ) &&
79
- self::$render_cache[ self::PIXEL_RENDER ] === true
80
- ) ||
81
- ! isset( $pixel_id ) ||
82
- $pixel_id === 0
83
- ) {
84
- return;
85
  }
86
 
87
  self::$render_cache[ self::PIXEL_RENDER ] = true;
88
- $params = self::add_version_info();
89
 
90
- return sprintf(
91
- "
92
- <!-- %s Facebook Integration Begin -->
93
- %s
94
- <script>
95
- %s
96
- fbq( 'track', 'PageView', %s );
97
-
98
- document.addEventListener( 'DOMContentLoaded', function() {
99
- jQuery && jQuery( function( $ ) {
100
-
101
- // insert placeholder for events injected when a product is added to the cart through Ajax
102
- $( document.body ).append( '<div class=\"wc-facebook-pixel-event-placeholder\"></div>' );
103
- } );
104
- }, false );
105
-
106
- </script>
107
- <!-- DO NOT MODIFY -->
108
- <!-- %s Facebook Integration end -->
109
- ",
110
- esc_html( WC_Facebookcommerce_Utils::getIntegrationName() ),
111
- self::get_basecode(),
112
- $this->pixel_init_code(),
113
- json_encode( $params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
114
- esc_html( WC_Facebookcommerce_Utils::getIntegrationName() )
115
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
117
 
 
118
  /**
119
  * Prevent double-fires by checking the last event
120
  */
@@ -130,11 +198,12 @@ document.addEventListener( 'DOMContentLoaded', function() {
130
  *
131
  * Use {@see \WC_Facebookcommerce_Pixel::inject_event()} to print or enqueue the code.
132
  *
133
- * @since x.y.z
134
  *
135
  * @param string $event_name the name of the event to track
136
  * @param array $params custom event parameters
137
  * @param string $method name of the pixel's fbq() function to call
 
138
  */
139
  public function get_event_code( $event_name, $params, $method = 'track' ) {
140
 
@@ -149,23 +218,26 @@ document.addEventListener( 'DOMContentLoaded', function() {
149
  *
150
  * @see \WC_Facebookcommerce_Pixel::get_event_code()
151
  *
152
- * @since x.y.z
153
  *
154
  * @param string $event_name the name of the event to track
155
  * @param array $params custom event parameters
156
  * @param string $method name of the pixel's fbq() function to call
 
157
  */
158
  public function get_event_script( $event_name, $params, $method = 'track' ) {
159
 
160
- $output = '
161
- <!-- Facebook Pixel Event Code -->
162
- <script>
163
- %s
164
- </script>
165
- <!-- End Facebook Pixel Event Code -->
166
- ';
 
 
167
 
168
- return sprintf( $output, $this->get_event_code( $event_name, $params, $method ) );
169
  }
170
 
171
 
@@ -173,7 +245,6 @@ document.addEventListener( 'DOMContentLoaded', function() {
173
  * Prints or enqueues the JavaScript code to track an event.
174
  *
175
  * Preferred method to inject events in a page.
176
- *
177
  * @see \WC_Facebookcommerce_Pixel::build_event()
178
  *
179
  * @param string $event_name the name of the event to track
@@ -182,29 +253,32 @@ document.addEventListener( 'DOMContentLoaded', function() {
182
  */
183
  public function inject_event( $event_name, $params, $method = 'track' ) {
184
 
185
- if ( WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
186
 
187
- WC_Facebookcommerce_Utils::wc_enqueue_js( $this->get_event_code( $event_name, $params, $method ) );
188
 
189
  } else {
190
 
191
  // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
192
- printf( $this->get_event_script( $event_name, $params, $method ) );
193
  }
194
  }
195
 
196
 
197
  /**
198
- * Prints the JavaScript code to track a conditional event.
199
  *
200
- * The tracking code will be executed when the given JavaScript event is triggered.
201
  *
202
- * @param string $event_name
 
 
203
  * @param array $params custom event parameters
204
  * @param string $listener name of the JavaScript event to listen for
205
  * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching
 
206
  */
207
- public function inject_conditional_event( $event_name, $params, $listener, $jsonified_pii = '' ) {
208
 
209
  $code = self::build_event( $event_name, $params, 'track' );
210
  $this->last_event = $event_name;
@@ -213,73 +287,157 @@ document.addEventListener( 'DOMContentLoaded', function() {
213
  // Prepends fbq(...) with pii information to the injected code.
214
  if ( $jsonified_pii && get_option( self::SETTINGS_KEY )[ self::USE_PII_KEY ] ) {
215
  $this->user_info = '%s';
216
- $code =
217
- sprintf( $this->pixel_init_code(), '" || ' . $jsonified_pii . ' || "' ) . $code;
218
  }
219
 
220
- $output = "
221
- <!-- Facebook Pixel Event Code -->
222
- <script>
223
- document.addEventListener('%s', function (event) {
224
- %s
225
- }, false );
226
- </script>
227
- <!-- End Facebook Pixel Event Code -->
228
- ";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
  // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
231
- printf( $output, esc_js( $listener ), $code );
232
  }
233
 
234
 
235
  /**
236
- * Returns FB pixel code noscript part to avoid W3 validation error
 
 
 
 
 
 
 
 
 
237
  */
238
- public function pixel_base_code_noscript() {
239
- $pixel_id = self::get_pixel_id();
240
- if (
241
- (
242
- isset( self::$render_cache[ self::NO_SCRIPT_RENDER ] ) &&
243
- self::$render_cache[ self::NO_SCRIPT_RENDER ] === true
244
- ) ||
245
- ! isset( $pixel_id ) ||
246
- $pixel_id === 0
247
- ) {
248
- return;
249
- }
250
 
251
- self::$render_cache[ self::NO_SCRIPT_RENDER ] = true;
252
 
253
- return sprintf(
254
- '
255
- <!-- Facebook Pixel Code -->
256
- <noscript>
257
- <img height="1" width="1" style="display:none" alt="fbpx"
258
- src="https://www.facebook.com/tr?id=%s&ev=PageView&noscript=1"/>
259
- </noscript>
260
- <!-- DO NOT MODIFY -->
261
- <!-- End Facebook Pixel Code -->
262
- ',
263
- esc_attr( $pixel_id )
264
- );
 
 
 
 
 
265
  }
266
 
 
267
  /**
268
- * You probably should use WC_Facebookcommerce_Pixel::inject_event() but
269
- * this method is available if you need to modify the JS code somehow
 
 
 
 
 
 
270
  */
271
  public static function build_event( $event_name, $params, $method = 'track' ) {
272
- $params = self::add_version_info( $params );
273
  return sprintf(
274
  "/* %s Facebook Integration Event Tracking */\n" .
275
  "fbq('%s', '%s', %s);",
276
  WC_Facebookcommerce_Utils::getIntegrationName(),
277
  esc_js( $method ),
278
  esc_js( $event_name ),
279
- json_encode( $params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
280
  );
281
  }
282
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  public static function get_pixel_id() {
284
  $fb_options = self::get_options();
285
  if ( ! $fb_options ) {
@@ -322,10 +480,6 @@ src="https://www.facebook.com/tr?id=%s&ev=PageView&noscript=1"/>
322
  update_option( self::SETTINGS_KEY, $fb_options );
323
  }
324
 
325
- public static function get_basecode() {
326
- return self::$default_pixel_basecode;
327
- }
328
-
329
  private static function get_version_info() {
330
  global $wp_version;
331
 
@@ -354,44 +508,22 @@ src="https://www.facebook.com/tr?id=%s&ev=PageView&noscript=1"/>
354
  );
355
  }
356
 
357
- /**
358
- * Returns an array with version_info for pixel fires. Parameters provided by
359
- * users should not be overwritten by this function
360
- */
361
- private static function add_version_info( $params = array() ) {
362
- // if any parameter is passed in the pixel, do not overwrite it
363
- return array_replace( self::get_version_info(), $params );
364
- }
365
 
366
  /**
367
- * Init code might contain additional information to help matching website
368
- * users with facebook users. Information is hashed in JS side using SHA256
369
- * before sending to Facebook.
 
 
370
  */
371
- private function pixel_init_code() {
372
- $version_info = self::get_version_info();
373
- $agent_string = sprintf(
374
- '%s-%s-%s',
375
- $version_info['source'],
376
- $version_info['version'],
377
- $version_info['pluginVersion']
378
- );
379
 
380
- $params = array(
381
- 'agent' => $agent_string,
382
- );
383
 
384
- return apply_filters(
385
- 'facebook_woocommerce_pixel_init',
386
- sprintf(
387
- "fbq('init', '%s', %s, %s);\n",
388
- esc_js( self::get_pixel_id() ),
389
- json_encode( $this->user_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
390
- json_encode( $params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
391
- )
392
- );
393
  }
394
 
 
395
  }
396
 
397
  endif;
12
 
13
 
14
  class WC_Facebookcommerce_Pixel {
15
+
16
+
17
  const SETTINGS_KEY = 'facebook_config';
18
  const PIXEL_ID_KEY = 'pixel_id';
19
  const USE_PII_KEY = 'use_pii';
20
 
21
+ /** @var string cache key for pixel script block output */
22
  const PIXEL_RENDER = 'pixel_render';
23
+ /** @var string cache key for pixel noscript block output */
24
  const NO_SCRIPT_RENDER = 'no_script_render';
25
 
26
+ /** @var array script render memoization helper */
27
+ public static $render_cache = [];
28
+
29
  private $user_info;
30
+
31
  private $last_event;
32
+
 
 
 
 
 
 
 
 
 
 
33
 
34
  public function __construct( $user_info = array() ) {
35
+
36
  $this->user_info = $user_info;
37
  $this->last_event = '';
38
  }
67
  }
68
  }
69
 
70
+
71
  /**
72
+ * Gets Facebook Pixel init code.
73
+ *
74
+ * Init code might contain additional information to help matching website users with facebook users.
75
+ * Information is hashed in JS side using SHA256 before sending to Facebook.
76
+ *
77
+ * @return string
78
+ */
79
+ private function get_pixel_init_code() {
80
+
81
+ $version_info = self::get_version_info();
82
+ $agent_string = sprintf(
83
+ '%s-%s-%s',
84
+ $version_info['source'],
85
+ $version_info['version'],
86
+ $version_info['pluginVersion']
87
+ );
88
+
89
+ /**
90
+ * Filters Facebook Pixel init code.
91
+ *
92
+ * @param string $js_code
93
+ */
94
+ return apply_filters( 'facebook_woocommerce_pixel_init', sprintf(
95
+ "fbq('init', '%s', %s, %s);\n",
96
+ esc_js( self::get_pixel_id() ),
97
+ json_encode( $this->user_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ),
98
+ json_encode( [ 'agent' => $agent_string ], JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
99
+ ) );
100
+ }
101
+
102
+
103
+ /**
104
+ * Gets the Facebook Pixel code scripts.
105
+ *
106
+ * @return string HTML scripts
107
  */
108
  public function pixel_base_code() {
109
+
110
  $pixel_id = self::get_pixel_id();
111
+
112
+ // bail if no ID or already rendered
113
+ if ( empty( $pixel_id )|| ! empty( self::$render_cache[ self::PIXEL_RENDER ] ) ) {
114
+ return '';
 
 
 
 
 
115
  }
116
 
117
  self::$render_cache[ self::PIXEL_RENDER ] = true;
 
118
 
119
+ ob_start();
120
+
121
+ ?>
122
+ <script <?php echo self::get_script_attributes(); ?>>
123
+ !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
124
+ n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
125
+ n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
126
+ t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
127
+ document,'script','https://connect.facebook.net/en_US/fbevents.js');
128
+ </script>
129
+ <!-- WooCommerce Facebook Integration Begin -->
130
+ <script <?php echo self::get_script_attributes(); ?>>
131
+
132
+ <?php echo $this->get_pixel_init_code(); ?>
133
+
134
+ fbq( 'track', 'PageView', <?php echo json_encode( self::build_params( [], 'PageView' ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT ) ?> );
135
+
136
+ document.addEventListener( 'DOMContentLoaded', function() {
137
+ jQuery && jQuery( function( $ ) {
138
+ // insert placeholder for events injected when a product is added to the cart through AJAX
139
+ $( document.body ).append( '<div class=\"wc-facebook-pixel-event-placeholder\"></div>' );
140
+ } );
141
+ }, false );
142
+
143
+ </script>
144
+ <!-- WooCommerce Facebook Integration End -->
145
+ <?php
146
+
147
+ return ob_get_clean();
148
+ }
149
+
150
+
151
+ /**
152
+ * Gets Facebook Pixel code noscript part to avoid W3 validation errors.
153
+ *
154
+ * @return string
155
+ */
156
+ public function pixel_base_code_noscript() {
157
+
158
+ $pixel_id = self::get_pixel_id();
159
+
160
+ if ( empty( $pixel_id ) || ! empty( self::$render_cache[ self::NO_SCRIPT_RENDER ] ) ) {
161
+ return '';
162
+ }
163
+
164
+ self::$render_cache[ self::NO_SCRIPT_RENDER ] = true;
165
+
166
+ ob_start();
167
+
168
+ ?>
169
+ <!-- Facebook Pixel Code -->
170
+ <noscript>
171
+ <img
172
+ height="1"
173
+ width="1"
174
+ style="display:none"
175
+ alt="fbpx"
176
+ src="https://www.facebook.com/tr?id=<?php echo esc_attr( $pixel_id ); ?>&ev=PageView&noscript=1"
177
+ />
178
+ </noscript>
179
+ <!-- End Facebook Pixel Code -->
180
+ <?php
181
+
182
+ return ob_get_clean();
183
  }
184
 
185
+
186
  /**
187
  * Prevent double-fires by checking the last event
188
  */
198
  *
199
  * Use {@see \WC_Facebookcommerce_Pixel::inject_event()} to print or enqueue the code.
200
  *
201
+ * @since 1.10.2
202
  *
203
  * @param string $event_name the name of the event to track
204
  * @param array $params custom event parameters
205
  * @param string $method name of the pixel's fbq() function to call
206
+ * @return string
207
  */
208
  public function get_event_code( $event_name, $params, $method = 'track' ) {
209
 
218
  *
219
  * @see \WC_Facebookcommerce_Pixel::get_event_code()
220
  *
221
+ * @since 1.10.2
222
  *
223
  * @param string $event_name the name of the event to track
224
  * @param array $params custom event parameters
225
  * @param string $method name of the pixel's fbq() function to call
226
+ * @return string
227
  */
228
  public function get_event_script( $event_name, $params, $method = 'track' ) {
229
 
230
+ ob_start();
231
+
232
+ ?>
233
+ <!-- Facebook Pixel Event Code -->
234
+ <script <?php echo self::get_script_attributes(); ?>>
235
+ <?php echo $this->get_event_code( $event_name, $params, $method ); ?>
236
+ </script>
237
+ <!-- End Facebook Pixel Event Code -->
238
+ <?php
239
 
240
+ return ob_get_clean();
241
  }
242
 
243
 
245
  * Prints or enqueues the JavaScript code to track an event.
246
  *
247
  * Preferred method to inject events in a page.
 
248
  * @see \WC_Facebookcommerce_Pixel::build_event()
249
  *
250
  * @param string $event_name the name of the event to track
253
  */
254
  public function inject_event( $event_name, $params, $method = 'track' ) {
255
 
256
+ if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
257
 
258
+ \WC_Facebookcommerce_Utils::wc_enqueue_js( $this->get_event_code( $event_name, self::build_params( $params, $event_name ), $method ) );
259
 
260
  } else {
261
 
262
  // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
263
+ printf( $this->get_event_script( $event_name, self::build_params( $params, $event_name ), $method ) );
264
  }
265
  }
266
 
267
 
268
  /**
269
+ * Gets the JavaScript code to track a conditional event wrapped in <script> tag.
270
  *
271
+ * @see \WC_Facebookcommerce_Pixel::get_event_code()
272
  *
273
+ * @since 1.10.2
274
+ *
275
+ * @param string $event_name the name of the event to track
276
  * @param array $params custom event parameters
277
  * @param string $listener name of the JavaScript event to listen for
278
  * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching
279
+ * @return string
280
  */
281
+ public function get_conditional_event_script( $event_name, $params, $listener, $jsonified_pii ) {
282
 
283
  $code = self::build_event( $event_name, $params, 'track' );
284
  $this->last_event = $event_name;
287
  // Prepends fbq(...) with pii information to the injected code.
288
  if ( $jsonified_pii && get_option( self::SETTINGS_KEY )[ self::USE_PII_KEY ] ) {
289
  $this->user_info = '%s';
290
+ $code = sprintf( $this->get_pixel_init_code(), '" || ' . $jsonified_pii . ' || "' ) . $code;
 
291
  }
292
 
293
+ ob_start();
294
+
295
+ ?>
296
+ <!-- Facebook Pixel Event Code -->
297
+ <script <?php echo self::get_script_attributes(); ?>>
298
+ document.addEventListener( '<?php echo esc_js( $listener ); ?>', function (event) {
299
+ <?php echo $code; ?>
300
+ }, false );
301
+ </script>
302
+ <!-- End Facebook Pixel Event Code -->
303
+ <?php
304
+
305
+ return ob_get_clean();
306
+ }
307
+
308
+
309
+ /**
310
+ * Prints the JavaScript code to track a conditional event.
311
+ *
312
+ * The tracking code will be executed when the given JavaScript event is triggered.
313
+ *
314
+ * @param string $event_name
315
+ * @param array $params custom event parameters
316
+ * @param string $listener name of the JavaScript event to listen for
317
+ * @param string $jsonified_pii JavaScript code representing an object of data for Advanced Matching
318
+ * @return string
319
+ */
320
+ public function inject_conditional_event( $event_name, $params, $listener, $jsonified_pii = '' ) {
321
 
322
  // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
323
+ return $this->get_conditional_event_script( $event_name, self::build_params( $params, $event_name ), $listener, $jsonified_pii );
324
  }
325
 
326
 
327
  /**
328
+ * Gets the JavaScript code to track a conditional event that is only triggered one time wrapped in <script> tag.
329
+ *
330
+ * @internal
331
+ *
332
+ * @since 1.10.2
333
+ *
334
+ * @param string $event_name the name of the event to track
335
+ * @param array $params custom event parameters
336
+ * @param string $listened_event name of the JavaScript event to listen for
337
+ * @return string
338
  */
339
+ public function get_conditional_one_time_event_script( $event_name, $params, $listened_event ) {
 
 
 
 
 
 
 
 
 
 
 
340
 
341
+ $code = $this->get_event_code( $event_name, $params );
342
 
343
+ ob_start();
344
+
345
+ ?>
346
+ <!-- Facebook Pixel Event Code -->
347
+ <script <?php echo self::get_script_attributes(); ?>>
348
+ function handle<?php echo $event_name; ?>Event() {
349
+ <?php echo $code; ?>
350
+ // some weird themes (hi, Basel) are running this script twice, so two listeners are added and we need to remove them after running one
351
+ jQuery( document.body ).off( '<?php echo esc_js( $listened_event ); ?>', handle<?php echo $event_name; ?>Event );
352
+ }
353
+
354
+ jQuery( document.body ).one( '<?php echo esc_js( $listened_event ); ?>', handle<?php echo $event_name; ?>Event );
355
+ </script>
356
+ <!-- End Facebook Pixel Event Code -->
357
+ <?php
358
+
359
+ return ob_get_clean();
360
  }
361
 
362
+
363
  /**
364
+ * Builds an event.
365
+ *
366
+ * @see \WC_Facebookcommerce_Pixel::inject_event() for the preferred method to inject an event.
367
+ *
368
+ * @param string $event_name event name
369
+ * @param array $params event params
370
+ * @param string $method optional, defaults to 'track'
371
+ * @return string
372
  */
373
  public static function build_event( $event_name, $params, $method = 'track' ) {
374
+
375
  return sprintf(
376
  "/* %s Facebook Integration Event Tracking */\n" .
377
  "fbq('%s', '%s', %s);",
378
  WC_Facebookcommerce_Utils::getIntegrationName(),
379
  esc_js( $method ),
380
  esc_js( $event_name ),
381
+ json_encode( self::build_params( $params, $event_name ), JSON_PRETTY_PRINT | JSON_FORCE_OBJECT )
382
  );
383
  }
384
 
385
+
386
+ /**
387
+ * Gets an array with version_info for pixel fires.
388
+ *
389
+ * Parameters provided by users should not be overwritten by this function.
390
+ *
391
+ * @since 1.10.2
392
+ *
393
+ * @param array $params user defined parameters
394
+ * @param string $event the event name the params are for
395
+ * @return array
396
+ */
397
+ private static function build_params( $params = [], $event = '' ) {
398
+
399
+ $params = array_replace( self::get_version_info(), $params );
400
+
401
+ /**
402
+ * Filters the parameters for the pixel code.
403
+ *
404
+ * @since 1.10.2
405
+ *
406
+ * @param array $params user defined parameters
407
+ * @param string $event the event name
408
+ */
409
+ return (array) apply_filters( 'wc_facebook_pixel_params', $params, $event );
410
+ }
411
+
412
+
413
+ /**
414
+ * Gets script tag attributes.
415
+ *
416
+ * @since 1.10.2
417
+ *
418
+ * @return string
419
+ */
420
+ private static function get_script_attributes() {
421
+
422
+ $script_attributes = '';
423
+
424
+ /**
425
+ * Filters Facebook Pixel script attributes.
426
+ *
427
+ * @since 1.10.2
428
+ *
429
+ * @param array $custom_attributes
430
+ */
431
+ $custom_attributes = (array) apply_filters( 'wc_facebook_pixel_script_attributes', [ 'type' => 'text/javascript' ] );
432
+
433
+ foreach ( $custom_attributes as $tag => $value ) {
434
+ $script_attributes .= ' ' . $tag . '="' . esc_attr( $value ) . '"';
435
+ }
436
+
437
+ return $script_attributes;
438
+ }
439
+
440
+
441
  public static function get_pixel_id() {
442
  $fb_options = self::get_options();
443
  if ( ! $fb_options ) {
480
  update_option( self::SETTINGS_KEY, $fb_options );
481
  }
482
 
 
 
 
 
483
  private static function get_version_info() {
484
  global $wp_version;
485
 
508
  );
509
  }
510
 
 
 
 
 
 
 
 
 
511
 
512
  /**
513
+ * Gets Facebook Pixel base code.
514
+ *
515
+ * @deprecated since 1.10.2
516
+ *
517
+ * @return string
518
  */
519
+ public static function get_basecode() {
 
 
 
 
 
 
 
520
 
521
+ wc_deprecated_function( __METHOD__, '1.10.2' );
 
 
522
 
523
+ return '';
 
 
 
 
 
 
 
 
524
  }
525
 
526
+
527
  }
528
 
529
  endif;
facebook-commerce.php CHANGED
@@ -76,6 +76,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
76
  /** @var string the messenger color HEX setting ID */
77
  const SETTING_MESSENGER_COLOR_HEX = 'messenger_color_hex';
78
 
 
 
 
79
  /** @var string the standard product description mode name */
80
  const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';
81
 
@@ -324,7 +327,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
324
  // Only load product processing hooks if we have completed setup.
325
  if ( $this->get_page_access_token() && $this->get_product_catalog_id() ) {
326
 
327
- add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 20 );
 
328
 
329
  add_action(
330
  'woocommerce_product_quick_edit_save',
@@ -1311,6 +1315,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1311
  function update_product_item( $woo_product, $fb_product_item_id ) {
1312
  $product_data = $woo_product->prepare_product();
1313
 
 
 
 
 
 
1314
  $result = $this->check_api_result(
1315
  $this->fbgraph->update_product_item(
1316
  $fb_product_item_id,
@@ -1480,6 +1489,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1480
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = 'no';
1481
  $this->settings[ self::SETTING_FACEBOOK_PAGE_ID ] = '';
1482
 
 
 
 
 
 
1483
  $this->update_external_merchant_settings_id( '' );
1484
  $this->update_pixel_install_time( 0 );
1485
  $this->update_feed_id( '' );
@@ -1524,7 +1538,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1524
  'connected' => true,
1525
  'status' => 'in progress',
1526
  );
1527
- if ( $this->settings['fb_upload_id'] ) {
1528
  if ( ! isset( $this->fbproductfeed ) ) {
1529
  if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
1530
  include_once 'includes/fbproductfeed.php';
@@ -1632,15 +1646,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1632
  */
1633
  function display_error_message( $msg ) {
1634
 
1635
- $msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
1636
-
1637
  WC_Facebookcommerce_Utils::log( $msg );
1638
 
1639
- set_transient(
1640
- 'facebook_plugin_api_error',
1641
- $msg,
1642
- self::FB_MESSAGE_DISPLAY_TIME
1643
- );
1644
  }
1645
 
1646
 
@@ -2002,24 +2010,88 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2002
 
2003
  /**
2004
  * Special function to run all visible products through on_product_publish
2005
- **/
2006
- function ajax_sync_all_fb_products() {
 
 
 
2007
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
2008
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2009
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2010
  if ( ! $this->is_product_sync_enabled() ) {
 
2011
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
2012
- wp_die();
 
2013
  }
2014
 
2015
  if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
2016
- WC_Facebookcommerce_Utils::log(
2017
- 'No API key or catalog ID: ' .
2018
- $this->get_page_access_token() . ' and ' . $this->get_product_catalog_id()
2019
- );
2020
- wp_die();
2021
- return;
2022
  }
 
2023
  $this->remove_resync_message();
2024
 
2025
  $currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
@@ -2032,40 +2104,27 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2032
  }
2033
 
2034
  if ( $currently_syncing ) {
2035
- WC_Facebookcommerce_Utils::log( 'Not syncing, sync in progress' );
 
2036
  WC_Facebookcommerce_Utils::fblog(
2037
  'Tried to sync during an in-progress sync!',
2038
  array(),
2039
  true
2040
  );
2041
- $this->display_warning_message(
2042
- 'A product sync is in progress.
2043
- Please wait until the sync finishes before starting a new one.'
2044
- );
2045
- wp_die();
2046
- return;
2047
  }
2048
 
2049
- $is_valid_product_catalog =
2050
- $this->fbgraph->validate_product_catalog( $this->get_product_catalog_id() );
2051
 
2052
- if ( ! $is_valid_product_catalog ) {
2053
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
2054
  WC_Facebookcommerce_Utils::fblog(
2055
  'Tried to sync with an invalid product catalog!',
2056
  array(),
2057
  true
2058
  );
2059
- $this->display_warning_message(
2060
- 'We\'ve detected that your
2061
- Facebook Product Catalog is no longer valid. This may happen if it was
2062
- deleted, or this may be a transient error.
2063
- If this error persists please remove your settings via
2064
- "Advanced Options > Advanced Settings > Remove"
2065
- and try setup again'
2066
- );
2067
- wp_die();
2068
- return;
2069
  }
2070
 
2071
  // Cache the cart URL to display a warning in case it changes later
@@ -2167,21 +2226,23 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2167
  // https://codex.wordpress.org/Function_Reference/wp_reset_postdata
2168
  wp_reset_postdata();
2169
 
2170
- // This is important, for some reason.
2171
- // See https://codex.wordpress.org/AJAX_in_Plugins
2172
- wp_die();
2173
  }
2174
 
 
2175
  /**
2176
  * Special function to run all visible products by uploading feed.
2177
- **/
2178
- function ajax_sync_all_fb_products_using_feed() {
 
 
2179
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
2180
  'syncall products using feed',
2181
  ! $this->test_mode
2182
  );
2183
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2184
- return $this->sync_all_fb_products_using_feed();
 
2185
  }
2186
 
2187
 
@@ -2190,42 +2251,38 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2190
  *
2191
  * @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
2192
  *
 
 
 
2193
  * @return bool
2194
  */
2195
- public function sync_all_fb_products_using_feed() {
2196
 
2197
  if ( ! $this->is_product_sync_enabled() ) {
2198
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
2199
- return false;
 
2200
  }
2201
 
2202
  if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
2203
- self::log(
2204
- 'No API key or catalog ID: ' . $this->get_page_access_token() .
2205
- ' and ' . $this->get_product_catalog_id()
2206
- );
2207
- return false;
2208
  }
 
2209
  $this->remove_resync_message();
2210
- $is_valid_product_catalog =
2211
- $this->fbgraph->validate_product_catalog( $this->get_product_catalog_id() );
2212
 
2213
- if ( ! $is_valid_product_catalog ) {
 
2214
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
2215
  WC_Facebookcommerce_Utils::fblog(
2216
  'Tried to sync with an invalid product catalog!',
2217
  array(),
2218
  true
2219
  );
2220
- $this->display_warning_message(
2221
- 'We\'ve detected that your
2222
- Facebook Product Catalog is no longer valid. This may happen if it was
2223
- deleted, or this may be a transient error.
2224
- If this error persists please remove your settings via
2225
- "Advanced Options > Advanced Settings > Remove"
2226
- and try setup again'
2227
- );
2228
- return false;
2229
  }
2230
 
2231
  // Cache the cart URL to display a warning in case it changes later
@@ -2251,27 +2308,46 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2251
  );
2252
  }
2253
 
2254
- $upload_success = $this->fbproductfeed->sync_all_products_using_feed();
2255
- if ( $upload_success ) {
2256
- $this->update_feed_id( $this->fbproductfeed->feed_id );
2257
- $this->settings['fb_upload_id'] = $this->fbproductfeed->upload_id;
2258
- update_option(
2259
- $this->get_option_key(),
2260
- apply_filters(
2261
- 'woocommerce_settings_api_sanitized_fields_' .
2262
- $this->id,
2263
- $this->settings
2264
- )
2265
- );
2266
- wp_reset_postdata();
2267
- return true;
2268
  }
2269
- WC_Facebookcommerce_Utils::fblog(
2270
- 'Sync all products using feed, curl failed',
2271
- array(),
2272
- true
 
 
 
 
2273
  );
2274
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2275
  }
2276
 
2277
 
@@ -2328,6 +2404,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2328
  $default_locale = 'en_US';
2329
  }
2330
 
 
 
 
2331
  $form_fields = [
2332
 
2333
  /** @see \WC_Facebookcommerce_Integration::generate_manage_connection_title_html() */
@@ -2444,6 +2523,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2444
  'class' => 'wc-enhanced-select messenger-field',
2445
  'default' => $default_locale,
2446
  'options' => $messenger_locales,
 
 
 
2447
  ],
2448
 
2449
  /** @see \WC_Facebookcommerce_Integration::generate_messenger_greeting_html() */
@@ -2452,19 +2534,36 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2452
  'title' => __( 'Greeting', 'facebook-for-woocommerce' ),
2453
  'type' => 'messenger_greeting',
2454
  'class' => 'messenger-field',
2455
- 'default' => __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' ),
2456
  'css' => 'max-width: 400px; margin-bottom: 10px',
2457
  'custom_attributes' => [
2458
- 'maxlength' => $this->get_messenger_greeting_max_characters(),
 
2459
  ],
2460
  ],
2461
 
2462
  self::SETTING_MESSENGER_COLOR_HEX => [
2463
- 'title' => __( 'Colors', 'facebook-for-woocommerce' ),
2464
- 'type' => 'color',
2465
- 'class' => 'messenger-field',
2466
- 'default' => '#0084ff',
2467
- 'css' => 'width: 6em;',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2468
  ],
2469
 
2470
  ];
@@ -2695,8 +2794,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2695
  href="#"
2696
  style="vertical-align: middle; margin-left: 20px;"
2697
  ><?php esc_html_e( 'Sync products', 'facebook-for-woocommerce' ); ?></a>
2698
- <span id="sync_progress" style="margin-left: 10px"></span>
2699
  </h3>
 
2700
  <table class="form-table">
2701
  <?php
2702
 
@@ -2726,6 +2825,66 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2726
  } elseif ( $saved_resync_offset !== $current_resync_offset || false === $this->is_resync_scheduled() ) {
2727
  $this->schedule_resync( $saved_resync_offset );
2728
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2729
  }
2730
 
2731
 
@@ -3602,6 +3761,27 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3602
  }
3603
 
3604
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3605
  /**
3606
  * Gets message HTML.
3607
  *
@@ -3630,17 +3810,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3630
  */
3631
  public function maybe_display_facebook_api_messages() {
3632
 
3633
- $error_msg = get_transient( 'facebook_plugin_api_error' );
3634
 
3635
- if ( $error_msg ) {
3636
-
3637
- $message = sprintf(
3638
- __(
3639
- 'Facebook extension error: %s ',
3640
- 'facebook-for-woocommerce'
3641
- ),
3642
- $error_msg
3643
- );
3644
 
3645
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3646
  echo $this->get_message_html( $message );
@@ -4001,7 +4173,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
4001
 
4002
  $body = WC_Facebookcommerce_Utils::decode_json( $product_fbid_result['body'] );
4003
 
4004
- if ( $body && $body->id ) {
4005
 
4006
  if ( $fbid_type == self::FB_PRODUCT_GROUP_ID ) {
4007
  $fb_id = $body->product_group->id;
@@ -4180,7 +4352,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
4180
  */
4181
  public function handle_scheduled_resync_action() {
4182
 
4183
- $this->sync_all_fb_products_using_feed();
 
 
4184
 
4185
  $resync_offset = $this->get_scheduled_resync_offset();
4186
 
76
  /** @var string the messenger color HEX setting ID */
77
  const SETTING_MESSENGER_COLOR_HEX = 'messenger_color_hex';
78
 
79
+ /** @var string the "debug mode" setting ID */
80
+ const SETTING_ENABLE_DEBUG_MODE = 'enable_debug_mode';
81
+
82
  /** @var string the standard product description mode name */
83
  const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';
84
 
327
  // Only load product processing hooks if we have completed setup.
328
  if ( $this->get_page_access_token() && $this->get_product_catalog_id() ) {
329
 
330
+ // on_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information
331
+ add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 40 );
332
 
333
  add_action(
334
  'woocommerce_product_quick_edit_save',
1315
  function update_product_item( $woo_product, $fb_product_item_id ) {
1316
  $product_data = $woo_product->prepare_product();
1317
 
1318
+ // send an empty string to clear the additional_image_urls property if the product has no additional images
1319
+ if ( empty( $product_data['additional_image_urls'] ) ) {
1320
+ $product_data['additional_image_urls'] = '';
1321
+ }
1322
+
1323
  $result = $this->check_api_result(
1324
  $this->fbgraph->update_product_item(
1325
  $fb_product_item_id,
1489
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = 'no';
1490
  $this->settings[ self::SETTING_FACEBOOK_PAGE_ID ] = '';
1491
 
1492
+ unset( $this->settings[ self::SETTING_ENABLE_MESSENGER ] );
1493
+ unset( $this->settings[ self::SETTING_MESSENGER_GREETING ] );
1494
+ unset( $this->settings[ self::SETTING_MESSENGER_LOCALE ] );
1495
+ unset( $this->settings[ self::SETTING_MESSENGER_COLOR_HEX ] );
1496
+
1497
  $this->update_external_merchant_settings_id( '' );
1498
  $this->update_pixel_install_time( 0 );
1499
  $this->update_feed_id( '' );
1538
  'connected' => true,
1539
  'status' => 'in progress',
1540
  );
1541
+ if ( ! empty( $this->settings['fb_upload_id'] ) ) {
1542
  if ( ! isset( $this->fbproductfeed ) ) {
1543
  if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
1544
  include_once 'includes/fbproductfeed.php';
1646
  */
1647
  function display_error_message( $msg ) {
1648
 
 
 
1649
  WC_Facebookcommerce_Utils::log( $msg );
1650
 
1651
+ set_transient( 'facebook_plugin_api_error', $msg, self::FB_MESSAGE_DISPLAY_TIME );
 
 
 
 
1652
  }
1653
 
1654
 
2010
 
2011
  /**
2012
  * Special function to run all visible products through on_product_publish
2013
+ *
2014
+ * @internal
2015
+ */
2016
+ public function ajax_sync_all_fb_products() {
2017
+
2018
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
2019
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2020
 
2021
+ $this->sync_facebook_products( 'background' );
2022
+ }
2023
+
2024
+
2025
+ /**
2026
+ * Syncs Facebook products using the GraphAPI.
2027
+ *
2028
+ * It can either use a Feed upload or update each product individually based on the selecetd method.
2029
+ * Ends the request sending a JSON response indicating success or failure.
2030
+ *
2031
+ * @since 1.10.2
2032
+ *
2033
+ * @param string $method either 'feed' or 'background'
2034
+ */
2035
+ private function sync_facebook_products( $method ) {
2036
+
2037
+ try {
2038
+
2039
+ if ( 'feed' === $method ) {
2040
+
2041
+ $this->sync_facebook_products_using_feed();
2042
+
2043
+ } elseif ( 'background' === $method ) {
2044
+
2045
+ // if syncs starts, the background processor will continue executing until the request ends and no response will be sent back to the browser
2046
+ $this->sync_facebook_products_using_background_processor();
2047
+ }
2048
+
2049
+ wp_send_json_success();
2050
+
2051
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
2052
+
2053
+ // Access token has expired
2054
+ if ( 190 === $e->getCode() ) {
2055
+ $error_message = __( 'Your connection has expired.', 'facebook-for-woocommerce' ) . ' <strong>' . __( 'Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook.', 'facebook-for-woocommerce' ) . '</strong>';
2056
+ } else {
2057
+ $error_message = $e->getMessage();
2058
+ }
2059
+
2060
+ $message = sprintf(
2061
+ /* translators: Placeholders %s - error message */
2062
+ __( 'There was an error trying to sync the products to Facebook. %s', 'facebook-for-woocommerce' ),
2063
+ $error_message
2064
+ );
2065
+
2066
+ wp_send_json_error( [ 'error' => $message ] );
2067
+ }
2068
+ }
2069
+
2070
+
2071
+ /**
2072
+ * Syncs Facebook products using the background processor.
2073
+ *
2074
+ * @since 1.10.2
2075
+ *
2076
+ * @throws Framework\SV_WC_Plugin_Exception
2077
+ * @return bool
2078
+ */
2079
+ private function sync_facebook_products_using_background_processor() {
2080
+
2081
  if ( ! $this->is_product_sync_enabled() ) {
2082
+
2083
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
2084
+
2085
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
2086
  }
2087
 
2088
  if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
2089
+
2090
+ WC_Facebookcommerce_Utils::log( sprintf( 'No API key or Catalog ID: %s and %s', $this->get_page_access_token(), $this->get_product_catalog_id() ) );
2091
+
2092
+ throw new Framework\SV_WC_Plugin_Exception( __( 'The page access token or product catalog ID are missing.', 'facebook-for-woocommerce' ) );
 
 
2093
  }
2094
+
2095
  $this->remove_resync_message();
2096
 
2097
  $currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
2104
  }
2105
 
2106
  if ( $currently_syncing ) {
2107
+
2108
+ WC_Facebookcommerce_Utils::log( 'Not syncing again, sync already in progress' );
2109
  WC_Facebookcommerce_Utils::fblog(
2110
  'Tried to sync during an in-progress sync!',
2111
  array(),
2112
  true
2113
  );
2114
+
2115
+ throw new Framework\SV_WC_Plugin_Exception( __( 'A product sync is in progress. Please wait until the sync finishes before starting a new one.', 'facebook-for-woocommerce' ) );
 
 
 
 
2116
  }
2117
 
2118
+ if ( ! $this->fbgraph->is_product_catalog_valid( $this->get_product_catalog_id() ) ) {
 
2119
 
 
2120
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
2121
  WC_Facebookcommerce_Utils::fblog(
2122
  'Tried to sync with an invalid product catalog!',
2123
  array(),
2124
  true
2125
  );
2126
+
2127
+ throw new Framework\SV_WC_Plugin_Exception( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
 
 
 
 
 
 
 
 
2128
  }
2129
 
2130
  // Cache the cart URL to display a warning in case it changes later
2226
  // https://codex.wordpress.org/Function_Reference/wp_reset_postdata
2227
  wp_reset_postdata();
2228
 
2229
+ return true;
 
 
2230
  }
2231
 
2232
+
2233
  /**
2234
  * Special function to run all visible products by uploading feed.
2235
+ *
2236
+ * @internal
2237
+ */
2238
+ public function ajax_sync_all_fb_products_using_feed() {
2239
  WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
2240
  'syncall products using feed',
2241
  ! $this->test_mode
2242
  );
2243
  check_ajax_referer( 'wc_facebook_settings_jsx' );
2244
+
2245
+ $this->sync_facebook_products( 'feed' );
2246
  }
2247
 
2248
 
2251
  *
2252
  * @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
2253
  *
2254
+ * @since 1.10.2
2255
+ *
2256
+ * @throws Framework\SV_WC_Plugin_Exception
2257
  * @return bool
2258
  */
2259
+ public function sync_facebook_products_using_feed() {
2260
 
2261
  if ( ! $this->is_product_sync_enabled() ) {
2262
  WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
2263
+
2264
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Product sync is disabled.', 'facebook-for-woocommerce' ) );
2265
  }
2266
 
2267
  if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
2268
+
2269
+ WC_Facebookcommerce_Utils::log( sprintf( 'No API key or Catalog ID: %s and %s', $this->get_page_access_token(), $this->get_product_catalog_id() ) );
2270
+
2271
+ throw new Framework\SV_WC_Plugin_Exception( __( 'The page access token or product catalog ID are missing.', 'facebook-for-woocommerce' ) );
 
2272
  }
2273
+
2274
  $this->remove_resync_message();
 
 
2275
 
2276
+ if ( ! $this->fbgraph->is_product_catalog_valid( $this->get_product_catalog_id() ) ) {
2277
+
2278
  WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
2279
  WC_Facebookcommerce_Utils::fblog(
2280
  'Tried to sync with an invalid product catalog!',
2281
  array(),
2282
  true
2283
  );
2284
+
2285
+ throw new Framework\SV_WC_Plugin_Exception( __( "We've detected that your Facebook Product Catalog is no longer valid. This may happen if it was deleted, but could also be a temporary error. If the error persists, please click Manage connection > Advanced Options > Remove and setup the plugin again.", 'facebook-for-woocommerce' ) );
 
 
 
 
 
 
 
2286
  }
2287
 
2288
  // Cache the cart URL to display a warning in case it changes later
2308
  );
2309
  }
2310
 
2311
+ if ( ! $this->fbproductfeed->sync_all_products_using_feed() ) {
2312
+
2313
+ WC_Facebookcommerce_Utils::fblog( 'Sync all products using feed, curl failed', [], true );
2314
+
2315
+ throw new Framework\SV_WC_Plugin_Exception( __( "We couldn't create the feed or upload the product information.", 'facebook-for-woocommerce' ) );
 
 
 
 
 
 
 
 
 
2316
  }
2317
+
2318
+ $this->update_feed_id( $this->fbproductfeed->feed_id );
2319
+
2320
+ $this->settings['fb_upload_id'] = $this->fbproductfeed->upload_id;
2321
+
2322
+ update_option(
2323
+ $this->get_option_key(),
2324
+ apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings )
2325
  );
2326
+
2327
+ wp_reset_postdata();
2328
+
2329
+ return true;
2330
+ }
2331
+
2332
+
2333
+ /**
2334
+ * Syncs Facebook products using a Feed.
2335
+ *
2336
+ * TODO: deprecate this methid in 1.11.0 or newer {WV 2020-03-12}
2337
+ *
2338
+ * @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
2339
+ *
2340
+ * @return bool
2341
+ */
2342
+ public function sync_all_fb_products_using_feed() {
2343
+
2344
+ try {
2345
+ $sync_started = $this->sync_facebook_products_using_feed();
2346
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
2347
+ $sync_started = false;
2348
+ }
2349
+
2350
+ return $sync_started;
2351
  }
2352
 
2353
 
2404
  $default_locale = 'en_US';
2405
  }
2406
 
2407
+ $default_messenger_greeting = __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' );
2408
+ $default_messenger_color = '#0084ff';
2409
+
2410
  $form_fields = [
2411
 
2412
  /** @see \WC_Facebookcommerce_Integration::generate_manage_connection_title_html() */
2523
  'class' => 'wc-enhanced-select messenger-field',
2524
  'default' => $default_locale,
2525
  'options' => $messenger_locales,
2526
+ 'custom_attributes' => [
2527
+ 'data-default' => $default_locale,
2528
+ ],
2529
  ],
2530
 
2531
  /** @see \WC_Facebookcommerce_Integration::generate_messenger_greeting_html() */
2534
  'title' => __( 'Greeting', 'facebook-for-woocommerce' ),
2535
  'type' => 'messenger_greeting',
2536
  'class' => 'messenger-field',
2537
+ 'default' => $default_messenger_greeting,
2538
  'css' => 'max-width: 400px; margin-bottom: 10px',
2539
  'custom_attributes' => [
2540
+ 'maxlength' => $this->get_messenger_greeting_max_characters(),
2541
+ 'data-default' => $default_messenger_greeting,
2542
  ],
2543
  ],
2544
 
2545
  self::SETTING_MESSENGER_COLOR_HEX => [
2546
+ 'title' => __( 'Colors', 'facebook-for-woocommerce' ),
2547
+ 'type' => 'color',
2548
+ 'class' => 'messenger-field',
2549
+ 'default' => $default_messenger_color,
2550
+ 'css' => 'width: 6em;',
2551
+ 'custom_attributes' => [
2552
+ 'data-default' => $default_messenger_color,
2553
+ ],
2554
+ ],
2555
+
2556
+ [
2557
+ 'title' => __( 'Debug', 'facebook-for-woocommerce' ),
2558
+ 'type' => 'title',
2559
+ ],
2560
+
2561
+ self::SETTING_ENABLE_DEBUG_MODE => [
2562
+ 'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ),
2563
+ 'type' => 'checkbox',
2564
+ 'label' => __( 'Log plugin events for debugging', 'facebook-for-woocommerce' ),
2565
+ 'desc_tip' => __( 'Only enable this if you are experiencing problems with the plugin.', 'facebook-for-woocommerce' ),
2566
+ 'default' => 'no',
2567
  ],
2568
 
2569
  ];
2794
  href="#"
2795
  style="vertical-align: middle; margin-left: 20px;"
2796
  ><?php esc_html_e( 'Sync products', 'facebook-for-woocommerce' ); ?></a>
 
2797
  </h3>
2798
+ <div><p id="sync_progress" style="display: none"></p></div>
2799
  <table class="form-table">
2800
  <?php
2801
 
2825
  } elseif ( $saved_resync_offset !== $current_resync_offset || false === $this->is_resync_scheduled() ) {
2826
  $this->schedule_resync( $saved_resync_offset );
2827
  }
2828
+
2829
+ // when settings are saved, if there are excluded categories/terms we can exclude corresponding products from sync
2830
+ $product_cats = $product_tags = [];
2831
+ $product_cat_ids = $this->get_excluded_product_category_ids();
2832
+ $product_tag_ids = $this->get_excluded_product_tag_ids();
2833
+
2834
+ $disable_sync_for_products = [];
2835
+
2836
+ // get all products belonging to excluded categories
2837
+ if ( ! empty( $product_cat_ids ) ) {
2838
+
2839
+ foreach ( $product_cat_ids as $tag_id ) {
2840
+
2841
+ $term = get_term_by( 'id', $tag_id, 'product_cat' );
2842
+
2843
+ if ( $term instanceof \WP_Term ) {
2844
+ $product_cats[] = $term->slug;
2845
+ }
2846
+ }
2847
+
2848
+ if ( ! empty( $product_cats ) ) {
2849
+
2850
+ $disable_sync_for_products = wc_get_products( [
2851
+ 'category' => $product_cats,
2852
+ 'limit' => -1,
2853
+ 'return' => 'ids',
2854
+ ] );
2855
+ }
2856
+ }
2857
+
2858
+ // get all products belonging to excluded tags
2859
+ if ( ! empty( $product_tag_ids ) ) {
2860
+
2861
+ foreach ( $product_tag_ids as $tag_id ) {
2862
+
2863
+ $term = get_term_by( 'id', $tag_id, 'product_tag' );
2864
+
2865
+ if ( $term instanceof \WP_Term ) {
2866
+ $product_tags[] = $term->slug;
2867
+ }
2868
+ }
2869
+
2870
+ if ( ! empty( $product_tags ) ) {
2871
+
2872
+ $disable_sync_for_products = array_merge( wc_get_products( [
2873
+ 'tag' => $product_tags,
2874
+ 'limit' => -1,
2875
+ 'return' => 'ids',
2876
+ ] ), $disable_sync_for_products );
2877
+ }
2878
+ }
2879
+
2880
+ if ( ! empty( $disable_sync_for_products ) ) {
2881
+
2882
+ // disable sync for found products that match any excluded term
2883
+ Products::disable_sync_for_products( wc_get_products( [
2884
+ 'limit' => -1,
2885
+ 'include' => array_unique( $disable_sync_for_products ),
2886
+ ] ) );
2887
+ }
2888
  }
2889
 
2890
 
3761
  }
3762
 
3763
 
3764
+ /**
3765
+ * Determines whether debug mode is enabled.
3766
+ *
3767
+ * @since 1.10.2
3768
+ *
3769
+ * @return bool
3770
+ */
3771
+ public function is_debug_mode_enabled() {
3772
+
3773
+ /**
3774
+ * Filters whether debug mode is enabled.
3775
+ *
3776
+ * @since 1.10.2
3777
+ *
3778
+ * @param bool $is_enabled whether debug mode is enabled
3779
+ * @param \WC_Facebookcommerce_Integration $integration the integration instance
3780
+ */
3781
+ return (bool) apply_filters( 'wc_facebook_is_debug_mode_enabled', 'yes' === $this->get_option( self::SETTING_ENABLE_DEBUG_MODE ), $this );
3782
+ }
3783
+
3784
+
3785
  /**
3786
  * Gets message HTML.
3787
  *
3810
  */
3811
  public function maybe_display_facebook_api_messages() {
3812
 
3813
+ if ( $error_msg = get_transient( 'facebook_plugin_api_error' ) ) {
3814
 
3815
+ $message = '<strong>' . __( 'Facebook for WooCommerce error:', 'facebook-for-woocommerce' ) . '</strong></br>' . $error_msg;
 
 
 
 
 
 
 
 
3816
 
3817
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3818
  echo $this->get_message_html( $message );
4173
 
4174
  $body = WC_Facebookcommerce_Utils::decode_json( $product_fbid_result['body'] );
4175
 
4176
+ if ( ! empty( $body->id ) ) {
4177
 
4178
  if ( $fbid_type == self::FB_PRODUCT_GROUP_ID ) {
4179
  $fb_id = $body->product_group->id;
4352
  */
4353
  public function handle_scheduled_resync_action() {
4354
 
4355
+ try {
4356
+ $this->sync_facebook_products_using_feed();
4357
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {}
4358
 
4359
  $resync_offset = $this->get_scheduled_resync_offset();
4360
 
facebook-for-woocommerce.php CHANGED
@@ -10,7 +10,7 @@
10
  * Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
11
  * Author: Facebook
12
  * Author URI: https://www.facebook.com/
13
- * Version: 1.10.1
14
  * Woo: 2127297:0ea4fe4c2d7ca6338f8a322fb3e4e187
15
  * Text Domain: facebook-for-woocommerce
16
  * WC requires at least: 3.5.0
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: 1.10.2
14
  * Woo: 2127297:0ea4fe4c2d7ca6338f8a322fb3e4e187
15
  * Text Domain: facebook-for-woocommerce
16
  * WC requires at least: 3.5.0
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 1.10.1\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
- "POT-Creation-Date: 2020-03-10 20:01:44+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -17,44 +17,44 @@ msgstr ""
17
  msgid "Facebook for WooCommerce"
18
  msgstr ""
19
 
20
- #: facebook-commerce.php:185
21
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
22
  msgstr ""
23
 
24
- #: facebook-commerce.php:581
25
  msgid "Facebook ID:"
26
  msgstr ""
27
 
28
- #: facebook-commerce.php:592
29
  msgid "Variant IDs:"
30
  msgstr ""
31
 
32
- #: facebook-commerce.php:619
33
  msgid "Visible:"
34
  msgstr ""
35
 
36
- #: facebook-commerce.php:630
37
  msgid "Reset Facebook metadata"
38
  msgstr ""
39
 
40
- #: facebook-commerce.php:635
41
  msgid "Delete product(s) on Facebook"
42
  msgstr ""
43
 
44
- #: facebook-commerce.php:643
45
  msgid "This product is not yet synced to Facebook."
46
  msgstr ""
47
 
48
- #: facebook-commerce.php:1278
49
  msgid "Nothing to update for product group for %1$s"
50
  msgstr ""
51
 
52
- #: facebook-commerce.php:1675
53
  #. translators: Placeholders %1$s - original error message from Facebook API
54
  msgid "There was an issue connecting to the Facebook API: %1$s"
55
  msgstr ""
56
 
57
- #: facebook-commerce.php:1809
58
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
59
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
60
  msgid ""
@@ -62,18 +62,63 @@ msgid ""
62
  "configuration, %3$scomplete the setup steps%4$s."
63
  msgstr ""
64
 
65
- #: facebook-commerce.php:1828
66
  #. translators: Placeholders %1$s - WooCommerce version
67
  msgid ""
68
  "Facebook product sync may not work correctly in WooCommerce version %1$s. "
69
  "Please upgrade to WooCommerce 3."
70
  msgstr ""
71
 
72
- #: facebook-commerce.php:2351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  msgid "Use Advanced Matching"
74
  msgstr ""
75
 
76
- #: facebook-commerce.php:2354
77
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
78
  #. </a> HTML link tag
79
  msgid ""
@@ -82,158 +127,160 @@ msgid ""
82
  "number). %1$sLearn more%2$s."
83
  msgstr ""
84
 
85
- #: facebook-commerce.php:2374
86
  msgid "Enable product sync"
87
  msgstr ""
88
 
89
- #: facebook-commerce.php:2382
90
  msgid "Exclude categories from sync"
91
  msgstr ""
92
 
93
- #: facebook-commerce.php:2386
94
  msgid "Products in one or more of these categories will not sync to Facebook."
95
  msgstr ""
96
 
97
- #: facebook-commerce.php:2390
98
  msgid "Search for a product category&hellip;"
99
  msgstr ""
100
 
101
- #: facebook-commerce.php:2395
102
  msgid "Exclude tags from sync"
103
  msgstr ""
104
 
105
- #: facebook-commerce.php:2399
106
  msgid "Products with one or more of these tags will not sync to Facebook."
107
  msgstr ""
108
 
109
- #: facebook-commerce.php:2403
110
  msgid "Search for a product tag&hellip;"
111
  msgstr ""
112
 
113
- #: facebook-commerce.php:2408
114
  msgid "Product description sync"
115
  msgstr ""
116
 
117
- #: facebook-commerce.php:2411
118
  msgid "Choose which product description to display in the Facebook catalog."
119
  msgstr ""
120
 
121
- #: facebook-commerce.php:2414
122
  msgid "Standard description"
123
  msgstr ""
124
 
125
- #: facebook-commerce.php:2415
126
  msgid "Short description"
127
  msgstr ""
128
 
129
- #: facebook-commerce.php:2422
130
  msgid "Force daily resync at"
131
  msgstr ""
132
 
133
- #: facebook-commerce.php:2428
134
  msgid "Messenger"
135
  msgstr ""
136
 
137
- #: facebook-commerce.php:2433
138
  msgid "Enable Messenger"
139
  msgstr ""
140
 
141
- #: facebook-commerce.php:2437
142
  msgid "Enable and customize Facebook Messenger on your store."
143
  msgstr ""
144
 
145
- #: facebook-commerce.php:2442
146
  msgid "Language"
147
  msgstr ""
148
 
149
- #: facebook-commerce.php:2452
150
  msgid "Greeting"
151
  msgstr ""
152
 
153
- #: facebook-commerce.php:2455
154
- msgid "Hi! We're here to answer any questions you may have."
155
  msgstr ""
156
 
157
- #: facebook-commerce.php:2463
158
- msgid "Colors"
159
  msgstr ""
160
 
161
- #: facebook-commerce.php:2496
162
- msgid "Connection"
163
  msgstr ""
164
 
165
- #: facebook-commerce.php:2503
166
- msgid "Manage connection"
167
  msgstr ""
168
 
169
- #: facebook-commerce.php:2508
170
- msgid "Your connection has expired."
171
  msgstr ""
172
 
173
- #: facebook-commerce.php:2510
174
- msgid ""
175
- "Please click Manage connection > Advanced Options > Update Token to refresh "
176
- "your connection to Facebook."
177
  msgstr ""
178
 
179
- #: facebook-commerce.php:2516
 
 
 
 
180
  msgid "Your access token has been updated."
181
  msgstr ""
182
 
183
- #: facebook-commerce.php:2518
184
  msgid "Please refresh the page."
185
  msgstr ""
186
 
187
- #: facebook-commerce.php:2552
188
  msgid "Facebook page"
189
  msgstr ""
190
 
191
- #: facebook-commerce.php:2616
192
  msgid "Pixel"
193
  msgstr ""
194
 
195
- #: facebook-commerce.php:2662
196
  msgid "Create ad"
197
  msgstr ""
198
 
199
- #: facebook-commerce.php:2691
200
  msgid "Product sync"
201
  msgstr ""
202
 
203
- #: facebook-commerce.php:2697
204
  msgid "Sync products"
205
  msgstr ""
206
 
207
- #: facebook-commerce.php:2837
208
  msgid "am"
209
  msgstr ""
210
 
211
- #: facebook-commerce.php:2842
212
  msgid "pm"
213
  msgstr ""
214
 
215
- #: facebook-commerce.php:2962
216
  msgid "The greeting hasn't been updated."
217
  msgstr ""
218
 
219
- #: facebook-commerce.php:2981
220
  #. translators: Placeholder: %d - maximum number of allowed characters
221
  msgid "The Messenger greeting must be %d characters or less."
222
  msgstr ""
223
 
224
- #: facebook-commerce.php:3638
225
- msgid "Facebook extension error: %s "
226
  msgstr ""
227
 
228
- #: facebook-commerce.php:3748
229
  msgid "Get started with Messenger Customer Chat"
230
  msgstr ""
231
 
232
- #: facebook-commerce.php:3749
233
  msgid "Get started with Instagram Shopping"
234
  msgstr ""
235
 
236
- #: facebook-commerce.php:3799
237
  msgid ""
238
  "You're using Remove HTTP which has incompatibilities with our extension. "
239
  "Please disable it, or select the \"Ignore external links\" option on the "
@@ -244,35 +291,35 @@ msgstr ""
244
  msgid "Facebook"
245
  msgstr ""
246
 
247
- #: facebook-commerce.php:3808
248
  msgid "Control how WooCommerce integrates with your Facebook store."
249
  msgstr ""
250
 
251
- #: facebook-commerce.php:3822
252
  msgid "Grow your business on Facebook"
253
  msgstr ""
254
 
255
- #: facebook-commerce.php:3825
256
  msgid "Use this WooCommerce and Facebook integration to:"
257
  msgstr ""
258
 
259
- #: facebook-commerce.php:3829
260
  msgid "Easily install a tracking pixel"
261
  msgstr ""
262
 
263
- #: facebook-commerce.php:3830
264
  msgid "Upload your products and create a shop"
265
  msgstr ""
266
 
267
- #: facebook-commerce.php:3831
268
  msgid "Create dynamic ads with your products and pixel"
269
  msgstr ""
270
 
271
- #: facebook-commerce.php:3839
272
  msgid "Get Started"
273
  msgstr ""
274
 
275
- #: facebook-commerce.php:3992
276
  #. translators: Placeholders %1$s - original error message from Facebook API
277
  msgid "There was an issue connecting to the Facebook API: %s"
278
  msgstr ""
@@ -295,7 +342,7 @@ msgstr ""
295
  msgid "Go to Settings"
296
  msgstr ""
297
 
298
- #: includes/AJAX.php:130 includes/AJAX.php:225 includes/AJAX.php:289
299
  msgid "Cancel"
300
  msgstr ""
301
 
@@ -334,7 +381,11 @@ msgstr ""
334
  msgid "Exclude Products"
335
  msgstr ""
336
 
337
- #: includes/AJAX.php:297
 
 
 
 
338
  #. translators: Placeholder %s - <br/> tags
339
  msgid ""
340
  "The categories and/or tags that you have selected to exclude from sync "
@@ -343,80 +394,139 @@ msgid ""
343
  "category / tag exclusion settings, click Cancel."
344
  msgstr ""
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  #: includes/Admin.php:132
347
- msgid "FB Sync Enabled"
348
  msgstr ""
349
 
350
  #: includes/Admin.php:133
 
 
 
 
 
 
 
 
 
 
351
  msgid "FB Catalog Visibility"
352
  msgstr ""
353
 
354
- #: includes/Admin.php:156
355
  msgid "Enabled"
356
  msgstr ""
357
 
358
- #: includes/Admin.php:158
359
  msgid "Disabled"
360
  msgstr ""
361
 
362
- #: includes/Admin.php:176
363
  #. translators: Points to a product that was never synced with Facebook
364
  msgid "Never synced with Facebook."
365
  msgstr ""
366
 
367
- #: includes/Admin.php:188
368
  #. translators: Action to hide a product (currently synced with Facebook) from
369
  #. the Facebook catalog
370
  msgid "Hide from Facebook catalog. Currently synced with Facebook."
371
  msgstr ""
372
 
373
- #: includes/Admin.php:190
374
  #. translators: Action to publish a product (currently synced with Facebook) in
375
  #. the Facebook catalog
376
  msgid "Publish in Facebook catalog. Currently synced with Facebook."
377
  msgstr ""
378
 
379
- #: includes/Admin.php:193
380
  #. translators: Action to hide a product (currently not synced with Facebook)
381
  #. from the Facebook catalog
382
  msgid "Hide from Facebook catalog. Not synced with Facebook."
383
  msgstr ""
384
 
385
- #: includes/Admin.php:195
386
  #. translators: Action to publish a product (currently not synced with
387
  #. Facebook) in the Facebook catalog
388
  msgid "Publish in Facebook catalog. Not synced with Facebook."
389
  msgstr ""
390
 
391
- #: includes/Admin.php:206
392
  msgid "Show"
393
  msgstr ""
394
 
395
- #: includes/Admin.php:214
396
  msgid "Hide"
397
  msgstr ""
398
 
399
- #: includes/Admin.php:242
400
  msgid "Filter by Facebook sync setting"
401
  msgstr ""
402
 
403
- #: includes/Admin.php:243
404
  msgid "Facebook sync enabled"
405
  msgstr ""
406
 
407
- #: includes/Admin.php:244
408
  msgid "Facebook sync disabled"
409
  msgstr ""
410
 
411
- #: includes/Admin.php:403 includes/Admin.php:552 includes/Admin.php:644
412
  msgid "Include in Facebook sync"
413
  msgstr ""
414
 
415
- #: includes/Admin.php:404
416
  msgid "Exclude from Facebook sync"
417
  msgstr ""
418
 
419
- #: includes/Admin.php:491
420
  #. translators: Placeholders: %1$s - Facebook for Woocommerce, %2$s - opening
421
  #. HTML <a> link tag, %3$s - closing HTML </a> link tag
422
  msgid ""
@@ -425,64 +535,68 @@ msgid ""
425
  "checkout URLs on Facebook%3$s."
426
  msgstr ""
427
 
428
- #: includes/Admin.php:558 includes/Admin.php:653
429
  msgid "Facebook Description"
430
  msgstr ""
431
 
432
- #: includes/Admin.php:560 includes/Admin.php:655
433
  msgid ""
434
  "Custom (plain-text only) description for product on Facebook. If blank, "
435
  "product description will be used. If product description is blank, "
436
  "shortname will be used."
437
  msgstr ""
438
 
439
- #: includes/Admin.php:569 includes/Admin.php:666
440
  msgid "Facebook Product Image"
441
  msgstr ""
442
 
443
- #: includes/Admin.php:571 includes/Admin.php:668
444
  msgid ""
445
  "Choose the product image that should be synced to the Facebook catalog for "
446
  "this product. If using a custom image, please enter an absolute URL (e.g. "
447
  "https://domain.com/image.jpg)."
448
  msgstr ""
449
 
450
- #: includes/Admin.php:573
451
  msgid "Use WooCommerce image"
452
  msgstr ""
453
 
454
- #: includes/Admin.php:574 includes/Admin.php:672
455
  msgid "Use custom image"
456
  msgstr ""
457
 
458
- #: includes/Admin.php:583 includes/Admin.php:682
459
  msgid "Custom Image URL"
460
  msgstr ""
461
 
462
- #: includes/Admin.php:592 includes/Admin.php:693
463
  #. translators: Placeholders %1$s - WC currency symbol
464
  msgid "Facebook Price (%1$s)"
465
  msgstr ""
466
 
467
- #: includes/Admin.php:596 includes/Admin.php:697
468
  msgid ""
469
  "Custom price for product on Facebook. Please enter in monetary decimal (.) "
470
  "format without thousand separators and currency symbols. If blank, product "
471
  "price will be used."
472
  msgstr ""
473
 
474
- #: includes/Admin.php:670
475
  msgid "Use variation image"
476
  msgstr ""
477
 
478
- #: includes/Admin.php:671
479
  msgid "Use parent image"
480
  msgstr ""
481
 
482
- #: includes/Admin.php:802
483
  msgid "Close modal panel"
484
  msgstr ""
485
 
 
 
 
 
486
  #: includes/fbinfobanner.php:211
487
  msgid "Click and redirect."
488
  msgstr ""
@@ -532,7 +646,7 @@ msgstr ""
532
  msgid "https://www.facebook.com/"
533
  msgstr ""
534
 
535
- #: facebook-commerce-messenger-chat.php:258 facebook-commerce.php:2327
536
  msgctxt "language"
537
  msgid "English (United States)"
538
  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 1.10.2\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
+ "POT-Creation-Date: 2020-03-18 01:00:59+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
17
  msgid "Facebook for WooCommerce"
18
  msgstr ""
19
 
20
+ #: facebook-commerce.php:188
21
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
22
  msgstr ""
23
 
24
+ #: facebook-commerce.php:585
25
  msgid "Facebook ID:"
26
  msgstr ""
27
 
28
+ #: facebook-commerce.php:596
29
  msgid "Variant IDs:"
30
  msgstr ""
31
 
32
+ #: facebook-commerce.php:623
33
  msgid "Visible:"
34
  msgstr ""
35
 
36
+ #: facebook-commerce.php:634
37
  msgid "Reset Facebook metadata"
38
  msgstr ""
39
 
40
+ #: facebook-commerce.php:639
41
  msgid "Delete product(s) on Facebook"
42
  msgstr ""
43
 
44
+ #: facebook-commerce.php:647
45
  msgid "This product is not yet synced to Facebook."
46
  msgstr ""
47
 
48
+ #: facebook-commerce.php:1282
49
  msgid "Nothing to update for product group for %1$s"
50
  msgstr ""
51
 
52
+ #: facebook-commerce.php:1683
53
  #. translators: Placeholders %1$s - original error message from Facebook API
54
  msgid "There was an issue connecting to the Facebook API: %1$s"
55
  msgstr ""
56
 
57
+ #: facebook-commerce.php:1817
58
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
59
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
60
  msgid ""
62
  "configuration, %3$scomplete the setup steps%4$s."
63
  msgstr ""
64
 
65
+ #: facebook-commerce.php:1836
66
  #. translators: Placeholders %1$s - WooCommerce version
67
  msgid ""
68
  "Facebook product sync may not work correctly in WooCommerce version %1$s. "
69
  "Please upgrade to WooCommerce 3."
70
  msgstr ""
71
 
72
+ #: facebook-commerce.php:2055 facebook-commerce.php:2607
73
+ msgid "Your connection has expired."
74
+ msgstr ""
75
+
76
+ #: facebook-commerce.php:2055 facebook-commerce.php:2609
77
+ msgid ""
78
+ "Please click Manage connection > Advanced Options > Update Token to refresh "
79
+ "your connection to Facebook."
80
+ msgstr ""
81
+
82
+ #: facebook-commerce.php:2062
83
+ #. translators: Placeholders %s - error message
84
+ msgid "There was an error trying to sync the products to Facebook. %s"
85
+ msgstr ""
86
+
87
+ #: facebook-commerce.php:2085 facebook-commerce.php:2264
88
+ msgid "Product sync is disabled."
89
+ msgstr ""
90
+
91
+ #: facebook-commerce.php:2092 facebook-commerce.php:2271
92
+ msgid "The page access token or product catalog ID are missing."
93
+ msgstr ""
94
+
95
+ #: facebook-commerce.php:2115
96
+ msgid ""
97
+ "A product sync is in progress. Please wait until the sync finishes before "
98
+ "starting a new one."
99
+ msgstr ""
100
+
101
+ #: facebook-commerce.php:2127 facebook-commerce.php:2285
102
+ msgid ""
103
+ "We've detected that your Facebook Product Catalog is no longer valid. This "
104
+ "may happen if it was deleted, but could also be a temporary error. If the "
105
+ "error persists, please click Manage connection > Advanced Options > Remove "
106
+ "and setup the plugin again."
107
+ msgstr ""
108
+
109
+ #: facebook-commerce.php:2315
110
+ msgid "We couldn't create the feed or upload the product information."
111
+ msgstr ""
112
+
113
+ #: facebook-commerce.php:2407
114
+ msgid "Hi! We're here to answer any questions you may have."
115
+ msgstr ""
116
+
117
+ #: facebook-commerce.php:2430
118
  msgid "Use Advanced Matching"
119
  msgstr ""
120
 
121
+ #: facebook-commerce.php:2433
122
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
123
  #. </a> HTML link tag
124
  msgid ""
127
  "number). %1$sLearn more%2$s."
128
  msgstr ""
129
 
130
+ #: facebook-commerce.php:2453
131
  msgid "Enable product sync"
132
  msgstr ""
133
 
134
+ #: facebook-commerce.php:2461
135
  msgid "Exclude categories from sync"
136
  msgstr ""
137
 
138
+ #: facebook-commerce.php:2465
139
  msgid "Products in one or more of these categories will not sync to Facebook."
140
  msgstr ""
141
 
142
+ #: facebook-commerce.php:2469
143
  msgid "Search for a product category&hellip;"
144
  msgstr ""
145
 
146
+ #: facebook-commerce.php:2474
147
  msgid "Exclude tags from sync"
148
  msgstr ""
149
 
150
+ #: facebook-commerce.php:2478
151
  msgid "Products with one or more of these tags will not sync to Facebook."
152
  msgstr ""
153
 
154
+ #: facebook-commerce.php:2482
155
  msgid "Search for a product tag&hellip;"
156
  msgstr ""
157
 
158
+ #: facebook-commerce.php:2487
159
  msgid "Product description sync"
160
  msgstr ""
161
 
162
+ #: facebook-commerce.php:2490
163
  msgid "Choose which product description to display in the Facebook catalog."
164
  msgstr ""
165
 
166
+ #: facebook-commerce.php:2493
167
  msgid "Standard description"
168
  msgstr ""
169
 
170
+ #: facebook-commerce.php:2494
171
  msgid "Short description"
172
  msgstr ""
173
 
174
+ #: facebook-commerce.php:2501
175
  msgid "Force daily resync at"
176
  msgstr ""
177
 
178
+ #: facebook-commerce.php:2507
179
  msgid "Messenger"
180
  msgstr ""
181
 
182
+ #: facebook-commerce.php:2512
183
  msgid "Enable Messenger"
184
  msgstr ""
185
 
186
+ #: facebook-commerce.php:2516
187
  msgid "Enable and customize Facebook Messenger on your store."
188
  msgstr ""
189
 
190
+ #: facebook-commerce.php:2521
191
  msgid "Language"
192
  msgstr ""
193
 
194
+ #: facebook-commerce.php:2534
195
  msgid "Greeting"
196
  msgstr ""
197
 
198
+ #: facebook-commerce.php:2546
199
+ msgid "Colors"
200
  msgstr ""
201
 
202
+ #: facebook-commerce.php:2557
203
+ msgid "Debug"
204
  msgstr ""
205
 
206
+ #: facebook-commerce.php:2562
207
+ msgid "Enable debug mode"
208
  msgstr ""
209
 
210
+ #: facebook-commerce.php:2564
211
+ msgid "Log plugin events for debugging"
212
  msgstr ""
213
 
214
+ #: facebook-commerce.php:2565
215
+ msgid "Only enable this if you are experiencing problems with the plugin."
216
  msgstr ""
217
 
218
+ #: facebook-commerce.php:2595
219
+ msgid "Connection"
 
 
220
  msgstr ""
221
 
222
+ #: facebook-commerce.php:2602
223
+ msgid "Manage connection"
224
+ msgstr ""
225
+
226
+ #: facebook-commerce.php:2615
227
  msgid "Your access token has been updated."
228
  msgstr ""
229
 
230
+ #: facebook-commerce.php:2617
231
  msgid "Please refresh the page."
232
  msgstr ""
233
 
234
+ #: facebook-commerce.php:2651
235
  msgid "Facebook page"
236
  msgstr ""
237
 
238
+ #: facebook-commerce.php:2715
239
  msgid "Pixel"
240
  msgstr ""
241
 
242
+ #: facebook-commerce.php:2761
243
  msgid "Create ad"
244
  msgstr ""
245
 
246
+ #: facebook-commerce.php:2790
247
  msgid "Product sync"
248
  msgstr ""
249
 
250
+ #: facebook-commerce.php:2796
251
  msgid "Sync products"
252
  msgstr ""
253
 
254
+ #: facebook-commerce.php:2996
255
  msgid "am"
256
  msgstr ""
257
 
258
+ #: facebook-commerce.php:3001
259
  msgid "pm"
260
  msgstr ""
261
 
262
+ #: facebook-commerce.php:3121
263
  msgid "The greeting hasn't been updated."
264
  msgstr ""
265
 
266
+ #: facebook-commerce.php:3140
267
  #. translators: Placeholder: %d - maximum number of allowed characters
268
  msgid "The Messenger greeting must be %d characters or less."
269
  msgstr ""
270
 
271
+ #: facebook-commerce.php:3815
272
+ msgid "Facebook for WooCommerce error:"
273
  msgstr ""
274
 
275
+ #: facebook-commerce.php:3920
276
  msgid "Get started with Messenger Customer Chat"
277
  msgstr ""
278
 
279
+ #: facebook-commerce.php:3921
280
  msgid "Get started with Instagram Shopping"
281
  msgstr ""
282
 
283
+ #: facebook-commerce.php:3971
284
  msgid ""
285
  "You're using Remove HTTP which has incompatibilities with our extension. "
286
  "Please disable it, or select the \"Ignore external links\" option on the "
291
  msgid "Facebook"
292
  msgstr ""
293
 
294
+ #: facebook-commerce.php:3980
295
  msgid "Control how WooCommerce integrates with your Facebook store."
296
  msgstr ""
297
 
298
+ #: facebook-commerce.php:3994
299
  msgid "Grow your business on Facebook"
300
  msgstr ""
301
 
302
+ #: facebook-commerce.php:3997
303
  msgid "Use this WooCommerce and Facebook integration to:"
304
  msgstr ""
305
 
306
+ #: facebook-commerce.php:4001
307
  msgid "Easily install a tracking pixel"
308
  msgstr ""
309
 
310
+ #: facebook-commerce.php:4002
311
  msgid "Upload your products and create a shop"
312
  msgstr ""
313
 
314
+ #: facebook-commerce.php:4003
315
  msgid "Create dynamic ads with your products and pixel"
316
  msgstr ""
317
 
318
+ #: facebook-commerce.php:4011
319
  msgid "Get Started"
320
  msgstr ""
321
 
322
+ #: facebook-commerce.php:4164
323
  #. translators: Placeholders %1$s - original error message from Facebook API
324
  msgid "There was an issue connecting to the Facebook API: %s"
325
  msgstr ""
342
  msgid "Go to Settings"
343
  msgstr ""
344
 
345
+ #: includes/AJAX.php:130 includes/AJAX.php:225 includes/AJAX.php:293
346
  msgid "Cancel"
347
  msgstr ""
348
 
381
  msgid "Exclude Products"
382
  msgstr ""
383
 
384
+ #: includes/AJAX.php:288
385
+ msgid "Exclude Products and Hide in Facebook"
386
+ msgstr ""
387
+
388
+ #: includes/AJAX.php:301
389
  #. translators: Placeholder %s - <br/> tags
390
  msgid ""
391
  "The categories and/or tags that you have selected to exclude from sync "
394
  "category / tag exclusion settings, click Cancel."
395
  msgstr ""
396
 
397
+ #: includes/Admin.php:112
398
+ #. translators: Placeholders %1$s - opening <strong> html tag, %2$s closing
399
+ #. </strong> html tag, {count} number of remaining items
400
+ msgid "%1$sProgress:%2$s {count} item remaining."
401
+ msgid_plural "%1$sProgress:%2$s {count} items remaining."
402
+ msgstr[0] ""
403
+ msgstr[1] ""
404
+
405
+ #: includes/Admin.php:120
406
+ #. translators: Placeholders %s - html code for a spinner icon
407
+ msgid "Your products will now be resynced to Facebook, this may take some time."
408
+ msgstr ""
409
+
410
+ #: includes/Admin.php:121
411
+ msgid "Launch Test?"
412
+ msgstr ""
413
+
414
+ #: includes/Admin.php:122
415
+ msgid ""
416
+ "Facebook for WooCommerce automatically syncs your products on "
417
+ "create/update. Are you sure you want to force product resync?\n"
418
+ "\n"
419
+ "This will query all published products and may take some time. You only "
420
+ "need to do this if your products are out of sync or some of your products "
421
+ "did not sync."
422
+ msgstr ""
423
+
424
+ #: includes/Admin.php:123
425
+ msgid "Syncing... Keep this browser open until sync is complete. %s"
426
+ msgstr ""
427
+
428
+ #: includes/Admin.php:127
429
+ #. translators: Placeholders %1$s - opening <strong> html tag, %2$s closing
430
+ #. </strong> html tag
431
+ msgid "%1$sStatus:%2$s Test Pass."
432
+ msgstr ""
433
+
434
+ #: includes/Admin.php:129
435
+ #. translators: Placeholders %1$s - opening <strong> html tag, %2$s closing
436
+ #. </strong> html tag
437
+ msgid "%1$sStatus:%2$s Integration test in progress..."
438
+ msgstr ""
439
+
440
+ #: includes/Admin.php:131
441
+ #. translators: Placeholders %1$s - opening <strong> html tag, %2$s closing
442
+ #. </strong> html tag
443
+ msgid "%1$sStatus:%2$s Test Fail."
444
+ msgstr ""
445
+
446
  #: includes/Admin.php:132
447
+ msgid "There was an error trying to sync the products to Facebook."
448
  msgstr ""
449
 
450
  #: includes/Admin.php:133
451
+ msgid ""
452
+ "Something went wrong while uploading the product information, please try "
453
+ "again."
454
+ msgstr ""
455
+
456
+ #: includes/Admin.php:153
457
+ msgid "FB Sync Enabled"
458
+ msgstr ""
459
+
460
+ #: includes/Admin.php:154
461
  msgid "FB Catalog Visibility"
462
  msgstr ""
463
 
464
+ #: includes/Admin.php:177
465
  msgid "Enabled"
466
  msgstr ""
467
 
468
+ #: includes/Admin.php:179
469
  msgid "Disabled"
470
  msgstr ""
471
 
472
+ #: includes/Admin.php:197
473
  #. translators: Points to a product that was never synced with Facebook
474
  msgid "Never synced with Facebook."
475
  msgstr ""
476
 
477
+ #: includes/Admin.php:209
478
  #. translators: Action to hide a product (currently synced with Facebook) from
479
  #. the Facebook catalog
480
  msgid "Hide from Facebook catalog. Currently synced with Facebook."
481
  msgstr ""
482
 
483
+ #: includes/Admin.php:211
484
  #. translators: Action to publish a product (currently synced with Facebook) in
485
  #. the Facebook catalog
486
  msgid "Publish in Facebook catalog. Currently synced with Facebook."
487
  msgstr ""
488
 
489
+ #: includes/Admin.php:214
490
  #. translators: Action to hide a product (currently not synced with Facebook)
491
  #. from the Facebook catalog
492
  msgid "Hide from Facebook catalog. Not synced with Facebook."
493
  msgstr ""
494
 
495
+ #: includes/Admin.php:216
496
  #. translators: Action to publish a product (currently not synced with
497
  #. Facebook) in the Facebook catalog
498
  msgid "Publish in Facebook catalog. Not synced with Facebook."
499
  msgstr ""
500
 
501
+ #: includes/Admin.php:227
502
  msgid "Show"
503
  msgstr ""
504
 
505
+ #: includes/Admin.php:235
506
  msgid "Hide"
507
  msgstr ""
508
 
509
+ #: includes/Admin.php:263
510
  msgid "Filter by Facebook sync setting"
511
  msgstr ""
512
 
513
+ #: includes/Admin.php:264
514
  msgid "Facebook sync enabled"
515
  msgstr ""
516
 
517
+ #: includes/Admin.php:265
518
  msgid "Facebook sync disabled"
519
  msgstr ""
520
 
521
+ #: includes/Admin.php:424 includes/Admin.php:573 includes/Admin.php:665
522
  msgid "Include in Facebook sync"
523
  msgstr ""
524
 
525
+ #: includes/Admin.php:425
526
  msgid "Exclude from Facebook sync"
527
  msgstr ""
528
 
529
+ #: includes/Admin.php:512
530
  #. translators: Placeholders: %1$s - Facebook for Woocommerce, %2$s - opening
531
  #. HTML <a> link tag, %3$s - closing HTML </a> link tag
532
  msgid ""
535
  "checkout URLs on Facebook%3$s."
536
  msgstr ""
537
 
538
+ #: includes/Admin.php:579 includes/Admin.php:674
539
  msgid "Facebook Description"
540
  msgstr ""
541
 
542
+ #: includes/Admin.php:581 includes/Admin.php:676
543
  msgid ""
544
  "Custom (plain-text only) description for product on Facebook. If blank, "
545
  "product description will be used. If product description is blank, "
546
  "shortname will be used."
547
  msgstr ""
548
 
549
+ #: includes/Admin.php:590 includes/Admin.php:687
550
  msgid "Facebook Product Image"
551
  msgstr ""
552
 
553
+ #: includes/Admin.php:592 includes/Admin.php:689
554
  msgid ""
555
  "Choose the product image that should be synced to the Facebook catalog for "
556
  "this product. If using a custom image, please enter an absolute URL (e.g. "
557
  "https://domain.com/image.jpg)."
558
  msgstr ""
559
 
560
+ #: includes/Admin.php:594
561
  msgid "Use WooCommerce image"
562
  msgstr ""
563
 
564
+ #: includes/Admin.php:595 includes/Admin.php:693
565
  msgid "Use custom image"
566
  msgstr ""
567
 
568
+ #: includes/Admin.php:604 includes/Admin.php:703
569
  msgid "Custom Image URL"
570
  msgstr ""
571
 
572
+ #: includes/Admin.php:613 includes/Admin.php:714
573
  #. translators: Placeholders %1$s - WC currency symbol
574
  msgid "Facebook Price (%1$s)"
575
  msgstr ""
576
 
577
+ #: includes/Admin.php:617 includes/Admin.php:718
578
  msgid ""
579
  "Custom price for product on Facebook. Please enter in monetary decimal (.) "
580
  "format without thousand separators and currency symbols. If blank, product "
581
  "price will be used."
582
  msgstr ""
583
 
584
+ #: includes/Admin.php:691
585
  msgid "Use variation image"
586
  msgstr ""
587
 
588
+ #: includes/Admin.php:692
589
  msgid "Use parent image"
590
  msgstr ""
591
 
592
+ #: includes/Admin.php:823
593
  msgid "Close modal panel"
594
  msgstr ""
595
 
596
+ #: includes/fbgraph.php:104
597
+ msgid "HTTP %s: %s"
598
+ msgstr ""
599
+
600
  #: includes/fbinfobanner.php:211
601
  msgid "Click and redirect."
602
  msgstr ""
646
  msgid "https://www.facebook.com/"
647
  msgstr ""
648
 
649
+ #: facebook-commerce-messenger-chat.php:258 facebook-commerce.php:2403
650
  msgctxt "language"
651
  msgid "English (United States)"
652
  msgstr ""
includes/AJAX.php CHANGED
@@ -280,8 +280,12 @@ class AJAX {
280
  ?>
281
  <button
282
  id="facebook-for-woocommerce-confirm-settings-change"
283
- class="button button-large button-primary"
284
  ><?php esc_html_e( 'Exclude Products', 'facebook-for-woocommerce' ); ?></button>
 
 
 
 
285
  <button
286
  id="facebook-for-woocommerce-cancel-settings-change"
287
  class="button button-large button-primary"
@@ -418,6 +422,59 @@ class AJAX {
418
  }
419
 
420
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  /**
422
  * Sets products visibility in Facebook.
423
  *
@@ -429,24 +486,46 @@ class AJAX {
429
 
430
  check_ajax_referer( 'set-products-visibility', 'security' );
431
 
 
 
432
  $integration = facebook_for_woocommerce()->get_integration();
433
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
434
- $products = isset( $_POST['products'] ) ? (array) $_POST['products'] : [];
 
 
 
 
 
 
 
 
 
 
 
435
 
436
  if ( $integration && ! empty( $products ) ) {
437
 
 
 
438
  foreach ( $products as $product_data ) {
439
 
 
 
 
 
 
 
 
440
  $visibility_meta_value = isset( $product_data['visibility'] ) ? wc_string_to_bool( $product_data['visibility'] ) : null;
441
 
 
442
  if ( ! is_bool( $visibility_meta_value ) ) {
443
  continue;
444
  }
445
 
446
  $visibility_api_value = $visibility_meta_value ? $integration::FB_SHOP_PRODUCT_VISIBLE : $integration::FB_SHOP_PRODUCT_HIDDEN;
447
 
448
- $product_id = isset( $product_data['product_id'] ) ? absint( $product_data['product_id'] ) : 0;
449
- $product = $product_id > 0 ? wc_get_product( $product_id ) : null;
450
 
451
  if ( $product instanceof \WC_Product ) {
452
 
@@ -455,6 +534,11 @@ class AJAX {
455
 
456
  foreach ( $product->get_children() as $variation_id ) {
457
 
 
 
 
 
 
458
  if ( $variation_product = wc_get_product( $variation_id ) ) {
459
 
460
  $fb_item_id = $integration->get_product_fbid( \WC_Facebookcommerce_Integration::FB_PRODUCT_ITEM_ID, $variation_product->get_id() );
@@ -465,6 +549,8 @@ class AJAX {
465
  if ( $integration->check_api_result( $fb_request ) ) {
466
  Products::set_product_visibility( $variation_product, $visibility_meta_value );
467
  }
 
 
468
  }
469
  }
470
 
@@ -481,13 +567,15 @@ class AJAX {
481
  Products::set_product_visibility( $product, $visibility_meta_value );
482
  }
483
  }
 
 
484
  }
485
  }
486
 
487
- wp_send_json_success();
488
  }
489
 
490
- wp_send_json_error();
491
  }
492
 
493
 
280
  ?>
281
  <button
282
  id="facebook-for-woocommerce-confirm-settings-change"
283
+ class="button button-large button-primary facebook-for-woocommerce-confirm-settings-change"
284
  ><?php esc_html_e( 'Exclude Products', 'facebook-for-woocommerce' ); ?></button>
285
+ <button
286
+ id="facebook-for-woocommerce-confirm-settings-change-hide-products"
287
+ class="button button-large button-primary facebook-for-woocommerce-confirm-settings-change hide-products"
288
+ ><?php esc_html_e( 'Exclude Products and Hide in Facebook', 'facebook-for-woocommerce' ); ?></button>
289
  <button
290
  id="facebook-for-woocommerce-cancel-settings-change"
291
  class="button button-large button-primary"
422
  }
423
 
424
 
425
+ /**
426
+ * Gets an array of product IDs data for handling visibility (helper method).
427
+ *
428
+ * @see \SkyVerge\WooCommerce\Facebook\AJAX::set_products_visibility()
429
+ *
430
+ * @since 1.10.2
431
+ *
432
+ * @param array $terms_data term data with product IDs and visibility
433
+ * @return array product IDs and visibility data
434
+ */
435
+ private function get_product_ids_for_visibility_from_terms( $terms_data ) {
436
+
437
+ $products = [];
438
+
439
+ foreach ( $terms_data as $term_data ) {
440
+
441
+ if ( ! isset( $term_data['term_id'], $term_data['taxonomy'], $term_data['visibility'] ) ) {
442
+ continue;
443
+ }
444
+
445
+ if ( 'product_cat' === $term_data['taxonomy'] ) {
446
+ $tax_query_arg = 'category';
447
+ } elseif( 'product_tag' === $term_data['taxonomy'] ) {
448
+ $tax_query_arg = 'tag';
449
+ } else {
450
+ continue;
451
+ }
452
+
453
+ $term = get_term_by( 'id', $term_data['term_id'], $term_data['taxonomy'] );
454
+
455
+ if ( ! $term instanceof \WP_Term ) {
456
+ continue;
457
+ }
458
+
459
+ $found_products = wc_get_products( [
460
+ 'limit' => -1,
461
+ 'return' => 'ids',
462
+ $tax_query_arg => [ $term->slug ],
463
+ ] );
464
+
465
+ foreach ( $found_products as $product_id ) {
466
+
467
+ $products[] = [
468
+ 'product_id' => $product_id,
469
+ 'visibility' => $term_data['visibility']
470
+ ];
471
+ }
472
+ }
473
+
474
+ return $products;
475
+ }
476
+
477
+
478
  /**
479
  * Sets products visibility in Facebook.
480
  *
486
 
487
  check_ajax_referer( 'set-products-visibility', 'security' );
488
 
489
+ // phpcs:disable WordPress.Security.NonceVerification.Missing
490
+
491
  $integration = facebook_for_woocommerce()->get_integration();
492
+ $products = isset( $_POST['products'] ) ? (array) $_POST['products'] : [];
493
+
494
+ if ( ! empty( $_POST['product_categories'] ) && is_array( $_POST['product_categories'] ) ) {
495
+ $products = array_merge( $products, $this->get_product_ids_for_visibility_from_terms( $_POST['product_categories'] ) );
496
+ }
497
+
498
+ if ( ! empty( $_POST['product_tags'] ) && is_array( $_POST['product_tags'] ) ) {
499
+ $products = array_merge( $products, $this->get_product_ids_for_visibility_from_terms( $_POST['product_tags'] ) );
500
+ }
501
+
502
+ // phpcs:enable WordPress.Security.NonceVerification.Missing
503
+
504
+ $error = 'No products found to set visibility for.'; // console error message
505
 
506
  if ( $integration && ! empty( $products ) ) {
507
 
508
+ $processed_products = [];
509
+
510
  foreach ( $products as $product_data ) {
511
 
512
+ $product_id = isset( $product_data['product_id'] ) ? absint( $product_data['product_id'] ) : 0;
513
+
514
+ // bail if already processed
515
+ if ( in_array( $product_id, $processed_products, true ) ) {
516
+ continue;
517
+ }
518
+
519
  $visibility_meta_value = isset( $product_data['visibility'] ) ? wc_string_to_bool( $product_data['visibility'] ) : null;
520
 
521
+ // bail if visibility value is not valid
522
  if ( ! is_bool( $visibility_meta_value ) ) {
523
  continue;
524
  }
525
 
526
  $visibility_api_value = $visibility_meta_value ? $integration::FB_SHOP_PRODUCT_VISIBLE : $integration::FB_SHOP_PRODUCT_HIDDEN;
527
 
528
+ $product = $product_id > 0 ? wc_get_product( $product_id ) : null;
 
529
 
530
  if ( $product instanceof \WC_Product ) {
531
 
534
 
535
  foreach ( $product->get_children() as $variation_id ) {
536
 
537
+ // bail if already processed
538
+ if ( in_array( $variation_id, $processed_products, true ) ) {
539
+ continue;
540
+ }
541
+
542
  if ( $variation_product = wc_get_product( $variation_id ) ) {
543
 
544
  $fb_item_id = $integration->get_product_fbid( \WC_Facebookcommerce_Integration::FB_PRODUCT_ITEM_ID, $variation_product->get_id() );
549
  if ( $integration->check_api_result( $fb_request ) ) {
550
  Products::set_product_visibility( $variation_product, $visibility_meta_value );
551
  }
552
+
553
+ $processed_products[] = $variation_id;
554
  }
555
  }
556
 
567
  Products::set_product_visibility( $product, $visibility_meta_value );
568
  }
569
  }
570
+
571
+ $processed_products[] = $product_id;
572
  }
573
  }
574
 
575
+ wp_send_json_success( $processed_products );
576
  }
577
 
578
+ wp_send_json_error( $error );
579
  }
580
 
581
 
includes/Admin.php CHANGED
@@ -84,7 +84,7 @@ class Admin {
84
 
85
  if ( isset( $current_screen->id ) ) {
86
 
87
- if ( in_array( $current_screen->id, $modal_screens ) || facebook_for_woocommerce()->is_plugin_settings() ) {
88
 
89
  // enqueue modal functions
90
  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 );
@@ -108,9 +108,30 @@ class Admin {
108
 
109
  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', 'facebook-for-woocommerce-modal' ], \WC_Facebookcommerce::PLUGIN_VERSION );
110
 
 
 
 
111
  wp_localize_script( 'facebook-for-woocommerce-settings-sync', 'facebook_for_woocommerce_settings_sync', [
112
  'ajax_url' => admin_url( 'admin-ajax.php' ),
113
  'set_excluded_terms_prompt_nonce' => wp_create_nonce( 'set-excluded-terms-prompt' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  ] );
115
  }
116
  }
84
 
85
  if ( isset( $current_screen->id ) ) {
86
 
87
+ if ( in_array( $current_screen->id, $modal_screens, true ) || facebook_for_woocommerce()->is_plugin_settings() ) {
88
 
89
  // enqueue modal functions
90
  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 );
108
 
109
  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', 'facebook-for-woocommerce-modal' ], \WC_Facebookcommerce::PLUGIN_VERSION );
110
 
111
+ /* translators: Placeholders %1$s - opening <strong> html tag, %2$s closing </strong> html tag, {count} number of remaining items */
112
+ $sync_remaining_items_string = _n_noop( '%1$sProgress:%2$s {count} item remaining.', '%1$sProgress:%2$s {count} items remaining.', 'facebook-for-woocommerce' );
113
+
114
  wp_localize_script( 'facebook-for-woocommerce-settings-sync', 'facebook_for_woocommerce_settings_sync', [
115
  'ajax_url' => admin_url( 'admin-ajax.php' ),
116
  'set_excluded_terms_prompt_nonce' => wp_create_nonce( 'set-excluded-terms-prompt' ),
117
+ 'set_product_visibility_nonce' => wp_create_nonce( 'set-products-visibility' ),
118
+ 'i18n' => [
119
+ /* translators: Placeholders %s - html code for a spinner icon */
120
+ 'confirm_resync' => esc_html__( 'Your products will now be resynced to Facebook, this may take some time.', 'facebook-for-woocommerce' ),
121
+ 'confirm_sync_test' => esc_html__( 'Launch Test?', 'facebook-for-woocommerce' ),
122
+ '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' ),
123
+ 'sync_in_progress' => sprintf( esc_html__( 'Syncing... Keep this browser open until sync is complete. %s', 'facebook-for-woocommerce' ), '<span class="spinner is-active"></span>' ),
124
+ 'sync_remaining_items_singular' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 1 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
125
+ 'sync_remaining_items_plural' => sprintf( esc_html( translate_nooped_plural( $sync_remaining_items_string, 2 ) ), '<strong>', '</strong>', '<span class="spinner is-active"></span>' ),
126
+ /* translators: Placeholders %1$s - opening <strong> html tag, %2$s closing </strong> html tag */
127
+ 'integration_test_sucessful' => sprintf( esc_html__( '%1$sStatus:%2$s Test Pass.', 'facebook-for-woocommerce' ), '<strong>', '</strong>' ),
128
+ /* translators: Placeholders %1$s - opening <strong> html tag, %2$s closing </strong> html tag */
129
+ 'integration_test_in_progress' => sprintf( esc_html__( '%1$sStatus:%2$s Integration test in progress...', 'facebook-for-woocommerce' ), '<strong>', '</strong>' ),
130
+ /* translators: Placeholders %1$s - opening <strong> html tag, %2$s closing </strong> html tag */
131
+ 'integration_test_failed' => sprintf( esc_html__( '%1$sStatus:%2$s Test Fail.', 'facebook-for-woocommerce' ), '<strong>', '</strong>' ),
132
+ 'general_error' => esc_html__( 'There was an error trying to sync the products to Facebook.', 'facebook-for-woocommerce' ),
133
+ 'feed_upload_error' => esc_html__( 'Something went wrong while uploading the product information, please try again.', 'facebook-for-woocommerce' ),
134
+ ],
135
  ] );
136
  }
137
  }
includes/fbgraph.php CHANGED
@@ -12,6 +12,8 @@ if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
 
 
 
15
  if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
16
 
17
  if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
@@ -37,19 +39,76 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
37
  $this->api_key = $api_key;
38
  }
39
 
 
 
 
 
 
 
 
 
40
  public function _get( $url, $api_key = '' ) {
 
41
  $api_key = $api_key ?: $this->api_key;
42
- return wp_remote_get(
43
- $url,
44
- array(
45
- 'headers' => array(
46
- 'Authorization' => 'Bearer ' . $api_key,
47
- ),
48
- 'timeout' => self::CURL_TIMEOUT,
49
- )
50
- );
 
 
 
 
51
  }
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  public function _post( $url, $data, $api_key = '' ) {
54
  if ( class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
55
  return self::_post_async( $url, $data );
@@ -60,70 +119,175 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
60
 
61
  public function _post_sync( $url, $data, $api_key = '' ) {
62
  $api_key = $api_key ?: $this->api_key;
63
- return wp_remote_post(
64
- $url,
65
- array(
66
- 'body' => $data,
67
- 'headers' => array(
68
- 'Authorization' => 'Bearer ' . $api_key,
69
- ),
70
- 'timeout' => self::CURL_TIMEOUT,
71
- )
72
- );
 
 
 
 
73
  }
74
 
 
 
 
 
 
 
 
 
 
75
  public function _post_async( $url, $data, $api_key = '' ) {
 
76
  if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
77
  return;
78
  }
79
 
80
  $api_key = $api_key ?: $this->api_key;
81
- $fbasync = new WC_Facebookcommerce_Async_Request();
82
 
83
- $fbasync->query_url = $url;
84
- $fbasync->query_args = array();
85
- $fbasync->post_args = array(
86
  'body' => $data,
87
- 'headers' => array(
88
  'Authorization' => 'Bearer ' . $api_key,
89
- ),
90
  'timeout' => self::CURL_TIMEOUT,
91
- );
 
 
92
 
93
- return $fbasync->dispatch();
 
 
 
 
 
 
 
 
94
  }
95
 
 
 
 
 
 
 
 
 
96
  public function _delete( $url, $api_key = '' ) {
 
97
  $api_key = $api_key ?: $this->api_key;
98
 
99
- return wp_remote_request(
100
- $url,
101
- array(
102
- 'headers' => array(
103
- 'Authorization' => 'Bearer ' . $api_key,
104
- ),
105
- 'timeout' => self::CURL_TIMEOUT,
106
- 'method' => 'DELETE',
107
- )
108
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  }
110
 
 
111
  // GET https://graph.facebook.com/vX.X/{page-id}/?fields=name
112
  public function get_page_name( $page_id, $api_key = '' ) {
113
  $api_key = $api_key ?: $this->api_key;
114
  $url = $this->build_url( $page_id, '/?fields=name' );
115
  $response = self::_get( $url, $api_key );
 
116
  if ( is_wp_error( $response ) ) {
117
  WC_Facebookcommerce_Utils::log( $response->get_error_message() );
118
- return;
119
  }
 
120
  if ( $response['response']['code'] != '200' ) {
121
- return;
122
  }
123
 
124
- $response_body = wp_remote_retrieve_body( $response );
125
 
126
- return json_decode( $response_body )->name;
127
  }
128
 
129
 
@@ -157,16 +321,47 @@ if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
157
  }
158
 
159
 
 
 
 
 
 
 
 
 
 
 
160
  public function validate_product_catalog( $product_catalog_id ) {
161
- $url = $this->build_url( $product_catalog_id );
162
- $response = self::_get( $url );
163
- if ( is_wp_error( $response ) ) {
164
- WC_Facebookcommerce_Utils::log( $response->get_error_message() );
165
- return;
166
  }
167
- return $response['response']['code'] == '200';
 
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  // POST https://graph.facebook.com/vX.X/{product-catalog-id}/product_groups
171
  public function create_product_group( $product_catalog_id, $data ) {
172
  $url = $this->build_url( $product_catalog_id, '/product_groups' );
12
  exit;
13
  }
14
 
15
+ use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
16
+
17
  if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) :
18
 
19
  if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
39
  $this->api_key = $api_key;
40
  }
41
 
42
+
43
+ /**
44
+ * Issues a GET request to the Graph API.
45
+ *
46
+ * @param string $url request URL
47
+ * @param string $api_key Graph API key
48
+ * @return array|\WP_Error
49
+ */
50
  public function _get( $url, $api_key = '' ) {
51
+
52
  $api_key = $api_key ?: $this->api_key;
53
+
54
+ $request_args = [
55
+ 'headers' => [
56
+ 'Authorization' => 'Bearer ' . $api_key,
57
+ ],
58
+ 'timeout' => self::CURL_TIMEOUT,
59
+ ];
60
+
61
+ $response = wp_remote_get( $url, $request_args );
62
+
63
+ $this->log_request( $url, $request_args, $response );
64
+
65
+ return $response;
66
  }
67
 
68
+
69
+ /**
70
+ * Performs a Graph API request to the given URL.
71
+ *
72
+ * Throws an exception if a WP_Error is returned or we receive a 401 Not Authorized response status.
73
+ *
74
+ * @since 1.10.2
75
+ *
76
+ * @param string $url
77
+ * @throws Framework\SV_WC_API_Exception
78
+ * @return array
79
+ */
80
+ public function perform_request( $url ) {
81
+
82
+ $request_args = [
83
+ 'headers' => [
84
+ 'Authorization' => 'Bearer ' . $this->api_key,
85
+ ],
86
+ 'timeout' => self::CURL_TIMEOUT,
87
+ ];
88
+
89
+ $response = wp_remote_get( $url, $request_args );
90
+
91
+ $this->log_request( $url, $request_args, $response );
92
+
93
+ if ( is_wp_error( $response ) ) {
94
+
95
+ throw new Framework\SV_WC_API_Exception( $response->get_error_message(), $response->get_error_code() );
96
+
97
+ } elseif ( 401 === (int) wp_remote_retrieve_response_code( $response ) ) {
98
+
99
+ $response_body = json_decode( wp_remote_retrieve_body( $response ) );
100
+
101
+ if ( isset( $response_body->error->code, $response_body->error->message ) ) {
102
+ throw new Framework\SV_WC_API_Exception( $response_body->error->message, $response_body->error->code );
103
+ } else {
104
+ 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 ) ) );
105
+ }
106
+ }
107
+
108
+ return $response;
109
+ }
110
+
111
+
112
  public function _post( $url, $data, $api_key = '' ) {
113
  if ( class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
114
  return self::_post_async( $url, $data );
119
 
120
  public function _post_sync( $url, $data, $api_key = '' ) {
121
  $api_key = $api_key ?: $this->api_key;
122
+
123
+ $request_args = [
124
+ 'body' => $data,
125
+ 'headers' => [
126
+ 'Authorization' => 'Bearer ' . $api_key,
127
+ ],
128
+ 'timeout' => self::CURL_TIMEOUT,
129
+ ];
130
+
131
+ $response = wp_remote_post( $url, $request_args );
132
+
133
+ $this->log_request( $url, $request_args, $response, 'POST' );
134
+
135
+ return $response;
136
  }
137
 
138
+
139
+ /**
140
+ * Issues an asynchronous POST request to the Graph API.
141
+ *
142
+ * @param string $url request URL
143
+ * @param array $data request data
144
+ * @param string $api_key Graph API key
145
+ * @return array|\WP_Error
146
+ */
147
  public function _post_async( $url, $data, $api_key = '' ) {
148
+
149
  if ( ! class_exists( 'WC_Facebookcommerce_Async_Request' ) ) {
150
  return;
151
  }
152
 
153
  $api_key = $api_key ?: $this->api_key;
 
154
 
155
+ $request_args = [
 
 
156
  'body' => $data,
157
+ 'headers' => [
158
  'Authorization' => 'Bearer ' . $api_key,
159
+ ],
160
  'timeout' => self::CURL_TIMEOUT,
161
+ ];
162
+
163
+ $fbasync = new WC_Facebookcommerce_Async_Request();
164
 
165
+ $fbasync->query_url = $url;
166
+ $fbasync->query_args = array();
167
+ $fbasync->post_args = $request_args;
168
+
169
+ $response = $fbasync->dispatch();
170
+
171
+ $this->log_request( $url, $request_args, $response, 'POST' );
172
+
173
+ return $response;
174
  }
175
 
176
+
177
+ /**
178
+ * Issues a DELETE request to the Graph API.
179
+ *
180
+ * @param string $url request URL
181
+ * @param string $api_key Graph API key
182
+ * @return array|\WP_Error
183
+ */
184
  public function _delete( $url, $api_key = '' ) {
185
+
186
  $api_key = $api_key ?: $this->api_key;
187
 
188
+ $request_args = [
189
+ 'headers' => [
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
+
198
+ $this->log_request( $url, $request_args, $response, 'DELETE' );
199
+
200
+ return $response;
201
+ }
202
+
203
+
204
+ /**
205
+ * Logs the request and response data.
206
+ *
207
+ * @since 1.10.2
208
+ *
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
+
216
+ // bail if this class is loaded incorrectly or logging is disabled
217
+ if ( ! function_exists( 'facebook_for_woocommerce' ) || ! facebook_for_woocommerce()->get_integration()->is_debug_mode_enabled() ) {
218
+ return;
219
+ }
220
+
221
+ // add the URI to the data
222
+ $request_data = array_merge( [
223
+ 'uri' => $url,
224
+ ], $request_args );
225
+
226
+ // the request args may not include the method, so allow it to be set
227
+ if ( $method ) {
228
+ $request_data['method'] = $method;
229
+ }
230
+
231
+ // mask the page access token
232
+ if ( ! empty( $request_data['headers']['Authorization'] ) ) {
233
+
234
+ $auth_value = $request_data['headers']['Authorization'];
235
+
236
+ $request_data['headers']['Authorization'] = str_replace( $auth_value, str_repeat( '*', strlen( $auth_value ) ), $auth_value );
237
+ }
238
+
239
+ // if there was a problem
240
+ if ( is_wp_error( $response ) ) {
241
+
242
+ $code = $response->get_error_code();
243
+ $message = $response->get_error_message();
244
+ $headers = [];
245
+ $body = '';
246
+
247
+ } else {
248
+
249
+ $headers = wp_remote_retrieve_headers( $response );
250
+
251
+ if ( is_object( $headers ) ) {
252
+ $headers = $headers->getAll();
253
+ } elseif ( ! is_array( $headers ) ) {
254
+ $headers = [];
255
+ }
256
+
257
+ $code = wp_remote_retrieve_response_code( $response );
258
+ $message = wp_remote_retrieve_response_message( $response );
259
+ $body = wp_remote_retrieve_body( $response );
260
+ }
261
+
262
+ $response_data = [
263
+ 'code' => $code,
264
+ 'message' => $message,
265
+ 'headers' => $headers,
266
+ 'body' => $body,
267
+ ];
268
+
269
+ facebook_for_woocommerce()->log_api_request( $request_data, $response_data );
270
  }
271
 
272
+
273
  // GET https://graph.facebook.com/vX.X/{page-id}/?fields=name
274
  public function get_page_name( $page_id, $api_key = '' ) {
275
  $api_key = $api_key ?: $this->api_key;
276
  $url = $this->build_url( $page_id, '/?fields=name' );
277
  $response = self::_get( $url, $api_key );
278
+
279
  if ( is_wp_error( $response ) ) {
280
  WC_Facebookcommerce_Utils::log( $response->get_error_message() );
281
+ return '';
282
  }
283
+
284
  if ( $response['response']['code'] != '200' ) {
285
+ return '';
286
  }
287
 
288
+ $response_body = json_decode( wp_remote_retrieve_body( $response ) );
289
 
290
+ return isset( $response_body->name ) ? $response_body->name : '';
291
  }
292
 
293
 
321
  }
322
 
323
 
324
+ /**
325
+ * Determines whether the product catalog ID is valid.
326
+ *
327
+ * Returns true if the product catalog ID can be successfully retrieved using the Graph API.
328
+ *
329
+ * TODO: deprecate this methid in 1.11.0 or newer {WV 2020-03-12}
330
+ *
331
+ * @param int $product_catalog_id the ID of the product catalog
332
+ * @return bool
333
+ */
334
  public function validate_product_catalog( $product_catalog_id ) {
335
+
336
+ try {
337
+ $is_valid = $this->is_product_catalog_valid( $product_catalog_id );
338
+ } catch ( Framework\SV_WC_API_Exception $e ) {
339
+ $is_valid = false;
340
  }
341
+
342
+ return $is_valid;
343
  }
344
 
345
+
346
+ /**
347
+ * Determines whether the product catalog ID is valid.
348
+ *
349
+ * Returns true if the product catalog ID can be successfully retrieved using the Graph API.
350
+ *
351
+ * @since 1.10.2
352
+ *
353
+ * @param int $product_catalog_id the ID of the product catalog
354
+ * @return boolean
355
+ * @throws Framework\SV_WC_API_Exception
356
+ */
357
+ public function is_product_catalog_valid( $product_catalog_id ) {
358
+
359
+ $response = $this->perform_request( $this->build_url( $product_catalog_id ) );
360
+
361
+ return 200 === (int) wp_remote_retrieve_response_code( $response );
362
+ }
363
+
364
+
365
  // POST https://graph.facebook.com/vX.X/{product-catalog-id}/product_groups
366
  public function create_product_group( $product_catalog_id, $data ) {
367
  $url = $this->build_url( $product_catalog_id, '/product_groups' );
includes/fbproduct.php CHANGED
@@ -476,11 +476,14 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
476
  }
477
 
478
  /**
479
- * Assemble product payload for POST
480
- **/
481
- function prepare_product(
482
- $retailer_id = null,
483
- $prepare_for_product_feed = false ) {
 
 
 
484
  if ( ! $retailer_id ) {
485
  $retailer_id =
486
  WC_Facebookcommerce_Utils::get_fb_retailer_id( $this );
476
  }
477
 
478
  /**
479
+ * Gets product data to send to Facebook.
480
+ *
481
+ * @param string $retailer_id the retailer ID of the product
482
+ * @param bool $prepare_for_product_feed whether the data is going to be used in a feed upload
483
+ * @return array
484
+ */
485
+ public function prepare_product( $retailer_id = null, $prepare_for_product_feed = false ) {
486
+
487
  if ( ! $retailer_id ) {
488
  $retailer_id =
489
  WC_Facebookcommerce_Utils::get_fb_retailer_id( $this );
includes/fbproductfeed.php CHANGED
@@ -376,22 +376,40 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
376
  );
377
  }
378
 
 
 
 
 
 
 
 
 
 
379
  public function is_upload_complete( &$settings ) {
380
  $result = $this->fbgraph->get_upload_status( $settings['fb_upload_id'] );
381
  if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
382
  $this->log_feed_progress( json_encode( $result ) );
383
  return 'error';
384
  }
385
- $decode_result = WC_Facebookcommerce_Utils::decode_json( $result['body'], true );
386
- $end_time = $decode_result['end_time'];
387
- if ( isset( $end_time ) ) {
388
- $settings['upload_end_time'] = $end_time;
389
- return 'complete';
390
- } else {
391
- return 'in progress';
 
 
 
 
 
 
392
  }
 
 
393
  }
394
 
 
395
  // Log progress in local log file and FB.
396
  public function log_feed_progress( $msg, $object = array() ) {
397
  WC_Facebookcommerce_Utils::fblog( $msg, $object );
376
  );
377
  }
378
 
379
+
380
+ /**
381
+ * Gets the status of the configured feed upload.
382
+ *
383
+ * The status indicator is one of 'in progress', 'complete', or 'error'.
384
+ *
385
+ * @param array $settings
386
+ * @return string
387
+ */
388
  public function is_upload_complete( &$settings ) {
389
  $result = $this->fbgraph->get_upload_status( $settings['fb_upload_id'] );
390
  if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
391
  $this->log_feed_progress( json_encode( $result ) );
392
  return 'error';
393
  }
394
+
395
+ $response_body = json_decode( wp_remote_retrieve_body( $result ) );
396
+ $upload_status = 'error';
397
+
398
+ if ( isset( $response_body->end_time ) ) {
399
+
400
+ $settings['upload_end_time'] = $response_body->end_time;
401
+
402
+ $upload_status = 'complete';
403
+
404
+ } else if ( 200 === (int) wp_remote_retrieve_response_code( $result ) ) {
405
+
406
+ $upload_status = 'in progress';
407
  }
408
+
409
+ return $upload_status;
410
  }
411
 
412
+
413
  // Log progress in local log file and FB.
414
  public function log_feed_progress( $msg, $object = array() ) {
415
  WC_Facebookcommerce_Utils::fblog( $msg, $object );
includes/fbutils.php CHANGED
@@ -345,13 +345,19 @@ if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) :
345
  * Helper log function for debugging
346
  */
347
  public static function log( $message ) {
348
- if ( WP_DEBUG === true ) {
349
- if ( is_array( $message ) || is_object( $message ) ) {
350
- error_log( json_encode( $message ) );
351
- } else {
352
- error_log( sanitize_textarea_field( $message ) );
353
- }
 
 
 
 
354
  }
 
 
355
  }
356
 
357
  // Return store name with sanitized apostrophe
345
  * Helper log function for debugging
346
  */
347
  public static function log( $message ) {
348
+
349
+ // if this file is being included outside the plugin, or the plugin setting is disabled
350
+ if ( ! function_exists( 'facebook_for_woocommerce' ) || ! facebook_for_woocommerce()->get_integration()->is_debug_mode_enabled() ) {
351
+ return;
352
+ }
353
+
354
+ if ( is_array( $message ) || is_object( $message ) ) {
355
+ $message = json_encode( $message );
356
+ } else {
357
+ $message = sanitize_textarea_field( $message );
358
  }
359
+
360
+ facebook_for_woocommerce()->log( $message );
361
  }
362
 
363
  // Return store name with sanitized apostrophe
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.3.2
6
- Stable tag: 1.10.1
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
@@ -39,9 +39,25 @@ When opening a bug on GitHub, please give us as many details as possible.
39
 
40
  == Changelog ==
41
 
42
- = 2020.03.10 - version 1.10.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  * Fix - Prevent Fatal error during the upgrade routine introduced in version 1.10.0
44
  * Fix - Only load the admin settings JavaScript on the Facebook settings page to prevent conflicts with other scripts
 
45
 
46
  = 2020.03.03 - version 1.10.0 =
47
  * Feature - Exclude specific products, variations, product categories, and product tags from syncing to Facebook
@@ -49,8 +65,8 @@ When opening a bug on GitHub, please give us as many details as possible.
49
  * Feature - Revamped settings screen with on-site control over pixel, product sync, and Messenger behavior
50
  * Tweak - Use Action Scheduler for the daily forced re-sync, if enabled
51
  * Fix - Improve pixel tracking accuracy for add-to-cart events
52
- * Misc. - Add the SkyVerge plugin framework as the plugin base
53
- * Misc. - Require WooCommerce 3.5 and above
54
 
55
  = 1.9.15 - 2019-06-27 =
56
  * CSRF handling for Ajax calls like ajax_woo_infobanner_post_click, ajax_woo_infobanner_post_xout, ajax_fb_toggle_visibility
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
  Tested up to: 5.3.2
6
+ Stable tag: 1.10.2
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
39
 
40
  == Changelog ==
41
 
42
+ = 2020.03.17 - version 1.10.2 =
43
+ * Tweak - Add a setting to easily enable debug logging
44
+ * Tweak - Allow third party plugins and themes to track an add-to-cart event on added_to_cart JS event
45
+ * Tweak - When excluding a product term from syncing in the plugin settings page, offer an option to hide excluded synced products from Facebook
46
+ * Tweak - When excluding product terms from syncing in the plugin settings page, and settings are saved, exclude corresponding products from sync
47
+ * Tweak - Improve error messages shown when a problem occurs during products sync
48
+ * Tweak - Log Graph API communication if logging is enabled
49
+ * Fix - When excluding a product term from syncing in the plugin settings page, ensure a modal opens to warn about possible conflicts with already synced products
50
+ * Fix - Messenger settings fields will correctly reflect the values selected during initial setup
51
+ * Fix - Fix a bug that caused newly added gallery images not to be synced immediately after they were added
52
+ * Fix - Fix a bug that prevented gallery images from being removed from products on Facebook
53
+ * Fix - Fix AddToCart Pixel event tracking when adding products from archive with AJAX and redirect to cart enabled
54
+ * Fix - Fix undefined index and undefined property notices.
55
+ * Dev - Make Pixel script attributes and event parameters filterable
56
+
57
+ = 2020.03.10 - version 1.10.1 =
58
  * Fix - Prevent Fatal error during the upgrade routine introduced in version 1.10.0
59
  * Fix - Only load the admin settings JavaScript on the Facebook settings page to prevent conflicts with other scripts
60
+ * Misc - Add support for WooCommerce 4.0
61
 
62
  = 2020.03.03 - version 1.10.0 =
63
  * Feature - Exclude specific products, variations, product categories, and product tags from syncing to Facebook
65
  * Feature - Revamped settings screen with on-site control over pixel, product sync, and Messenger behavior
66
  * Tweak - Use Action Scheduler for the daily forced re-sync, if enabled
67
  * Fix - Improve pixel tracking accuracy for add-to-cart events
68
+ * Misc - Add the SkyVerge plugin framework as the plugin base
69
+ * Misc - Require WooCommerce 3.5 and above
70
 
71
  = 1.9.15 - 2019-06-27 =
72
  * CSRF handling for Ajax calls like ajax_woo_infobanner_post_click, ajax_woo_infobanner_post_xout, ajax_fb_toggle_visibility