Facebook for WooCommerce - Version 1.11.0

Version Description

Download this release

Release Info

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

Code changes from version 1.10.2 to 1.11.0

assets/css/admin/facebook-for-woocommerce-products-admin.css CHANGED
@@ -35,3 +35,9 @@
35
  .woocommerce_variation .fb-product-image-source-field .wc-radios {
36
  clear: both;
37
  }
 
 
 
 
 
 
35
  .woocommerce_variation .fb-product-image-source-field .wc-radios {
36
  clear: both;
37
  }
38
+
39
+ /* adjusts widths of the products table, same as WooCommerce categories and tags */
40
+ #facebook_sync_enabled,
41
+ #facebook_catalog_visibility {
42
+ width: 11% !important;
43
+ }
assets/js/admin/facebook-for-woocommerce-products-admin.js CHANGED
@@ -69,7 +69,8 @@ jQuery( document ).ready( function( $ ) {
69
  let $submitButton = $( this ),
70
  chosenBulkAction = $submitButton.prev( 'select' ).val();
71
 
72
- if ( 'facebook_exclude' === chosenBulkAction || 'facebook_include' === chosenBulkAction ) {
 
73
 
74
  let products = [];
75
 
@@ -195,26 +196,34 @@ jQuery( document ).ready( function( $ ) {
195
  return true;
196
  }
197
 
198
- let $submitButton = $( this ),
199
  $visibleCheckbox = $( 'input[name="fb_visibility"]' ),
200
- productID = parseInt( $( 'input#post_ID' ).val(), 10 ),
201
- productCat = [],
202
- productTag = $( 'textarea[name="tax_input[product_tag]"]' ).val().split( ',' ),
203
- syncEnabled = $( 'input#fb_sync_enabled' ).prop( 'checked' );
 
 
204
 
205
  $( '#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked' ).each( function() {
206
  productCat.push( parseInt( $( this ).val(), 10 ) );
207
  } );
208
 
 
 
 
 
 
209
  if ( productID > 0 ) {
210
 
211
  $.post( facebook_for_woocommerce_products_admin.ajax_url, {
212
- action: 'facebook_for_woocommerce_set_product_sync_prompt',
213
- security: facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,
214
- sync_enabled: syncEnabled ? 'enabled' : 'disabled',
215
- product: productID,
216
- categories: productCat,
217
- tags: productTag
 
218
  }, function( response ) {
219
 
220
  // open modal if visibility checkbox is checked or if there are conflicting terms set for sync exclusion
@@ -261,4 +270,24 @@ jQuery( document ).ready( function( $ ) {
261
  } );
262
  }
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  } );
69
  let $submitButton = $( this ),
70
  chosenBulkAction = $submitButton.prev( 'select' ).val();
71
 
72
+ // TODO: also check `'facebook_exclude' === chosenBulkAction` once Catalog Visibility settings are available again {WV 2020-04-20}
73
+ if ( 'facebook_include' === chosenBulkAction ) {
74
 
75
  let products = [];
76
 
196
  return true;
197
  }
198
 
199
+ let $submitButton = $( this ),
200
  $visibleCheckbox = $( 'input[name="fb_visibility"]' ),
201
+ productID = parseInt( $( 'input#post_ID' ).val(), 10 ),
202
+ productCat = [],
203
+ // this query will get tags when not using checkboxes
204
+ productTag = $( 'textarea[name="tax_input[product_tag]"]' ).length ? $( 'textarea[name="tax_input[product_tag]"]' ).val().split( ',' ) : [],
205
+ syncEnabled = $( 'input#fb_sync_enabled' ).prop( 'checked' ),
206
+ varSyncEnabled = $( '.js-variable-fb-sync-toggle' ).is( ':checked' );
207
 
208
  $( '#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked' ).each( function() {
209
  productCat.push( parseInt( $( this ).val(), 10 ) );
210
  } );
211
 
212
+ // this query will get tags when using checkboxes
213
+ $( '#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked' ).each( function() {
214
+ productTag.push( parseInt( $( this ).val(), 10 ) );
215
+ } );
216
+
217
  if ( productID > 0 ) {
218
 
219
  $.post( facebook_for_woocommerce_products_admin.ajax_url, {
220
+ action: 'facebook_for_woocommerce_set_product_sync_prompt',
221
+ security: facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,
222
+ sync_enabled: syncEnabled ? 'enabled' : 'disabled',
223
+ var_sync_enabled: varSyncEnabled ? 'enabled' : 'disabled',
224
+ product: productID,
225
+ categories: productCat,
226
+ tags: productTag
227
  }, function( response ) {
228
 
229
  // open modal if visibility checkbox is checked or if there are conflicting terms set for sync exclusion
270
  } );
271
  }
272
 
273
+
274
+ // product list screen or individual product edit screen
275
+ if ( 'product' === pagenow || 'edit-product' === pagenow ) {
276
+
277
+ // handle the "Do not show this notice again" button
278
+ $( '.js-wc-plugin-framework-admin-notice' ).on( 'click', '.notice-dismiss-permanently', function() {
279
+
280
+ var $notice = $( this ).closest( '.js-wc-plugin-framework-admin-notice' );
281
+
282
+ $.get( ajaxurl, {
283
+ action: 'wc_plugin_framework_' + $( $notice ).data( 'plugin-id' ) + '_dismiss_notice',
284
+ messageid: $( $notice ).data( 'message-id' ),
285
+ permanently: true
286
+ } );
287
+
288
+ $notice.fadeOut();
289
+ } );
290
+ }
291
+
292
+
293
  } );
assets/js/admin/facebook-for-woocommerce-products-admin.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";jQuery(document).ready(function(n){var o=window.pagenow.length?window.pagenow:"";window.typenow.length&&window.typenow;if("edit-product"===o){var e=n(".facebook-for-woocommerce-product-visibility-toggle");e.tipTip({attribute:"title",edgeOffset:5,fadeIn:50,fadeOut:50,delay:200}),e.on("click",function(o){o.preventDefault();var e=n(this).data("action"),c="show"===e?"yes":"no",t=parseInt(n(this).data("product-id"),10);"show"===e?n(this).hide().next("button").show():"hide"===e&&n(this).hide().prev("button").show(),n.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_products_visibility",security:facebook_for_woocommerce_products_admin.set_product_visibility_nonce,products:[{product_id:t,visibility:c}]})});var i=!1;n("input#doaction, input#doaction2").on("click",function(o){if(i)return!0;o.preventDefault();var e=n(this),c=e.prev("select").val();if("facebook_exclude"===c||"facebook_include"===c){var t=[];n.each(n('input[name="post[]"]:checked'),function(){t.push(parseInt(n(this).val(),10))}),n.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:c,products:t},function(o){o&&!o.success?(n("#wc-backbone-modal-dialog .modal-close").trigger("click"),new n.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),n(".facebook-for-woocommerce-toggle-product-visibility").on("click",function(o){blockModal(),n(this).hasClass("hide-products")&&n.each(t,function(){var o=n("#post-"+this).find("td.facebook_catalog_visibility button.facebook-for-woocommerce-product-visibility-hide");o.is(":visible")&&o.trigger("click")}),i=!0,e.trigger("click")})):(i=!0,e.trigger("click"))})}else i=!0,e.trigger("click")})}if("product"===o){var c=function(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)},t=n("#fb_sync_enabled"),r=t.closest(".woocommerce_options_panel");t.on("click",function(){c(n(this).prop("checked"),r)}),c(t.prop("checked"),r),n(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",function(){c(n(this).prop("checked"),n(this).closest(".wc-metabox-content"))}),n("#woocommerce-product-data").on("change",".js-fb-product-image-source",function(){var o=n(this).closest(".woocommerce_options_panel, .wc-metabox-content"),e=n(this).val();o.find(".product-image-source-field").closest(".form-field").hide(),o.find(".show-if-product-image-source-"+e).closest(".form-field").show()}),n(".js-fb-product-image-source:checked").trigger("change"),n("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){n(".js-variable-fb-sync-toggle").trigger("change"),n(".js-fb-product-image-source:checked").trigger("change")});var s=!1;n('form#post input[type="submit"]').on("click",function(o){if(s)return!0;o.preventDefault();var e=n(this),c=n('input[name="fb_visibility"]'),t=parseInt(n("input#post_ID").val(),10),i=[],r=n('textarea[name="tax_input[product_tag]"]').val().split(","),a=n("input#fb_sync_enabled").prop("checked");n('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each(function(){i.push(parseInt(n(this).val(),10))}),0<t?n.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:a?"enabled":"disabled",product:t,categories:i,tags:r},function(o){o&&!o.success&&(a||!a&&c.length&&c.is(":checked"))?(n("#wc-backbone-modal-dialog .modal-close").trigger("click"),new n.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),n(".facebook-for-woocommerce-toggle-product-visibility").on("click",function(o){blockModal(),n(this).hasClass("hide-products")&&c.prop("checked",!1),s=!0,e.trigger("click")})):(s=!0,e.trigger("click"))}):(s=!0,e.trigger("click"))})}});
1
+ "use strict";jQuery(document).ready(function(s){var o=window.pagenow.length?window.pagenow:"";window.typenow.length&&window.typenow;if("edit-product"===o){var e=s(".facebook-for-woocommerce-product-visibility-toggle");e.tipTip({attribute:"title",edgeOffset:5,fadeIn:50,fadeOut:50,delay:200}),e.on("click",function(o){o.preventDefault();var e=s(this).data("action"),c="show"===e?"yes":"no",t=parseInt(s(this).data("product-id"),10);"show"===e?s(this).hide().next("button").show():"hide"===e&&s(this).hide().prev("button").show(),s.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_products_visibility",security:facebook_for_woocommerce_products_admin.set_product_visibility_nonce,products:[{product_id:t,visibility:c}]})});var i=!1;s("input#doaction, input#doaction2").on("click",function(o){if(i)return!0;o.preventDefault();var e=s(this),c=e.prev("select").val();if("facebook_include"===c){var t=[];s.each(s('input[name="post[]"]:checked'),function(){t.push(parseInt(s(this).val(),10))}),s.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_bulk_action_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_bulk_action_prompt_nonce,toggle:c,products:t},function(o){o&&!o.success?(s("#wc-backbone-modal-dialog .modal-close").trigger("click"),new s.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),s(".facebook-for-woocommerce-toggle-product-visibility").on("click",function(o){blockModal(),s(this).hasClass("hide-products")&&s.each(t,function(){var o=s("#post-"+this).find("td.facebook_catalog_visibility button.facebook-for-woocommerce-product-visibility-hide");o.is(":visible")&&o.trigger("click")}),i=!0,e.trigger("click")})):(i=!0,e.trigger("click"))})}else i=!0,e.trigger("click")})}if("product"===o){var c=function(o,e){e.find(".enable-if-sync-enabled").prop("disabled",!o)},t=s("#fb_sync_enabled"),a=t.closest(".woocommerce_options_panel");t.on("click",function(){c(s(this).prop("checked"),a)}),c(t.prop("checked"),a),s(".woocommerce_variations").on("change",".js-variable-fb-sync-toggle",function(){c(s(this).prop("checked"),s(this).closest(".wc-metabox-content"))}),s("#woocommerce-product-data").on("change",".js-fb-product-image-source",function(){var o=s(this).closest(".woocommerce_options_panel, .wc-metabox-content"),e=s(this).val();o.find(".product-image-source-field").closest(".form-field").hide(),o.find(".show-if-product-image-source-"+e).closest(".form-field").show()}),s(".js-fb-product-image-source:checked").trigger("change"),s("#woocommerce-product-data").on("woocommerce_variations_loaded",function(){s(".js-variable-fb-sync-toggle").trigger("change"),s(".js-fb-product-image-source:checked").trigger("change")});var d=!1;s('form#post input[type="submit"]').on("click",function(o){if(d)return!0;o.preventDefault();var e=s(this),c=s('input[name="fb_visibility"]'),t=parseInt(s("input#post_ID").val(),10),i=[],a=s('textarea[name="tax_input[product_tag]"]').length?s('textarea[name="tax_input[product_tag]"]').val().split(","):[],n=s("input#fb_sync_enabled").prop("checked"),r=s(".js-variable-fb-sync-toggle").is(":checked");s('#taxonomy-product_cat input[name="tax_input[product_cat][]"]:checked').each(function(){i.push(parseInt(s(this).val(),10))}),s('#taxonomy-product_tag input[name="tax_input[product_tag][]"]:checked').each(function(){a.push(parseInt(s(this).val(),10))}),0<t?s.post(facebook_for_woocommerce_products_admin.ajax_url,{action:"facebook_for_woocommerce_set_product_sync_prompt",security:facebook_for_woocommerce_products_admin.set_product_sync_prompt_nonce,sync_enabled:n?"enabled":"disabled",var_sync_enabled:r?"enabled":"disabled",product:t,categories:i,tags:a},function(o){o&&!o.success&&(n||!n&&c.length&&c.is(":checked"))?(s("#wc-backbone-modal-dialog .modal-close").trigger("click"),new s.WCBackboneModal.View({target:"facebook-for-woocommerce-modal",string:o.data}),s(".facebook-for-woocommerce-toggle-product-visibility").on("click",function(o){blockModal(),s(this).hasClass("hide-products")&&c.prop("checked",!1),d=!0,e.trigger("click")})):(d=!0,e.trigger("click"))}):(d=!0,e.trigger("click"))})}"product"!==o&&"edit-product"!==o||s(".js-wc-plugin-framework-admin-notice").on("click",".notice-dismiss-permanently",function(){var o=s(this).closest(".js-wc-plugin-framework-admin-notice");s.get(ajaxurl,{action:"wc_plugin_framework_"+s(o).data("plugin-id")+"_dismiss_notice",messageid:s(o).data("message-id"),permanently:!0}),o.fadeOut()})});
assets/js/facebook-for-woocommerce-modal.js CHANGED
@@ -7,38 +7,44 @@
7
  * @package FacebookCommerce
8
  */
9
 
10
- $ = jQuery;
11
 
12
- /**
13
- * Determines if the current modal is blocked.
14
- *
15
- * @returns {boolean}
16
- */
17
- function isModalBlocked() {
18
- let $modal = $( '.wc-backbone-modal-content' );
19
- return $modal.is( '.processing') || $modal.parents( '.processing' ).length;
20
- }
21
 
 
22
 
23
- /**
24
- * Blocks the current modal.
25
- */
26
- function blockModal() {
27
- if ( ! isModalBlocked() ) {
28
- return $( '.wc-backbone-modal-content' ).addClass( 'processing' ).block( {
29
- message: null,
30
- overlayCSS: {
31
- background: '#fff',
32
- opacity: 0.6
33
- }
34
- } );
35
  }
36
- }
37
 
38
 
39
- /**
40
- * Unblocks the current modal.
41
- */
42
- function unBlockModal() {
43
- $( '.wc-backbone-modal-content' ).removeClass( 'processing' ).unblock();
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  * @package FacebookCommerce
8
  */
9
 
10
+ (function( $ ) {
11
 
12
+ /**
13
+ * Determines if the current modal is blocked.
14
+ *
15
+ * @returns {boolean}
16
+ */
17
+ window.isModalBlocked = function() {
 
 
 
18
 
19
+ let $modal = $( '.wc-backbone-modal-content' );
20
 
21
+ return $modal.is( '.processing' ) || $modal.parents( '.processing' ).length;
 
 
 
 
 
 
 
 
 
 
 
22
  }
 
23
 
24
 
25
+ /**
26
+ * Blocks the current modal.
27
+ */
28
+ window.blockModal = function() {
29
+
30
+ if ( ! isModalBlocked() ) {
31
+ return $( '.wc-backbone-modal-content' ).addClass( 'processing' ).block( {
32
+ message: null,
33
+ overlayCSS: {
34
+ background: '#fff',
35
+ opacity: 0.6
36
+ }
37
+ } );
38
+ }
39
+ }
40
+
41
+
42
+ /**
43
+ * Unblocks the current modal.
44
+ */
45
+ window.unBlockModal = function() {
46
+
47
+ $( '.wc-backbone-modal-content' ).removeClass( 'processing' ).unblock();
48
+ }
49
+
50
+ })( jQuery );
assets/js/facebook-for-woocommerce-modal.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";function isModalBlocked(){var o=$(".wc-backbone-modal-content");return o.is(".processing")||o.parents(".processing").length}function blockModal(){if(!isModalBlocked())return $(".wc-backbone-modal-content").addClass("processing").block({message:null,overlayCSS:{background:"#fff",opacity:.6}})}function unBlockModal(){$(".wc-backbone-modal-content").removeClass("processing").unblock()}$=jQuery;
1
+ "use strict";!function(n){window.isModalBlocked=function(){var o=n(".wc-backbone-modal-content");return o.is(".processing")||o.parents(".processing").length},window.blockModal=function(){if(!isModalBlocked())return n(".wc-backbone-modal-content").addClass("processing").block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},window.unBlockModal=function(){n(".wc-backbone-modal-content").removeClass("processing").unblock()}}(jQuery);
assets/js/facebook-settings.js CHANGED
@@ -166,8 +166,10 @@ function sync_confirm(verbose = null) {
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" ); };
173
  } else if (window.location.href.includes( "fb_test_product_sync" )) {
@@ -175,6 +177,7 @@ if (window.location.href.includes( "fb_force_resync" )) {
175
  window.is_test = true;
176
  window.onload = function() { sync_confirm( "fb_test_product_sync" ); };
177
  }
 
178
 
179
 
180
  /**
@@ -241,7 +244,7 @@ function sync_all_products($using_feed = false, $is_test = false) {
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
  }
@@ -266,8 +269,8 @@ function delete_all_settings(callback = null, failcallback = null) {
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
 
@@ -322,11 +325,11 @@ function save_settings(callback = null, failcallback = null, localsettings = nul
322
  function save_settings_for_plugin(callback, failcallback) {
323
  save_settings(
324
  function(response){
325
- if (response && response.includes( 'settings_saved' )) {
326
  console.log( response );
327
  callback( response );
328
  } else {
329
- console.log( 'Fail response on save_settings_and_sync' );
330
  failcallback( response );
331
  }
332
  },
@@ -348,7 +351,7 @@ function save_settings_and_sync(message) {
348
  window.sendToFacebook( 'ack set pixel', message.params );
349
  window.sendToFacebook( 'ack set page access token', message.params );
350
  window.sendToFacebook( 'ack set merchant settings', message.params );
351
- sync_all_products( true );
352
  } else {
353
  window.sendToFacebook( 'fail save_settings', response );
354
  console.log( 'Fail response on save_settings_and_sync' );
@@ -460,7 +463,7 @@ function setPixel(message) {
460
  // We need this to support changing the pixel id after setup.
461
  save_settings(
462
  function(response){
463
- if (response && response.includes( 'settings_saved' )) {
464
  window.sendToFacebook( 'ack set pixel', message.params );
465
  } //may not get settings_saved if we try to save pixel before an API key
466
  },
@@ -472,8 +475,17 @@ function setPixel(message) {
472
  );
473
  }
474
 
475
- function genFeed(message) {
476
- // no-op
 
 
 
 
 
 
 
 
 
477
  }
478
 
479
  function setAccessTokenAndPageId(message) {
@@ -549,12 +561,51 @@ function setMsgerChatSetup( data ) {
549
  }
550
  }
551
 
552
- function iFrameListener(event) {
553
- // Fix for web.facebook.com
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  const origin = event.origin || event.originalEvent.origin;
555
- if (origin != window.facebookAdsToolboxConfig.popupOrigin &&
556
- urlFromSameDomain( origin, window.facebookAdsToolboxConfig.popupOrigin )) {
557
- window.facebookAdsToolboxConfig.popupOrigin = origin;
 
 
 
 
 
 
 
 
 
 
558
  }
559
 
560
  switch (event.data.type) {
@@ -580,28 +631,74 @@ function iFrameListener(event) {
580
  case 'get dia settings':
581
  window.sendToFacebook( 'dia settings', window.diaConfig );
582
  break;
583
- case 'set merchant settings':
584
- setMerchantSettings( event.data );
585
- break;
586
  case 'set catalog':
587
  setCatalog( event.data );
588
  break;
 
 
 
589
  case 'set pixel':
590
  setPixel( event.data );
591
  break;
592
  case 'gen feed':
593
- genFeed();
594
  break;
595
 
 
596
  case 'set page access token':
597
- // should be last message received
598
- setAccessTokenAndPageId( event.data );
599
- save_settings_and_sync( event.data );
600
-
601
- // hide Facebook fancy box and show integration settings
602
- jQuery( '#fbsetup' ).hide();
603
- jQuery( '#integration-settings' ).show();
604
- jQuery( '.woocommerce-save-button' ).show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  break;
606
 
607
  case 'set msger chat':
@@ -611,7 +708,7 @@ function iFrameListener(event) {
611
  window.sendToFacebook( 'ack msger chat', event.data );
612
  },
613
  function(response) {
614
- window.sendToFacebook( 'fail ack msger chat', event.data );
615
  }
616
  );
617
  break;
@@ -620,6 +717,31 @@ function iFrameListener(event) {
620
 
621
  addAnEventListener( window,'message',iFrameListener );
622
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
623
  function urlFromSameDomain(url1, url2) {
624
  if ( ! url1.startsWith( 'http' ) || ! url2.startsWith( 'http' )) {
625
  return false;
@@ -804,7 +926,7 @@ function check_feed_upload_queue(check_num) {
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) {
@@ -957,11 +1079,15 @@ function syncShortDescription() {
957
  );
958
  }
959
 
960
-
961
  jQuery( document ).ready( function( $ ) {
962
 
963
- // check background processor status in case products are being synced in the background when the page loads
964
- check_background_processor_status();
 
 
 
 
 
965
 
966
  $( '#woocommerce-facebook-settings-sync-products' ).click( function( event ) {
967
 
@@ -969,4 +1095,5 @@ jQuery( document ).ready( function( $ ) {
969
 
970
  sync_confirm();
971
  } );
 
972
  } );
166
  }
167
  }
168
 
169
+ /*
170
+ * Launch the confirm dialog immediately if the param is in the URL.
171
+ * TODO: restore after migration to FBE 2.0
172
+ *
173
  if (window.location.href.includes( "fb_force_resync" )) {
174
  window.onload = function() { sync_confirm( "fb_force_resync" ); };
175
  } else if (window.location.href.includes( "fb_test_product_sync" )) {
177
  window.is_test = true;
178
  window.onload = function() { sync_confirm( "fb_test_product_sync" ); };
179
  }
180
+ */
181
 
182
 
183
  /**
244
  message = facebook_for_woocommerce_settings_sync.i18n.general_error;
245
  }
246
 
247
+ jQuery( '#sync_progress' ).show().html( '<span style="color: #DC3232">' + message + '</span>' );
248
  }
249
  } );
250
  }
269
 
270
  jQuery( '.messenger-field' ).each( function () {
271
 
272
+ if ( typeof jQuery( this ).data( 'default' ) !== 'undefined' ) {
273
+ jQuery( this ).val( jQuery( this ).data( 'default' ) ).trigger( 'change' );
274
  }
275
  } );
276
 
325
  function save_settings_for_plugin(callback, failcallback) {
326
  save_settings(
327
  function(response){
328
+ if (response && true === response.success ) {
329
  console.log( response );
330
  callback( response );
331
  } else {
332
+ console.log( 'Fail response on save_settings_for_plugin' );
333
  failcallback( response );
334
  }
335
  },
351
  window.sendToFacebook( 'ack set pixel', message.params );
352
  window.sendToFacebook( 'ack set page access token', message.params );
353
  window.sendToFacebook( 'ack set merchant settings', message.params );
354
+ // sync_all_products( true ); TODO: reinstate when switching back to FBE 2
355
  } else {
356
  window.sendToFacebook( 'fail save_settings', response );
357
  console.log( 'Fail response on save_settings_and_sync' );
463
  // We need this to support changing the pixel id after setup.
464
  save_settings(
465
  function(response){
466
+ if (response && true === response.success ) {
467
  window.sendToFacebook( 'ack set pixel', message.params );
468
  } //may not get settings_saved if we try to save pixel before an API key
469
  },
475
  );
476
  }
477
 
478
+ function genFeed( message ) {
479
+
480
+ console.log( 'generating feed' );
481
+
482
+ jQuery.get( window.facebookAdsToolboxConfig.feedPrepared.feedUrl + '&regenerate=true' )
483
+ .done( function( json ) {
484
+ window.sendToFacebook( 'ack feed', message.params );
485
+ } )
486
+ .fail( function( xhr, ajaxOptions, thronwError ) {
487
+ window.sendToFacebook( 'fail feed', message.params );
488
+ } );
489
  }
490
 
491
  function setAccessTokenAndPageId(message) {
561
  }
562
  }
563
 
564
+ function setFeedMigrated(message) {
565
+
566
+ if ( ! message.params.hasOwnProperty( 'feed_migrated' ) ) {
567
+
568
+ console.error(
569
+ 'Facebook Extension Error: feed migrated not received',
570
+ message.params
571
+ );
572
+
573
+ window.sendToFacebook( 'fail set feed migrated', message.params );
574
+ return;
575
+ }
576
+
577
+ settings.feed_migrated = message.params.feed_migrated;
578
+ window.facebookAdsToolboxConfig.feedPrepared.feedMigrated = message.params.feed_migrated;
579
+
580
+ jQuery( '#woocommerce-facebook-settings-sync-products' ).hide();
581
+ jQuery( '.notice.wc-facebook-migrate-notice' ).hide();
582
+
583
+ save_settings_for_plugin(
584
+ function( response ) {
585
+ window.sendToFacebook( 'ack set feed migrated', event.data );
586
+ },
587
+ function( response ) {
588
+ window.sendToFacebook( 'fail set feed migrated', event.data );
589
+ }
590
+ );
591
+ }
592
+
593
+ function iFrameListener( event ) {
594
+
595
  const origin = event.origin || event.originalEvent.origin;
596
+
597
+ if ( origin !== window.facebookAdsToolboxConfig.popupOrigin ) {
598
+
599
+ // Fix for web.facebook.com
600
+ if ( urlFromSameDomain( origin, window.facebookAdsToolboxConfig.popupOrigin ) ) {
601
+
602
+ window.facebookAdsToolboxConfig.popupOrigin = origin;
603
+
604
+ // otherwise bail if not a message from facebook.com
605
+ } else {
606
+
607
+ return;
608
+ }
609
  }
610
 
611
  switch (event.data.type) {
631
  case 'get dia settings':
632
  window.sendToFacebook( 'dia settings', window.diaConfig );
633
  break;
 
 
 
634
  case 'set catalog':
635
  setCatalog( event.data );
636
  break;
637
+ case 'set feed migrated':
638
+ setFeedMigrated( event.data );
639
+ break;
640
  case 'set pixel':
641
  setPixel( event.data );
642
  break;
643
  case 'gen feed':
644
+ genFeed( event.data );
645
  break;
646
 
647
+ // simulate this success response so FBE considers setup complete
648
  case 'set page access token':
649
+ window.sendToFacebook( 'ack set page access token', event.data.params );
650
+ break;
651
+
652
+ case 'set page':
653
+ setPage( event.data );
654
+ break;
655
+
656
+ case 'set merchant settings':
657
+
658
+ setMerchantSettings( event.data );
659
+
660
+ // this should be the final message sent, so save the settings at this point
661
+ save_settings(
662
+ function( response ) {
663
+
664
+ console.log( response );
665
+
666
+ if ( response && true === response.success ) {
667
+
668
+ // final acks
669
+ if ( settings.pixel_id ) {
670
+ window.sendToFacebook( 'ack set pixel', event.data.params );
671
+ }
672
+
673
+ if ( settings.page_id ) {
674
+ window.sendToFacebook( 'ack set page', event.data.params );
675
+ }
676
+
677
+ if ( settings.external_merchant_settings_id ) {
678
+
679
+ window.sendToFacebook( 'ack set merchant settings', event.data.params );
680
+
681
+ // hide Facebook fancy box and show integration settings
682
+ jQuery( '#fbsetup' ).hide();
683
+ jQuery( '#integration-settings' ).show();
684
+ jQuery( '.woocommerce-save-button' ).show();
685
+ }
686
+
687
+ } else {
688
+
689
+ window.sendToFacebook( 'fail save_settings', response );
690
+
691
+ console.log( 'Fail response on save_settings' );
692
+ }
693
+ },
694
+ function( errorResponse ){
695
+
696
+ console.log( 'Ajax error while saving settings:' + JSON.stringify( errorResponse ) );
697
+
698
+ window.sendToFacebook( 'fail save_settings_ajax', JSON.stringify( errorResponse ) );
699
+ }
700
+ );
701
+
702
  break;
703
 
704
  case 'set msger chat':
708
  window.sendToFacebook( 'ack msger chat', event.data );
709
  },
710
  function(response) {
711
+ window.sendToFacebook( 'fail msger chat', event.data );
712
  }
713
  );
714
  break;
717
 
718
  addAnEventListener( window,'message',iFrameListener );
719
 
720
+ /**
721
+ * Sets the page parameters received from FBE.
722
+ *
723
+ * @since 1.11.0
724
+ *
725
+ * @param {Object} message
726
+ */
727
+ function setPage( message ) {
728
+
729
+ if ( ! message.params.hasOwnProperty( 'page_id' ) ) {
730
+
731
+ console.error(
732
+ 'Facebook Extension Error: page ID not received',
733
+ message.params
734
+ );
735
+
736
+ window.sendToFacebook( 'fail set page', message.params );
737
+ return;
738
+ }
739
+
740
+ settings.page_id = message.params.page_id;
741
+
742
+ jQuery( '#woocommerce_facebookcommerce_facebook_page_id' ).val( settings.page_id );
743
+ }
744
+
745
  function urlFromSameDomain(url1, url2) {
746
  if ( ! url1.startsWith( 'http' ) || ! url2.startsWith( 'http' )) {
747
  return false;
926
  // enable Manage connection and Sync products buttons when sync stops
927
  jQuery( '#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products' ).css( 'pointer-events', 'auto' );
928
 
929
+ jQuery( '#sync_progress' ).show().html( '<span style="color: #DC3232">' + facebook_for_woocommerce_settings_sync.i18n.feed_upload_error + '</span>' );
930
 
931
  window.feed_upload = false;
932
  if (window.is_test) {
1079
  );
1080
  }
1081
 
 
1082
  jQuery( document ).ready( function( $ ) {
1083
 
1084
+ // when a "manage connection" link is click from a notice
1085
+ $( '.notice .wc-facebook-manage-connection' ).click( function( event ) {
1086
+
1087
+ event.preventDefault();
1088
+
1089
+ facebookConfig();
1090
+ } );
1091
 
1092
  $( '#woocommerce-facebook-settings-sync-products' ).click( function( event ) {
1093
 
1095
 
1096
  sync_confirm();
1097
  } );
1098
+
1099
  } );
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 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()})});
1
+ "use strict";var fb_sync_no_response_count=0,fb_show_advanced_options=!1;function openPopup(){var e,o=screen.height/2-404,n=screen.width/2-576.5;window.originParam=window.location.protocol+"//"+window.location.host,window.facebookAdsToolboxConfig.popupOrigin.includes("staticxx")&&(window.facebookAdsToolboxConfig.popupOrigin="https://www.facebook.com/"),window.facebookAdsToolboxConfig.popupOrigin=prepend_protocol(window.facebookAdsToolboxConfig.popupOrigin),e=window.facebookAdsToolboxConfig.popupOrigin;var t=window.open(e+"/login.php?display=popup&next="+encodeURIComponent(e+"/ads/dia?origin="+window.originParam+" &merchant_settings_id="+window.facebookAdsToolboxConfig.diaSettingId),"DiaWizard",["toolbar=no","location=no","directories=no","status=no","menubar=no","scrollbars=no","resizable=no","copyhistory=no","width=1153","height=808","top="+o,"left="+n].join(","));return function(e,o){t.postMessage({type:e,params:o},window.facebookAdsToolboxConfig.popupOrigin)}}function prepend_protocol(e){return 0===e.indexOf("//www.")&&(e="https:"+e),e}function get_pixel_id_box(){return document.querySelector("#woocommerce_facebookcommerce_facebook_pixel_id")}function get_pixel_use_pii_id_box(){return document.querySelector("#woocommerce_facebookcommerce_enable_advanced_matching")}function get_page_id_box(){return document.querySelector("#woocommerce_facebookcommerce_facebook_page_id")}function ajax(e){var o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null,t=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,s=Object.assign({},{action:e},o);jQuery.post(ajaxurl,s,function(e){n&&n(e)}).fail(function(e){t&&t(e)})}var settings={facebook_for_woocommerce:1},pixel_settings={facebook_for_woocommerce:1};function facebookConfig(){window.sendToFacebook=openPopup(),window.diaConfig={clientSetup:window.facebookAdsToolboxConfig}}function fb_flush(){return console.log("Removing all FBIDs from all products!"),ajax("ajax_reset_all_fb_products",{_ajax_nonce:wc_facebook_settings_jsx.nonce},null,function(){console.log("Failed to reset all FB products")})}function sync_confirm(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,o="";switch(e){case"fb_force_resync":o=facebook_for_woocommerce_settings_sync.i18n.confirm_resync;break;case"fb_test_product_sync":o=facebook_for_woocommerce_settings_sync.i18n.confirm_sync_test;break;default:o=facebook_for_woocommerce_settings_sync.i18n.confirm_sync}confirm(o)&&(sync_all_products(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload,"fb_test_product_sync"==e),window.fb_sync_start_time=(new Date).getTime())}function sync_all_products(){var n=0<arguments.length&&void 0!==arguments[0]&&arguments[0],e=1<arguments.length&&void 0!==arguments[1]&&arguments[1];window.fb_connected=!0,sync_in_progress();var o={};o=n?(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload=!0,window.feed_upload=!0,ping_feed_status_queue(),e?{action:"ajax_test_sync_products_using_feed"}:{action:"ajax_sync_all_fb_products_using_feed",_ajax_nonce:wc_facebook_settings_jsx.nonce}):(check_background_processor_status(),{action:"ajax_sync_all_fb_products",_ajax_nonce:wc_facebook_settings_jsx.nonce}),jQuery.post(ajaxurl,o).then(function(e){if(!e&&n||e&&!1===e.success){clearInterval(window.fb_pings),clearInterval(window.fb_feed_pings),jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto");var o=void 0;o=e&&e.data&&e.data.error?e.data.error:facebook_for_woocommerce_settings_sync.i18n.general_error,jQuery("#sync_progress").show().html('<span style="color: #DC3232">'+o+"</span>")}})}function delete_all_settings(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,o=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null;return get_pixel_id_box()&&(get_pixel_id_box().value=""),get_pixel_use_pii_id_box()&&(get_pixel_use_pii_id_box().checked=!1),get_page_id_box()&&(get_page_id_box().value=""),jQuery("#woocommerce_facebookcommerce_enable_messenger").prop("checked",!1).trigger("change"),jQuery(".messenger-field").each(function(){void 0!==jQuery(this).data("default")&&jQuery(this).val(jQuery(this).data("default")).trigger("change")}),window.facebookAdsToolboxConfig.pixel.pixelId="",window.facebookAdsToolboxConfig.diaSettingId="",window.fb_connected=!1,not_connected(),console.log("Deleting all settings and removing all FBIDs!"),ajax("ajax_delete_fb_settings",{_ajax_nonce:wc_facebook_settings_jsx.nonce},e,o)}function save_settings(){var o=0<arguments.length&&void 0!==arguments[0]?arguments[0]:null,n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:null,e=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;(e=e||settings)._ajax_nonce=wc_facebook_settings_jsx.nonce,ajax("ajax_save_fb_settings",e,function(e){o&&o(e)},function(e){n&&n(e)})}function save_settings_for_plugin(o,n){save_settings(function(e){e&&!0===e.success?(console.log(e),o(e)):(console.log("Fail response on save_settings_for_plugin"),n(e))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),n(e)})}function save_settings_and_sync(o){"api_key"in settings&&save_settings(function(e){e&&e.includes("settings_saved")?(console.log(e),window.sendToFacebook("ack set pixel",o.params),window.sendToFacebook("ack set page access token",o.params),window.sendToFacebook("ack set merchant settings",o.params)):(window.sendToFacebook("fail save_settings",e),console.log("Fail response on save_settings_and_sync"))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),window.sendToFacebook("fail save_settings_ajax",JSON.stringify(e))})}function sync_in_progress(){jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","none"),jQuery("#sync_progress").show().html(facebook_for_woocommerce_settings_sync.i18n.sync_in_progress)}function sync_not_in_progress(){jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto"),jQuery("#sync_progress").empty().hide()}function not_connected(){jQuery("#fbsetup").show(),jQuery("#integration-settings").hide(),jQuery(".woocommerce-save-button").hide()}function addAnEventListener(e,o,n){"addEventListener"in e?e.addEventListener(o,n,!1):"attachEvent"in e&&e.attachEvent("on"+o,n)}function setMerchantSettings(e){if(!e.params.setting_id)return console.error("Facebook Extension Error: got no setting_id",e.params),void window.sendToFacebook("fail set merchant settings",e.params);settings.external_merchant_settings_id=e.params.setting_id,window.facebookAdsToolboxConfig.diaSettingId=e.params.setting_id}function setCatalog(e){if(!e.params.catalog_id)return console.error("Facebook Extension Error: got no catalog_id",e.params),void window.sendToFacebook("fail set catalog",e.params);settings.product_catalog_id=e.params.catalog_id,window.sendToFacebook("ack set catalog",e.params)}function setPixel(o){if(!o.params.pixel_id)return console.error("Facebook Ads Extension Error: got no pixel_id",o.params),void window.sendToFacebook("fail set pixel",o.params);get_pixel_id_box()&&(get_pixel_id_box().value=o.params.pixel_id),settings.pixel_id=o.params.pixel_id,pixel_settings.pixel_id=settings.pixel_id,void 0!==o.params.pixel_use_pii&&(get_pixel_use_pii_id_box()&&(get_pixel_use_pii_id_box().checked=!!o.params.pixel_use_pii),settings.pixel_use_pii=o.params.pixel_use_pii,pixel_settings.pixel_use_pii=settings.pixel_use_pii),save_settings(function(e){e&&!0===e.success&&window.sendToFacebook("ack set pixel",o.params)},function(e){console.log(e),window.sendToFacebook("fail set pixel",e)},pixel_settings)}function genFeed(t){console.log("generating feed"),jQuery.get(window.facebookAdsToolboxConfig.feedPrepared.feedUrl+"&regenerate=true").done(function(e){window.sendToFacebook("ack feed",t.params)}).fail(function(e,o,n){window.sendToFacebook("fail feed",t.params)})}function setAccessTokenAndPageId(e){if(!e.params.page_token)return console.error("Facebook Ads Extension Error: got no page_token",e.params),void window.sendToFacebook("fail set page access token",e.params);get_page_id_box()&&(get_page_id_box().value=e.params.page_id),settings.api_key=e.params.page_token,settings.page_id=e.params.page_id,window.facebookAdsToolboxConfig.tokenExpired=!1,document.querySelector("#connection-message-invalid")&&(document.querySelector("#connection-message-invalid").style.display="none"),document.querySelector("#connection-message-refresh")&&(document.querySelector("#connection-message-refresh").style.display="block")}function setMsgerChatSetup(e){if(e.hasOwnProperty("is_messenger_chat_plugin_enabled")&&(settings.is_messenger_chat_plugin_enabled=e.is_messenger_chat_plugin_enabled,jQuery("#woocommerce_facebookcommerce_enable_messenger").prop("checked",e.is_messenger_chat_plugin_enabled).trigger("change")),e.hasOwnProperty("facebook_jssdk_version")&&(settings.facebook_jssdk_version=e.facebook_jssdk_version),e.hasOwnProperty("page_id")&&(settings.fb_page_id=e.page_id),e.hasOwnProperty("customization")){var o=e.customization;o.hasOwnProperty("greetingTextCode")&&(settings.msger_chat_customization_greeting_text_code=o.greetingTextCode,jQuery("#woocommerce_facebookcommerce_messenger_greeting").val(o.greetingTextCode).trigger("change")),o.hasOwnProperty("locale")&&(settings.msger_chat_customization_locale=o.locale,jQuery("#woocommerce_facebookcommerce_messenger_locale").val(o.locale).trigger("change")),o.hasOwnProperty("themeColorCode")&&(settings.msger_chat_customization_theme_color_code=o.themeColorCode,jQuery("#woocommerce_facebookcommerce_messenger_color_hex").val(o.themeColorCode).trigger("change"))}}function setFeedMigrated(e){if(!e.params.hasOwnProperty("feed_migrated"))return console.error("Facebook Extension Error: feed migrated not received",e.params),void window.sendToFacebook("fail set feed migrated",e.params);settings.feed_migrated=e.params.feed_migrated,window.facebookAdsToolboxConfig.feedPrepared.feedMigrated=e.params.feed_migrated,jQuery("#woocommerce-facebook-settings-sync-products").hide(),jQuery(".notice.wc-facebook-migrate-notice").hide(),save_settings_for_plugin(function(e){window.sendToFacebook("ack set feed migrated",event.data)},function(e){window.sendToFacebook("fail set feed migrated",event.data)})}function iFrameListener(o){var e=o.origin||o.originalEvent.origin;if(e!==window.facebookAdsToolboxConfig.popupOrigin){if(!urlFromSameDomain(e,window.facebookAdsToolboxConfig.popupOrigin))return;window.facebookAdsToolboxConfig.popupOrigin=e}switch(o.data.type){case"reset":delete_all_settings(function(e){e&&o.data.params?"Settings Deleted"===e?window.sendToFacebook("ack reset",o.data.params):(console.log(e),alert(e)):console.log("Got no response from delete_all_settings")},function(e){console.error(e)});break;case"get dia settings":window.sendToFacebook("dia settings",window.diaConfig);break;case"set catalog":setCatalog(o.data);break;case"set feed migrated":setFeedMigrated(o.data);break;case"set pixel":setPixel(o.data);break;case"gen feed":genFeed(o.data);break;case"set page access token":window.sendToFacebook("ack set page access token",o.data.params);break;case"set page":setPage(o.data);break;case"set merchant settings":setMerchantSettings(o.data),save_settings(function(e){console.log(e),e&&!0===e.success?(settings.pixel_id&&window.sendToFacebook("ack set pixel",o.data.params),settings.page_id&&window.sendToFacebook("ack set page",o.data.params),settings.external_merchant_settings_id&&(window.sendToFacebook("ack set merchant settings",o.data.params),jQuery("#fbsetup").hide(),jQuery("#integration-settings").show(),jQuery(".woocommerce-save-button").show())):(window.sendToFacebook("fail save_settings",e),console.log("Fail response on save_settings"))},function(e){console.log("Ajax error while saving settings:"+JSON.stringify(e)),window.sendToFacebook("fail save_settings_ajax",JSON.stringify(e))});break;case"set msger chat":setMsgerChatSetup(o.data.params),save_settings_for_plugin(function(e){window.sendToFacebook("ack msger chat",o.data)},function(e){window.sendToFacebook("fail msger chat",o.data)})}}function setPage(e){if(!e.params.hasOwnProperty("page_id"))return console.error("Facebook Extension Error: page ID not received",e.params),void window.sendToFacebook("fail set page",e.params);settings.page_id=e.params.page_id,jQuery("#woocommerce_facebookcommerce_facebook_page_id").val(settings.page_id)}function urlFromSameDomain(e,o){if(!e.startsWith("http")||!o.startsWith("http"))return!1;var n=parseURL(e),t=parseURL(o),s=n.host.replace(/^\w+\./,"www."),i=t.host.replace(/^\w+\./,"www.");return n.protocol===t.protocol&&s===i}function parseURL(e){var o=document.createElement("a");return o.href=e,o}function check_background_processor_status(){window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload||(clearInterval(window.fb_pings),window.fb_pings=setInterval(function(){console.log("Pinging queue..."),check_queues()},1e4))}function ping_feed_status_queue(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:0;clearInterval(window.fb_feed_pings),window.fb_feed_pings=setInterval(function(){console.log("Pinging feed uploading queue..."),check_feed_upload_queue(e)},3e4*(1<<e))}function product_sync_complete(e){sync_not_in_progress(),e.empty().hide(),clearInterval(window.fb_pings)}function check_queues(){ajax("ajax_fb_background_check_queue",{request_time:(new Date).getTime(),_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){if(window.feed_upload)clearInterval(window.fb_pings);else{var o=jQuery("#sync_progress"),n=parse_response_check_connection(e);if(n){if(fb_sync_no_response_count=0,n){n.background||(console.log("No background sync found, disabling pings"),clearInterval(window.fb_pings));var t=!!n.processing,s=n.remaining;if(t){var i="";i=1===s?facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_singular:facebook_for_woocommerce_settings_sync.i18n.sync_remaining_items_plural,o.show().html(i.replace("{count}",s)),0===s&&product_sync_complete(o)}else{if(window.fb_sync_start_time&&n.request_time){var a=new Date(parseInt(n.request_time));if(window.fb_sync_start_time>a)return void console.log("OLD PING")}0===s&&product_sync_complete(o)}}}else 5<fb_sync_no_response_count++&&clearInterval(window.fb_pings)}})}function parse_response_check_connection(e){if(e){console.log(e);var o=e.substring(e.indexOf("{"));return(o=JSON.parse(o)).connected||window.fb_connected?o:(not_connected(),null)}return null}function check_feed_upload_queue(t){ajax("ajax_check_feed_upload_status",{_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){var o=jQuery("#sync_progress"),n=parse_response_check_connection(e);if(clearInterval(window.fb_feed_pings),n)switch(n.status){case"complete":window.feed_upload=!1,window.is_test?display_test_result():product_sync_complete(o);break;case"in progress":o.show().html(facebook_for_woocommerce_settings_sync.i18n.sync_in_progress),ping_feed_status_queue(t+1);break;default:jQuery("#woocommerce-facebook-settings-manage-connection, #woocommerce-facebook-settings-sync-products").css("pointer-events","auto"),jQuery("#sync_progress").show().html('<span style="color: #DC3232">'+facebook_for_woocommerce_settings_sync.i18n.feed_upload_error+"</span>"),window.feed_upload=!1,window.is_test&&display_test_result()}})}function display_test_result(){ajax("ajax_display_test_result",{_ajax_nonce:wc_facebook_settings_jsx.nonce},function(e){var o=jQuery("#sync_progress"),n=document.querySelector("#sync_complete"),t=parse_response_check_connection(e);if(t)switch(t.pass){case"true":sync_not_in_progress(),n&&(n.style.display="",n.innerHTML=facebook_for_woocommerce_settings_sync.i18n.integration_test_sucessful),o.empty().hide(),window.is_test=!1;break;case"in progress":o.show().html(facebook_for_woocommerce_settings_sync.i18n.integration_test_in_progress),ping_feed_status_queue();break;default:window.debug_info=t.debug_info+"<br/>"+t.stack_trace,n&&(n.style.display="",n.innerHTML=facebook_for_woocommerce_settings_sync.i18n.integration_test_failed),o.empty().hide(),document.querySelector("#debug_info")&&(document.querySelector("#debug_info").style.display=""),window.is_test=!1}})}function show_debug_info(){var e=document.querySelector("#stack_trace");e&&(e.innerHTML=window.debug_info),document.querySelector("#debug_info")&&(document.querySelector("#debug_info").style.display="none"),window.debug_info=""}function fbe_init_nux_messages(){var r=window.jQuery;r(function(){r.each(r(".nux-message"),function(e,o){var n=r(o),t=n.data("target"),s=r("#"+t),i=s.position(),a=s.height()/2,c=s.outerWidth();n.css({top:Math.ceil(i.top+a)+"px",left:Math.ceil(i.left+c)+"px",display:"block"}),r(".nux-message-close-btn",n).click(function(){r(o).hide()})})})}function saveAutoSyncSchedule(){var e=document.getElementsByClassName("autosyncCheck")[0].checked,o=document.getElementsByClassName("autosyncTime")[0],n=(document.getElementsByClassName("autosyncSaveButton")[0],document.getElementsByClassName("autosyncSavedNotice")[0]);e?(o.removeAttribute("disabled"),n.style.transition="",n.style.opacity=1,setTimeout(function(){n.style.opacity=0,n.style.transition="opacity 5s"},3e3)):o.setAttribute("disabled",!0),ajax("ajax_schedule_force_resync",{enabled:e?1:0,time:o.value,_ajax_nonce:wc_facebook_settings_jsx.nonce})}function syncShortDescription(){var e=document.getElementsByClassName("syncShortDescription")[0].checked;ajax("ajax_update_fb_option",{option:"fb_sync_short_description",option_value:e?1:0,_ajax_nonce:wc_facebook_settings_jsx.nonce},null,function(){document.getElementsByClassName("syncShortDescription")[0].checked=!e,console.log("Failed to sync Short Description")})}addAnEventListener(window,"message",iFrameListener),jQuery(document).ready(function(e){e(".notice .wc-facebook-manage-connection").click(function(e){e.preventDefault(),facebookConfig()}),e("#woocommerce-facebook-settings-sync-products").click(function(e){e.preventDefault(),sync_confirm()})});
changelog.txt CHANGED
@@ -1,5 +1,16 @@
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
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
+ 2020.04.23 - version 1.11.0
4
+ * Tweak - Sync products using Facebook's feed pull method
5
+ * Fix - When filtering products by sync enabled status, make sure variable products with sync disabled status do not show up in results
6
+ * Fix - Make sure that the Facebook sync enabled and catalog visibility columns are properly displayed on narrow screen sizes on some browsers
7
+ * Fix - Do not show a confirmation modal when saving a variable product that was previously synced but belongs now to a term excluded from sync
8
+ * Fix - Ensure variable products excluded from sync are not synced in Facebook
9
+ * Fix - Trigger a modal prompt when attempting to enable sync for variations of a variable product that belongs to a term excluded from sync
10
+ * Fix - Address potential PHP warnings in the product feed with non-standard product variations introduced by third party plugins
11
+ * Fix - Fix a JavaScript error triggered on the settings page while trying to excluded terms from sync
12
+ * Fix - Fix a JavaScript error triggered when saving a product and using checkboxes for tags
13
+
14
  2020.03.17 - version 1.10.2
15
  * Tweak - Add a setting to easily enable debug logging
16
  * Tweak - Allow third party plugins and themes to track an add-to-cart event on added_to_cart JS event
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.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;
@@ -46,6 +46,9 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
46
  /** @var \SkyVerge\WooCommerce\Facebook\AJAX Ajax handler instance */
47
  private $ajax;
48
 
 
 
 
49
 
50
  /**
51
  * Constructs the plugin.
@@ -76,14 +79,18 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
76
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
77
 
78
  if ( ! defined( 'WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL' ) ) {
79
- define( 'WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL', get_admin_url() . '/admin.php?page=wc-settings&tab=integration' . '&section=facebookcommerce' );
80
  }
81
 
82
  include_once 'facebook-commerce.php';
83
 
84
  require_once __DIR__ . '/includes/Products.php';
 
 
85
  require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
86
 
 
 
87
  if ( is_ajax() ) {
88
 
89
  require_once __DIR__ . '/includes/AJAX.php';
@@ -112,6 +119,51 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
112
  }
113
 
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  /**
116
  * Adds a Facebook integration to WooCommerce.
117
  *
@@ -168,6 +220,19 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
168
  }
169
 
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  /**
172
  * Gets the integration instance.
173
  *
19
 
20
 
21
  /** @var string the plugin version */
22
+ const VERSION = '1.11.0';
23
 
24
  /** @var string for backwards compatibility TODO: remove this in v2.0.0 {CW 2020-02-06} */
25
  const PLUGIN_VERSION = self::VERSION;
46
  /** @var \SkyVerge\WooCommerce\Facebook\AJAX Ajax handler instance */
47
  private $ajax;
48
 
49
+ /** @var \SkyVerge\WooCommerce\Facebook\Products\Feed product feed handler */
50
+ private $product_feed;
51
+
52
 
53
  /**
54
  * Constructs the plugin.
79
  if ( \WC_Facebookcommerce_Utils::isWoocommerceIntegration() ) {
80
 
81
  if ( ! defined( 'WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL' ) ) {
82
+ define( 'WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL', admin_url( 'admin.php?page=wc-settings&tab=integration&section=facebookcommerce' ) );
83
  }
84
 
85
  include_once 'facebook-commerce.php';
86
 
87
  require_once __DIR__ . '/includes/Products.php';
88
+ require_once __DIR__ . '/includes/Products/Feed.php';
89
+ require_once __DIR__ . '/includes/fbproductfeed.php';
90
  require_once __DIR__ . '/facebook-commerce-messenger-chat.php';
91
 
92
+ $this->product_feed = new \SkyVerge\WooCommerce\Facebook\Products\Feed();
93
+
94
  if ( is_ajax() ) {
95
 
96
  require_once __DIR__ . '/includes/AJAX.php';
119
  }
120
 
121
 
122
+ /**
123
+ * Adds the plugin admin notices.
124
+ *
125
+ * @since 1.11.0
126
+ */
127
+ public function add_admin_notices() {
128
+
129
+ parent::add_admin_notices();
130
+
131
+ $integration = $this->get_integration();
132
+
133
+ // if the feed hasn't been migrated to FBE 1.5 and the access token is bad, display a notice
134
+ if ( $integration && $integration->is_configured() && ! $integration->is_feed_migrated() && ! $integration->get_page_name() ) {
135
+
136
+ $docs_url = 'https://docs.woocommerce.com/document/facebook-for-woocommerce/#faq-security';
137
+
138
+ if ( $this->is_plugin_settings() ) {
139
+
140
+ $message = sprintf(
141
+ /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag */
142
+ __( '%1$sHeads up!%2$s Facebook for WooCommerce is migrating to a more secure connection experience. Please %3$sclick here%4$s and go to %1$sAdvanced Options%2$s > %1$sReconnect Catalog%2$s to securely reconnect. %5$sLearn more%6$s.', 'facebook-for-woocommerce' ),
143
+ '<strong>', '</strong>',
144
+ '<a href="#" class="wc-facebook-manage-connection">', '</a>',
145
+ '<a href="' . esc_url( $docs_url ) . '" target="_blank">', '</a>'
146
+ );
147
+
148
+ } else {
149
+
150
+ $message = sprintf(
151
+ /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag */
152
+ __( '%1$sHeads up!%2$s Facebook for WooCommerce is migrating to a more secure connection experience. Please %3$sclick here%4$s and go to %1$sManage Connection%2$s > %1$sAdvanced Options%2$s > %1$sReconnect Catalog%2$s to securely reconnect. %5$sLearn more%6$s.', 'facebook-for-woocommerce' ),
153
+ '<strong>', '</strong>',
154
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>',
155
+ '<a href="' . esc_url( $docs_url ) . '" target="_blank">', '</a>'
156
+ );
157
+ }
158
+
159
+ $this->get_admin_notice_handler()->add_admin_notice( $message, self::PLUGIN_ID . '_migrate_to_v1_5', [
160
+ 'dismissible' => false,
161
+ 'notice_class' => 'notice-info wc-facebook-migrate-notice',
162
+ ] );
163
+ }
164
+ }
165
+
166
+
167
  /**
168
  * Adds a Facebook integration to WooCommerce.
169
  *
220
  }
221
 
222
 
223
+ /**
224
+ * Gets the product feed handler.
225
+ *
226
+ * @since 1.11.0
227
+ *
228
+ * @return \SkyVerge\WooCommerce\Facebook\Products\Feed
229
+ */
230
+ public function get_product_feed_handler() {
231
+
232
+ return $this->product_feed;
233
+ }
234
+
235
+
236
  /**
237
  * Gets the integration instance.
238
  *
facebook-commerce-events-tracker.php CHANGED
@@ -51,8 +51,8 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
51
  array( $this, 'inject_search_event' )
52
  );
53
 
54
- // AddToCart
55
- add_action( 'woocommerce_add_to_cart', [ $this, 'inject_add_to_cart_event' ], 40, 4 );
56
  // AddToCart while AJAX is enabled
57
  add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'add_filter_for_add_to_cart_fragments' ] );
58
  // AddToCart while using redirect to cart page
@@ -62,26 +62,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
62
  add_action( 'woocommerce_after_cart', [ $this, 'inject_add_to_cart_redirect_event' ], 10, 2 );
63
  }
64
 
65
- add_action(
66
- 'woocommerce_after_checkout_form',
67
- array( $this, 'inject_initiate_checkout_event' )
68
- );
69
- add_action(
70
- 'woocommerce_thankyou',
71
- array( $this, 'inject_gateway_purchase_event' ),
72
- self::FB_PRIORITY_HIGH
73
- );
74
- add_action(
75
- 'woocommerce_payment_complete',
76
- array( $this, 'inject_purchase_event' ),
77
- self::FB_PRIORITY_HIGH
78
- );
79
- add_action(
80
- 'wpcf7_contact_form',
81
- array( $this, 'inject_lead_event_hook' ),
82
- self::FB_PRIORITY_LOW
83
- );
84
 
 
 
85
  }
86
 
87
  public function apply_filters() {
@@ -177,7 +165,7 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
177
  }
178
 
179
  if ( ! is_admin() && is_search() && get_search_query() !== '' ) {
180
- if ( $this->pixel->check_last_event( 'Search' ) ) {
181
  return;
182
  }
183
 
@@ -391,13 +379,15 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
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
395
  * @return string
396
  */
397
- public function set_last_product_added_to_cart_upon_redirect( $redirect, $product ) {
398
 
399
  if ( $product instanceof \WC_Product ) {
400
  WC()->session->set( 'facebook_for_woocommerce_last_product_added_to_cart', $product->get_id() );
 
 
401
  }
402
 
403
  return $redirect;
@@ -411,9 +401,14 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
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
 
@@ -446,11 +441,13 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
446
 
447
 
448
  /**
449
- * Triggers InitiateCheckout for checkout page.
 
 
450
  */
451
  public function inject_initiate_checkout_event() {
452
 
453
- if ( ! self::$isEnabled || $this->pixel->check_last_event( 'InitiateCheckout' ) ) {
454
  return;
455
  }
456
 
@@ -465,20 +462,29 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
465
 
466
 
467
  /**
468
- * Triggers Purchase for payment transaction complete and for the thank you page in cases of delayed payment.
 
 
 
 
 
 
 
 
 
469
  *
470
  * @param int $order_id order identifier
471
  */
472
  public function inject_purchase_event( $order_id ) {
473
 
474
- if ( ! self::$isEnabled || $this->pixel->check_last_event( 'Purchase' ) ) {
475
  return;
476
  }
477
 
478
- $this->inject_subscribe_event( $order_id );
479
-
480
- $order = new \WC_Order( $order_id );
481
  $content_type = 'product';
 
 
482
  $product_ids = [ [] ];
483
 
484
  foreach ( $order->get_items() as $item ) {
@@ -490,60 +496,80 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
490
  if ( 'product_group' !== $content_type && $product->is_type( 'variable' ) ) {
491
  $content_type = 'product_group';
492
  }
 
 
 
 
 
 
 
 
 
493
  }
494
  }
495
 
496
- $product_ids = wp_json_encode( array_merge( ... $product_ids ) );
497
-
498
  $this->pixel->inject_event( 'Purchase', [
499
- 'num_items' => $this->get_cart_num_items(),
500
- 'content_ids' => $product_ids,
 
501
  'content_type' => $content_type,
502
  'value' => $order->get_total(),
503
  'currency' => get_woocommerce_currency(),
504
  ] );
 
 
505
  }
506
 
507
 
508
  /**
509
- * Triggers Subscribe for payment transaction complete of purchase with
510
- * subscription.
 
 
 
 
 
511
  */
512
  public function inject_subscribe_event( $order_id ) {
513
- if ( ! function_exists( 'wcs_get_subscriptions_for_order' ) ) {
 
514
  return;
515
  }
516
 
517
- $subscription_ids = wcs_get_subscriptions_for_order( $order_id );
518
- foreach ( $subscription_ids as $subscription_id ) {
519
- $subscription = new WC_Subscription( $subscription_id );
520
- $this->pixel->inject_event(
521
- 'Subscribe',
522
- array(
523
- 'sign_up_fee' => $subscription->get_sign_up_fee(),
524
- 'value' => $subscription->get_total(),
525
- 'currency' => get_woocommerce_currency(),
526
- )
527
- );
528
  }
529
  }
530
 
 
531
  /**
532
- * Triggers Purchase for thank you page for COD, BACS CHEQUE payment
533
- * which won't invoke woocommerce_payment_complete.
 
 
 
 
 
 
 
 
534
  */
535
  public function inject_gateway_purchase_event( $order_id ) {
536
- if ( ! self::$isEnabled ||
537
- $this->pixel->check_last_event( 'Purchase' ) ) {
538
- return;
539
- }
540
 
541
- $order = new WC_Order( $order_id );
542
- $payment = $order->get_payment_method();
543
  $this->inject_purchase_event( $order_id );
544
- $this->inject_subscribe_event( $order_id );
545
  }
546
 
 
547
  /** Contact Form 7 Support **/
548
  public function inject_lead_event_hook() {
549
  add_action( 'wp_footer', array( $this, 'inject_lead_event' ), 11 );
51
  array( $this, 'inject_search_event' )
52
  );
53
 
54
+ // AddToCart events
55
+ add_action( 'woocommerce_add_to_cart', [ $this, 'inject_add_to_cart_event' ], 40, 4 );
56
  // AddToCart while AJAX is enabled
57
  add_action( 'woocommerce_ajax_added_to_cart', [ $this, 'add_filter_for_add_to_cart_fragments' ] );
58
  // AddToCart while using redirect to cart page
62
  add_action( 'woocommerce_after_cart', [ $this, 'inject_add_to_cart_redirect_event' ], 10, 2 );
63
  }
64
 
65
+ // InitiateCheckout events
66
+ add_action( 'woocommerce_after_checkout_form', [ $this, 'inject_initiate_checkout_event' ] );
67
+ // Purchase and Subscribe events
68
+ add_action( 'woocommerce_thankyou', [ $this, 'inject_purchase_event' ], 40 );
69
+ add_action( 'woocommerce_payment_complete', [ $this, 'inject_purchase_event' ], 40 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
72
+ add_action( 'wpcf7_contact_form', [ $this, 'inject_lead_event_hook' ], self::FB_PRIORITY_LOW );
73
  }
74
 
75
  public function apply_filters() {
165
  }
166
 
167
  if ( ! is_admin() && is_search() && get_search_query() !== '' ) {
168
+ if ( $this->pixel->is_last_event( 'Search' ) ) {
169
  return;
170
  }
171
 
379
  * @since 1.10.2
380
  *
381
  * @param string $redirect URL redirecting to (usually cart)
382
+ * @param null|\WC_Product $product the product just added to the cart
383
  * @return string
384
  */
385
+ public function set_last_product_added_to_cart_upon_redirect( $redirect, $product = null ) {
386
 
387
  if ( $product instanceof \WC_Product ) {
388
  WC()->session->set( 'facebook_for_woocommerce_last_product_added_to_cart', $product->get_id() );
389
+ } else {
390
+ facebook_for_woocommerce()->log( 'Cannot record AddToCart event because the product cannot be determined. Backtrace: ' . print_r( wp_debug_backtrace_summary(), true ) );
391
  }
392
 
393
  return $redirect;
401
  *
402
  * @since 1.10.2
403
  *
404
+ * @param null|int $product_id the ID of the product just added to the cart
405
  */
406
+ public function set_last_product_added_to_cart_upon_ajax_redirect( $product_id = null ) {
407
+
408
+ if ( ! $product_id ) {
409
+ facebook_for_woocommerce()->log( 'Cannot record AddToCart event because the product cannot be determined. Backtrace: ' . print_r( wp_debug_backtrace_summary(), true ) );
410
+ return;
411
+ }
412
 
413
  $product = wc_get_product( $product_id );
414
 
441
 
442
 
443
  /**
444
+ * Triggers an InitiateCheckout event when customer reaches checkout page.
445
+ *
446
+ * @internal
447
  */
448
  public function inject_initiate_checkout_event() {
449
 
450
+ if ( ! self::$isEnabled || $this->pixel->is_last_event( 'InitiateCheckout' ) ) {
451
  return;
452
  }
453
 
462
 
463
 
464
  /**
465
+ * Triggers a Purchase event when checkout is completed.
466
+ *
467
+ * This may happen either when:
468
+ * - WooCommerce signals a payment transaction complete (most gateways)
469
+ * - Customer reaches Thank You page skipping payment (for gateways that do not require payment, e.g. Cheque, BACS, Cash on delivery...)
470
+ *
471
+ * The method checks if the event was not triggered already avoiding a duplicate.
472
+ * Finally, if the order contains subscriptions, it will also track an associated Subscription event.
473
+ *
474
+ * @internal
475
  *
476
  * @param int $order_id order identifier
477
  */
478
  public function inject_purchase_event( $order_id ) {
479
 
480
+ if ( ! self::$isEnabled || $this->pixel->is_last_event( 'Purchase' ) ) {
481
  return;
482
  }
483
 
484
+ $order = wc_get_order( $order_id );
 
 
485
  $content_type = 'product';
486
+ $num_items = 0;
487
+ $contents = [];
488
  $product_ids = [ [] ];
489
 
490
  foreach ( $order->get_items() as $item ) {
496
  if ( 'product_group' !== $content_type && $product->is_type( 'variable' ) ) {
497
  $content_type = 'product_group';
498
  }
499
+
500
+ $quantity = $item->get_quantity();
501
+ $content = new \stdClass();
502
+
503
+ $content->id = \WC_Facebookcommerce_Utils::get_fb_retailer_id( $product );
504
+ $content->quantity = $quantity;
505
+
506
+ $contents[] = $content;
507
+ $num_items += $quantity;
508
  }
509
  }
510
 
 
 
511
  $this->pixel->inject_event( 'Purchase', [
512
+ 'num_items' => $num_items,
513
+ 'content_ids' => wp_json_encode( array_merge( ... $product_ids ) ),
514
+ 'contents' => wp_json_encode( $contents ),
515
  'content_type' => $content_type,
516
  'value' => $order->get_total(),
517
  'currency' => get_woocommerce_currency(),
518
  ] );
519
+
520
+ $this->inject_subscribe_event( $order_id );
521
  }
522
 
523
 
524
  /**
525
+ * Triggers a Subscribe event when a given order contains subscription products.
526
+ *
527
+ * @see \WC_Facebookcommerce_EventsTracker::inject_purchase_event()
528
+ *
529
+ * @internal
530
+ *
531
+ * @param int $order_id order identifier
532
  */
533
  public function inject_subscribe_event( $order_id ) {
534
+
535
+ if ( ! self::$isEnabled || ! function_exists( 'wcs_get_subscriptions_for_order' ) || $this->pixel->is_last_event( 'Subscribe' ) ) {
536
  return;
537
  }
538
 
539
+ foreach ( wcs_get_subscriptions_for_order( $order_id ) as $subscription ) {
540
+
541
+ // TODO consider 'StartTrial' event for free trial Subscriptions, which is the same as here (minus sign_up_fee) and tracks "when a person starts a free trial of a product or service" {FN 2020-03-20}
542
+
543
+ // TODO consider including (int|float) 'predicted_ltv': "Predicted lifetime value of a subscriber as defined by the advertiser and expressed as an exact value." {FN 2020-03-20}
544
+ $this->pixel->inject_event( 'Subscribe', [
545
+ 'sign_up_fee' => $subscription->get_sign_up_fee(),
546
+ 'value' => $subscription->get_total(),
547
+ 'currency' => get_woocommerce_currency(),
548
+ ] );
 
549
  }
550
  }
551
 
552
+
553
  /**
554
+ * Triggers a Purchase event.
555
+ *
556
+ * Duplicate of {@see \WC_Facebookcommerce_EventsTracker::inject_purchase_event()}
557
+ *
558
+ * TODO remove this deprecated method by version 2.0.0 or by March 2020 {FN 2020-03-20}
559
+ *
560
+ * @internal
561
+ * @deprecated since 1.11.0
562
+ *
563
+ * @param int $order_id order identifier
564
  */
565
  public function inject_gateway_purchase_event( $order_id ) {
 
 
 
 
566
 
567
+ wc_deprecated_function( __METHOD__, '1.11.0', __CLASS__ . '::inject_purchase_event()' );
568
+
569
  $this->inject_purchase_event( $order_id );
 
570
  }
571
 
572
+
573
  /** Contact Form 7 Support **/
574
  public function inject_lead_event_hook() {
575
  add_action( 'wp_footer', array( $this, 'inject_lead_event' ), 11 );
facebook-commerce-pixel-event.php CHANGED
@@ -184,13 +184,37 @@ if ( ! class_exists( 'WC_Facebookcommerce_Pixel' ) ) :
184
 
185
 
186
  /**
187
- * Prevent double-fires by checking the last event
 
 
 
 
 
188
  */
189
- public function check_last_event( $event_name ) {
 
190
  return $event_name === $this->last_event;
191
  }
192
 
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  /**
195
  * Gets the JavaScript code to track an event.
196
  *
184
 
185
 
186
  /**
187
+ * Determines if the last event in the current thread matches a given event.
188
+ *
189
+ * @since 1.11.0
190
+ *
191
+ * @param string $event_name
192
+ * @return bool
193
  */
194
+ public function is_last_event( $event_name ) {
195
+
196
  return $event_name === $this->last_event;
197
  }
198
 
199
 
200
+ /**
201
+ * Determines if the last event in the current thread matches a given event.
202
+ *
203
+ * TODO remove this deprecated method by March 2020 or version 2.0.0 {FN 2020-03-25}
204
+ *
205
+ * @deprecated since 1.11.0
206
+ *
207
+ * @param string $event_name
208
+ * @return bool
209
+ */
210
+ public function check_last_event( $event_name ) {
211
+
212
+ wc_deprecated_function( __METHOD__, '1.11.0', __CLASS__ . '::has_last_event()' );
213
+
214
+ return $this->is_last_event( $event_name );
215
+ }
216
+
217
+
218
  /**
219
  * Gets the JavaScript code to track an event.
220
  *
facebook-commerce.php CHANGED
@@ -10,6 +10,7 @@
10
 
11
  use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
12
  use SkyVerge\WooCommerce\Facebook\Products;
 
13
 
14
  if ( ! defined( 'ABSPATH' ) ) {
15
  exit; // Exit if accessed directly
@@ -34,6 +35,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
34
  /** @var string the WordPress option name where the feed ID is stored */
35
  const OPTION_FEED_ID = 'wc_facebook_feed_id';
36
 
 
 
 
37
  /** @var string the WordPress option name where the JS SDK version is stored */
38
  const OPTION_JS_SDK_VERSION = 'wc_facebook_js_sdk_version';
39
 
@@ -101,12 +105,18 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
101
  /** @var string|null the configured feed ID */
102
  public $feed_id;
103
 
 
 
 
104
  /** @var string|null the configured pixel install time */
105
  public $pixel_install_time;
106
 
107
  /** @var string|null the configured JS SDK version */
108
  private $js_sdk_version;
109
 
 
 
 
110
 
111
  /** Legacy properties *********************************************************************************************/
112
 
@@ -324,12 +334,15 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
324
  self::FB_PRIORITY_MID
325
  );
326
 
 
 
 
 
 
 
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',
335
  array( $this, 'on_quick_and_bulk_edit_save' ),
@@ -344,6 +357,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
344
  1 // Args passed to on_quick_and_bulk_edit_save ('product')
345
  );
346
 
 
 
347
  add_action(
348
  'before_delete_post',
349
  array( $this, 'on_product_delete' ),
@@ -375,11 +390,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
375
  array( $this, 'ajax_delete_fb_product' )
376
  );
377
 
378
- add_filter(
379
- 'woocommerce_duplicate_product_exclude_meta',
380
- array( $this, 'fb_duplicate_product_reset_meta' )
381
- );
382
-
383
  add_action(
384
  'pmxi_after_xml_import',
385
  array( $this, 'wp_all_import_compat' )
@@ -407,6 +417,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
407
  // Must be outside of admin for cron to schedule correctly.
408
  add_action( 'sync_all_fb_products_using_feed', [ $this, 'handle_scheduled_resync_action' ], self::FB_PRIORITY_MID );
409
 
 
 
 
410
  if ( $this->get_facebook_pixel_id() ) {
411
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $this->is_advanced_matching_enabled() );
412
  $this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info );
@@ -620,6 +633,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
620
  }
621
 
622
  ?>
 
623
  <?php echo esc_html__( 'Visible:', 'facebook-for-woocommerce' ); ?>
624
  <input name="<?php echo esc_attr( Products::VISIBILITY_META_KEY ); ?>"
625
  type="checkbox"
@@ -627,6 +641,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
627
  <?php echo checked( ! $woo_product->woo_product instanceof \WC_Product || Products::is_product_visible( $woo_product->woo_product ) ); ?>/>
628
 
629
  <p/>
 
630
  <input name="is_product_page" type="hidden" value="1"/>
631
 
632
  <p/>
@@ -736,14 +751,16 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
736
  },
737
  feed: {
738
  totalVisibleProducts: '<?php echo esc_js( $this->get_product_count() ); ?>',
739
- hasClientSideFeedUpload: '<?php echo esc_js( ! ! $this->get_feed_id() ); ?>'
 
 
740
  },
741
  feedPrepared: {
742
- feedUrl: '',
743
  feedPingUrl: '',
 
744
  samples: <?php echo $this->get_sample_product_feed(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
745
  },
746
- tokenExpired: '<?php echo $this->get_page_access_token() && ! $this->get_page_name(); ?>',
747
  excludedCategoryIDs: <?php echo json_encode( $this->get_excluded_product_category_ids() ); ?>,
748
  excludedTagIDs: <?php echo json_encode( $this->get_excluded_product_tag_ids() ); ?>,
749
  messengerGreetingMaxCharacters: <?php echo esc_js( $this->get_messenger_greeting_max_characters() ); ?>
@@ -797,7 +814,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
797
 
798
  // phpcs:ignore WordPress.Security.NonceVerification.Missing
799
  $sync_enabled = ! empty( $_POST['fb_sync_enabled'] );
800
- $is_visible = ! empty( $_POST['fb_visibility'] );
801
 
802
  if ( ! $product->is_type( 'variable' ) ) {
803
 
@@ -813,9 +830,10 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
813
  }
814
  }
815
 
816
- $this->update_fb_visibility( $product->get_id(), $is_visible ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN );
 
817
 
818
- if ( $sync_enabled ) {
819
 
820
  switch ( $product->get_type() ) {
821
 
@@ -836,6 +854,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
836
  break;
837
  }
838
  }
 
 
839
  }
840
 
841
 
@@ -871,6 +891,25 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
871
  }
872
 
873
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
  /**
875
  * Deletes a product from Facebook.
876
  *
@@ -887,7 +926,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
887
  }
888
 
889
  // skip if not enabled for sync
890
- if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
891
  return;
892
  }
893
 
@@ -916,12 +955,21 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
916
  $pg_result = $this->fbgraph->delete_product_group( $fb_product_group_id );
917
  WC_Facebookcommerce_Utils::log( $pg_result );
918
  }
 
 
919
  }
920
 
 
921
  /**
922
- * Update FB visibility for trashing and restore.
 
 
 
 
 
 
923
  */
924
- function fb_change_product_published_status( $new_status, $old_status, $post ) {
925
  global $post;
926
 
927
  if ( ! $post ) {
@@ -933,19 +981,19 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
933
  $product = wc_get_product( $post->ID );
934
 
935
  // bail if this product isn't enabled for sync
936
- if ( ! $product instanceof \WC_Product || ! Products::is_sync_enabled_for_product( $product ) ) {
937
  return;
938
  }
939
 
940
- // change from publish status -> unpublish status, e.g. trash, draft, etc.
941
  // change from trash status -> publish status
942
  // no need to update for change from trash <-> unpublish status
943
- if ( ( $old_status == 'publish' && $new_status != 'publish' ) ||
944
- ( $old_status == 'trash' && $new_status == 'publish' ) ) {
945
  $this->update_fb_visibility( $post->ID, $visibility );
946
  }
947
  }
948
 
 
949
  /**
950
  * Generic function for use with any product publishing.
951
  *
@@ -956,14 +1004,19 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
956
  */
957
  public function on_product_publish( $wp_id ) {
958
 
959
- if ( get_post_status( $wp_id ) != 'publish' ) {
 
 
 
 
 
960
  return;
961
  }
962
 
963
- $woo_product = new WC_Facebook_Product( $wp_id );
964
 
965
  // skip if not enabled for sync
966
- if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
967
  return;
968
  }
969
 
@@ -974,6 +1027,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
974
  }
975
  }
976
 
 
977
  /**
978
  * If the user has opt-in to remove products that are out of stock,
979
  * this function will delete the product from FB Page as well.
@@ -1071,7 +1125,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1071
  }
1072
 
1073
  // skip if not enabled for sync
1074
- if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
1075
  return;
1076
  }
1077
 
@@ -1210,11 +1264,13 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1210
  $fb_product_group_id
1211
  );
1212
 
 
1213
  $this->display_success_message(
1214
  'Created product group <a href="https://facebook.com/' .
1215
  $fb_product_group_id . '" target="_blank">' .
1216
  $fb_product_group_id . '</a> on Facebook.'
1217
  );
 
1218
 
1219
  return $fb_product_group_id;
1220
  }
@@ -1249,11 +1305,13 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1249
  $fb_product_item_id
1250
  );
1251
 
 
1252
  $this->display_success_message(
1253
  'Created product item <a href="https://facebook.com/' .
1254
  $fb_product_item_id . '" target="_blank">' .
1255
  $fb_product_item_id . '</a> on Facebook.'
1256
  );
 
1257
 
1258
  return $fb_product_item_id;
1259
  }
@@ -1300,6 +1358,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1300
  )
1301
  );
1302
 
 
1303
  if ( $result ) {
1304
  $this->display_success_message(
1305
  'Updated product group <a href="https://facebook.com/' .
@@ -1307,6 +1366,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1307
  '</a> on Facebook.'
1308
  );
1309
  }
 
1310
  }
1311
 
1312
  /**
@@ -1327,12 +1387,14 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1327
  )
1328
  );
1329
 
 
1330
  if ( $result ) {
1331
  $this->display_success_message(
1332
  'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
1333
  '" target="_blank">' . $fb_product_item_id . '</a> on Facebook.'
1334
  );
1335
  }
 
1336
  }
1337
 
1338
  /**
@@ -1352,6 +1414,19 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1352
  return;
1353
  }
1354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1355
  if ( isset( $_REQUEST['api_key'] ) ) {
1356
 
1357
  $api_key = sanitize_text_field( wp_unslash( $_REQUEST['api_key'] ) );
@@ -1361,6 +1436,15 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1361
  }
1362
  }
1363
 
 
 
 
 
 
 
 
 
 
1364
  if ( isset( $_REQUEST['product_catalog_id'] ) ) {
1365
 
1366
  $product_catalog_id = sanitize_text_field( wp_unslash( $_REQUEST['product_catalog_id'] ) );
@@ -1382,7 +1466,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1382
  if ( ctype_digit( $pixel_id ) ) {
1383
 
1384
  // to prevent race conditions with pixel-only settings, only save a pixel if we already have an access token
1385
- if ( $this->get_page_access_token() ) {
1386
 
1387
  if ( $this->get_facebook_pixel_id() !== $pixel_id ) {
1388
  $this->update_pixel_install_time( time() );
@@ -1393,9 +1477,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1393
  } else {
1394
 
1395
  WC_Facebookcommerce_Utils::log( 'Got pixel-only settings, doing nothing' );
1396
- echo 'Not saving pixel-only settings';
1397
 
1398
- wp_die();
1399
  }
1400
  }
1401
  }
@@ -1413,15 +1496,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1413
  }
1414
  }
1415
 
1416
- if ( isset( $_REQUEST['external_merchant_settings_id'] ) ) {
1417
-
1418
- $external_merchant_settings_id = sanitize_text_field( wp_unslash( $_REQUEST['external_merchant_settings_id'] ) );
1419
-
1420
- if ( ctype_digit( $external_merchant_settings_id ) ) {
1421
- $this->update_external_merchant_settings_id( $external_merchant_settings_id );
1422
- }
1423
- }
1424
-
1425
  if ( isset( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) {
1426
  $this->settings[ self::SETTING_ENABLE_MESSENGER ] = wc_bool_to_string( wc_clean( wp_unslash( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) );
1427
  }
@@ -1446,9 +1520,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1446
  update_option( $this->get_option_key(), apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings ) );
1447
 
1448
  WC_Facebookcommerce_Utils::log( 'Settings saved!' );
1449
- echo 'settings_saved';
1450
 
1451
- wp_die();
1452
  }
1453
 
1454
  /**
@@ -1484,6 +1557,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1484
  $this->init_settings();
1485
  $this->update_page_access_token( '' );
1486
  $this->update_product_catalog_id( '' );
 
1487
 
1488
  $this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = '';
1489
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = 'no';
@@ -1497,7 +1571,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1497
  $this->update_external_merchant_settings_id( '' );
1498
  $this->update_pixel_install_time( 0 );
1499
  $this->update_feed_id( '' );
1500
- $this->settings['fb_upload_id'] = '';
1501
  $this->settings['upload_end_time'] = '';
1502
 
1503
  WC_Facebookcommerce_Pixel::set_pixel_id( 0 );
@@ -1528,36 +1602,66 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1528
  }
1529
 
1530
  /**
1531
- * Check Feed Upload Status
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1532
  **/
1533
- function ajax_check_feed_upload_status() {
1534
- WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
 
 
1535
  check_ajax_referer( 'wc_facebook_settings_jsx' );
 
1536
  if ( $this->get_page_access_token() ) {
1537
- $response = array(
 
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';
1545
  }
1546
- $this->fbproductfeed = new WC_Facebook_Product_Feed(
 
1547
  $this->get_product_catalog_id(),
1548
  $this->fbgraph
1549
  );
1550
  }
 
1551
  $status = $this->fbproductfeed->is_upload_complete( $this->settings );
1552
 
1553
  $response['status'] = $status;
 
1554
  } else {
1555
- $response = array(
 
1556
  'connected' => true,
1557
  'status' => 'error',
1558
- );
1559
  }
1560
- if ( $response['status'] == 'complete' ) {
 
 
1561
  update_option(
1562
  $this->get_option_key(),
1563
  apply_filters(
@@ -1566,11 +1670,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1566
  )
1567
  );
1568
  }
 
1569
  } else {
1570
- $response = array(
1571
- 'connected' => false,
1572
- );
1573
  }
 
1574
  printf( json_encode( $response ) );
1575
  wp_die();
1576
  }
@@ -1810,7 +1915,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1810
  }
1811
 
1812
  // check required fields
1813
- if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
1814
 
1815
  $message = sprintf(
1816
  /* translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag */
@@ -2316,8 +2421,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
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(),
@@ -2447,6 +2551,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2447
  /** @see \WC_Facebookcommerce_Integration::generate_product_sync_title_html() */
2448
  [
2449
  'type' => 'product_sync_title',
 
2450
  ],
2451
 
2452
  self::SETTING_ENABLE_PRODUCT_SYNC => [
@@ -2497,11 +2602,11 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2497
 
2498
  /** @see \WC_Facebookcommerce_Integration::generate_resync_schedule_html() */
2499
  /** @see \WC_Facebookcommerce_Integration::validate_resync_schedule_field() */
2500
- self::SETTING_SCHEDULED_RESYNC_OFFSET => [
2501
- 'title' => __( 'Force daily resync at', 'facebook-for-woocommerce' ),
2502
- 'class' => 'product-sync-field resync-schedule-fieldset',
2503
- 'type' => 'resync_schedule',
2504
- ],
2505
 
2506
  [
2507
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
@@ -2601,7 +2706,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2601
  onclick="facebookConfig();"
2602
  ><?php esc_html_e( 'Manage connection', 'facebook-for-woocommerce' ); ?></a>
2603
  </h3>
2604
- <?php if ( empty( $this->get_page_name() ) ) : ?>
 
 
2605
  <div id="connection-message-invalid">
2606
  <p style="color: #DC3232;">
2607
  <?php esc_html_e( 'Your connection has expired.', 'facebook-for-woocommerce' ); ?>
@@ -2618,7 +2725,9 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2618
  </strong>
2619
  </p>
2620
  </div>
2621
- <?php endif; ?>
 
 
2622
  <table class="form-table">
2623
  <?php
2624
 
@@ -2640,12 +2749,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2640
  protected function generate_facebook_page_name_html( $key, array $args = [] ) {
2641
 
2642
  $key = $this->get_field_key( $key );
2643
- $page_name = $this->get_page_name();
2644
- $page_url = $this->get_page_url();
2645
 
2646
  ob_start();
2647
 
2648
- ?>
2649
  <tr valign="top">
2650
  <th scope="row" class="titledesc">
2651
  <?php esc_html_e( 'Facebook page', 'facebook-for-woocommerce' ); ?>
@@ -2676,16 +2785,17 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2676
 
2677
  &mdash;
2678
 
2679
- <?php endif; ?>
2680
  <input
2681
  type="hidden"
2682
  name="<?php echo esc_attr( $key ); ?>"
2683
  id="<?php echo esc_attr( $key ); ?>"
2684
  value="<?php echo esc_attr( $this->get_facebook_page_id() ); ?>"
2685
  />
 
2686
  </td>
2687
  </tr>
2688
- <?php
2689
 
2690
  return ob_get_clean();
2691
  }
@@ -2787,13 +2897,18 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
2787
  ?>
2788
  </table>
2789
  <h3 class="wc-settings-sub-title" id="<?php echo esc_attr( $key ); ?>">
 
2790
  <?php esc_html_e( 'Product sync', 'facebook-for-woocommerce' ); ?>
2791
- <a
2792
- id="woocommerce-facebook-settings-sync-products"
2793
- class="button product-sync-field"
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">
@@ -3170,7 +3285,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3170
  * @param string $page_access_token Facebook page access token
3171
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
3172
  */
3173
- return (string) apply_filters( 'wc_facebook_page_access_token', $this->page_access_token, $this );
3174
  }
3175
 
3176
 
@@ -3258,6 +3373,34 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3258
  }
3259
 
3260
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3261
  /**
3262
  * Gets the Facebook pixel install time in UTC seconds.
3263
  *
@@ -3615,6 +3758,21 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3615
  }
3616
 
3617
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3618
  /**
3619
  * Updates the Facebook pixel install time.
3620
  *
@@ -3661,6 +3819,21 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3661
  }
3662
 
3663
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3664
  /** Conditional methods *******************************************************************************************/
3665
 
3666
 
@@ -3673,7 +3846,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3673
  */
3674
  public function is_configured() {
3675
 
3676
- return $this->get_page_access_token() && $this->get_facebook_page_id();
3677
  }
3678
 
3679
 
@@ -3782,6 +3955,26 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3782
  }
3783
 
3784
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3785
  /**
3786
  * Gets message HTML.
3787
  *
@@ -3875,7 +4068,8 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3875
  */
3876
  public function get_page_name() {
3877
 
3878
- if ( $this->is_configured() ) {
 
3879
  $page_name = $this->fbgraph->get_page_name( $this->get_facebook_page_id(), $this->get_page_access_token() );
3880
  } else {
3881
  $page_name = '';
@@ -4052,7 +4246,14 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
4052
  * Helper function to update FB visibility.
4053
  */
4054
  function update_fb_visibility( $wp_id, $visibility ) {
 
 
 
 
 
 
4055
  $woo_product = new WC_Facebook_Product( $wp_id );
 
4056
  if ( ! $woo_product->exists() ) {
4057
  // This function can be called for non-woo products.
4058
  return;
@@ -4089,23 +4290,30 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
4089
  }
4090
  }
4091
 
4092
- function on_quick_and_bulk_edit_save( $product ) {
 
 
 
 
 
 
 
 
4093
 
4094
  // bail if not a product or product is not enabled for sync
4095
- if ( ! $product instanceof \WC_Product || ! Products::is_sync_enabled_for_product( $product ) ) {
4096
  return;
4097
  }
4098
 
4099
  $wp_id = $product->get_id();
4100
- $visibility = get_post_status( $wp_id ) === 'publish'
4101
- ? self::FB_SHOP_PRODUCT_VISIBLE
4102
- : self::FB_SHOP_PRODUCT_HIDDEN;
4103
- // case 1: new status is 'publish' regardless of old status, sync to FB
4104
  if ( $visibility === self::FB_SHOP_PRODUCT_VISIBLE ) {
 
4105
  $this->on_product_publish( $wp_id );
4106
  } else {
4107
- // case 2: product never publish to FB, new status is not publish
4108
- // case 3: product new status is not publish and published before
4109
  $this->update_fb_visibility( $wp_id, $visibility );
4110
  }
4111
  }
@@ -4365,4 +4573,34 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
4365
  }
4366
 
4367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4368
  }
10
 
11
  use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
12
  use SkyVerge\WooCommerce\Facebook\Products;
13
+ use SkyVerge\WooCommerce\Facebook\Products\Feed;
14
 
15
  if ( ! defined( 'ABSPATH' ) ) {
16
  exit; // Exit if accessed directly
35
  /** @var string the WordPress option name where the feed ID is stored */
36
  const OPTION_FEED_ID = 'wc_facebook_feed_id';
37
 
38
+ /** @var string the WordPress option name where the upload ID is stored */
39
+ const OPTION_UPLOAD_ID = 'wc_facebook_upload_id';
40
+
41
  /** @var string the WordPress option name where the JS SDK version is stored */
42
  const OPTION_JS_SDK_VERSION = 'wc_facebook_js_sdk_version';
43
 
105
  /** @var string|null the configured feed ID */
106
  public $feed_id;
107
 
108
+ /** @var string|null the configured upload ID */
109
+ private $upload_id;
110
+
111
  /** @var string|null the configured pixel install time */
112
  public $pixel_install_time;
113
 
114
  /** @var string|null the configured JS SDK version */
115
  private $js_sdk_version;
116
 
117
+ /** @var bool|null whether the feed has been migrated from FBE 1 to FBE 1.5 */
118
+ private $feed_migrated;
119
+
120
 
121
  /** Legacy properties *********************************************************************************************/
122
 
334
  self::FB_PRIORITY_MID
335
  );
336
 
337
+ // on_product_save() must run with priority larger than 20 to make sure WooCommerce has a chance to save the submitted product information
338
+ add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 40 );
339
+
340
+ // don't duplicate product FBID meta
341
+ add_filter( 'woocommerce_duplicate_product_exclude_meta', [ $this, 'fb_duplicate_product_reset_meta' ] );
342
+
343
  // Only load product processing hooks if we have completed setup.
344
  if ( $this->get_page_access_token() && $this->get_product_catalog_id() ) {
345
 
 
 
 
346
  add_action(
347
  'woocommerce_product_quick_edit_save',
348
  array( $this, 'on_quick_and_bulk_edit_save' ),
357
  1 // Args passed to on_quick_and_bulk_edit_save ('product')
358
  );
359
 
360
+ add_action( 'trashed_post', [ $this, 'on_product_trash' ] );
361
+
362
  add_action(
363
  'before_delete_post',
364
  array( $this, 'on_product_delete' ),
390
  array( $this, 'ajax_delete_fb_product' )
391
  );
392
 
 
 
 
 
 
393
  add_action(
394
  'pmxi_after_xml_import',
395
  array( $this, 'wp_all_import_compat' )
417
  // Must be outside of admin for cron to schedule correctly.
418
  add_action( 'sync_all_fb_products_using_feed', [ $this, 'handle_scheduled_resync_action' ], self::FB_PRIORITY_MID );
419
 
420
+ // handle the special background feed generation action
421
+ add_action( 'wc_facebook_generate_product_catalog_feed', [ $this, 'handle_generate_product_catalog_feed' ] );
422
+
423
  if ( $this->get_facebook_pixel_id() ) {
424
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $this->is_advanced_matching_enabled() );
425
  $this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info );
633
  }
634
 
635
  ?>
636
+ <?php /* ?>
637
  <?php echo esc_html__( 'Visible:', 'facebook-for-woocommerce' ); ?>
638
  <input name="<?php echo esc_attr( Products::VISIBILITY_META_KEY ); ?>"
639
  type="checkbox"
641
  <?php echo checked( ! $woo_product->woo_product instanceof \WC_Product || Products::is_product_visible( $woo_product->woo_product ) ); ?>/>
642
 
643
  <p/>
644
+ <?php */ ?>
645
  <input name="is_product_page" type="hidden" value="1"/>
646
 
647
  <p/>
751
  },
752
  feed: {
753
  totalVisibleProducts: '<?php echo esc_js( $this->get_product_count() ); ?>',
754
+ hasClientSideFeedUpload: '<?php echo esc_js( ! ! $this->get_feed_id() ); ?>',
755
+ enabled: true,
756
+ format: 'csv'
757
  },
758
  feedPrepared: {
759
+ feedUrl: '<?php echo esc_url_raw( Feed::get_feed_data_url() ); ?>',
760
  feedPingUrl: '',
761
+ feedMigrated: <?php echo $this->is_feed_migrated() ? 'true' : 'false'; ?>,
762
  samples: <?php echo $this->get_sample_product_feed(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
763
  },
 
764
  excludedCategoryIDs: <?php echo json_encode( $this->get_excluded_product_category_ids() ); ?>,
765
  excludedTagIDs: <?php echo json_encode( $this->get_excluded_product_tag_ids() ); ?>,
766
  messengerGreetingMaxCharacters: <?php echo esc_js( $this->get_messenger_greeting_max_characters() ); ?>
814
 
815
  // phpcs:ignore WordPress.Security.NonceVerification.Missing
816
  $sync_enabled = ! empty( $_POST['fb_sync_enabled'] );
817
+ $is_visible = ! empty( $_POST[ Products::VISIBILITY_META_KEY ] );
818
 
819
  if ( ! $product->is_type( 'variable' ) ) {
820
 
830
  }
831
  }
832
 
833
+ // do not attempt to update product visibility during FBE 1.5: the Visible setting was removed so it always seems as if the visibility had been disabled
834
+ // $this->update_fb_visibility( $product->get_id(), $is_visible ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN );
835
 
836
+ if ( $sync_enabled && $this->get_page_access_token() && $this->get_product_catalog_id() ) {
837
 
838
  switch ( $product->get_type() ) {
839
 
854
  break;
855
  }
856
  }
857
+
858
+ $this->enable_product_sync_delay_admin_notice();
859
  }
860
 
861
 
891
  }
892
 
893
 
894
+ /**
895
+ * Enables product sync delay notice when a post is moved to the trash.
896
+ *
897
+ * @internal
898
+ *
899
+ * @since 1.11.0
900
+ *
901
+ * @param int $post_id the post ID
902
+ */
903
+ public function on_product_trash( $post_id ) {
904
+
905
+ $product = wc_get_product( $post_id );
906
+
907
+ if ( $product instanceof \WC_Product ) {
908
+ $this->enable_product_sync_delay_admin_notice();
909
+ }
910
+ }
911
+
912
+
913
  /**
914
  * Deletes a product from Facebook.
915
  *
926
  }
927
 
928
  // skip if not enabled for sync
929
+ if ( ! $woo_product->woo_product instanceof \WC_Product || ! Products::product_should_be_synced( $woo_product->woo_product ) ) {
930
  return;
931
  }
932
 
955
  $pg_result = $this->fbgraph->delete_product_group( $fb_product_group_id );
956
  WC_Facebookcommerce_Utils::log( $pg_result );
957
  }
958
+
959
+ $this->enable_product_sync_delay_admin_notice();
960
  }
961
 
962
+
963
  /**
964
+ * Updates Facebook Visibility upon trashing and restore.
965
+ *
966
+ * @internal
967
+ *
968
+ * @param string $new_status
969
+ * @param string $old_status
970
+ * @param \WP_post $post
971
  */
972
+ public function fb_change_product_published_status( $new_status, $old_status, $post ) {
973
  global $post;
974
 
975
  if ( ! $post ) {
981
  $product = wc_get_product( $post->ID );
982
 
983
  // bail if this product isn't enabled for sync
984
+ if ( ! $product instanceof \WC_Product || ! Products::product_should_be_synced( $product ) ) {
985
  return;
986
  }
987
 
988
+ // change from publish status -> unpublish status (e.g. trash, draft, etc.)
989
  // change from trash status -> publish status
990
  // no need to update for change from trash <-> unpublish status
991
+ if ( ( $old_status === 'publish' && $new_status !== 'publish' ) || ( $old_status === 'trash' && $new_status === 'publish' ) ) {
 
992
  $this->update_fb_visibility( $post->ID, $visibility );
993
  }
994
  }
995
 
996
+
997
  /**
998
  * Generic function for use with any product publishing.
999
  *
1004
  */
1005
  public function on_product_publish( $wp_id ) {
1006
 
1007
+ // bail if we don't have a page access token or a catalog ID configured
1008
+ if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
1009
+ return;
1010
+ }
1011
+
1012
+ if ( get_post_status( $wp_id ) !== 'publish' ) {
1013
  return;
1014
  }
1015
 
1016
+ $woo_product = new WC_Facebook_Product( $wp_id );
1017
 
1018
  // skip if not enabled for sync
1019
+ if ( ! $woo_product->woo_product instanceof \WC_Product || ! Products::product_should_be_synced( $woo_product->woo_product ) ) {
1020
  return;
1021
  }
1022
 
1027
  }
1028
  }
1029
 
1030
+
1031
  /**
1032
  * If the user has opt-in to remove products that are out of stock,
1033
  * this function will delete the product from FB Page as well.
1125
  }
1126
 
1127
  // skip if not enabled for sync
1128
+ if ( ! $woo_product->woo_product instanceof \WC_Product || ! Products::product_should_be_synced( $woo_product->woo_product ) ) {
1129
  return;
1130
  }
1131
 
1264
  $fb_product_group_id
1265
  );
1266
 
1267
+ /** TODO: restore when adopting FBE 2.0
1268
  $this->display_success_message(
1269
  'Created product group <a href="https://facebook.com/' .
1270
  $fb_product_group_id . '" target="_blank">' .
1271
  $fb_product_group_id . '</a> on Facebook.'
1272
  );
1273
+ */
1274
 
1275
  return $fb_product_group_id;
1276
  }
1305
  $fb_product_item_id
1306
  );
1307
 
1308
+ /** TODO: restore when adopting FBE 2.0
1309
  $this->display_success_message(
1310
  'Created product item <a href="https://facebook.com/' .
1311
  $fb_product_item_id . '" target="_blank">' .
1312
  $fb_product_item_id . '</a> on Facebook.'
1313
  );
1314
+ */
1315
 
1316
  return $fb_product_item_id;
1317
  }
1358
  )
1359
  );
1360
 
1361
+ /** TODO: restore when adopting FBE 2.0
1362
  if ( $result ) {
1363
  $this->display_success_message(
1364
  'Updated product group <a href="https://facebook.com/' .
1366
  '</a> on Facebook.'
1367
  );
1368
  }
1369
+ */
1370
  }
1371
 
1372
  /**
1387
  )
1388
  );
1389
 
1390
+ /** TODO: restore when adopting FBE 2.0
1391
  if ( $result ) {
1392
  $this->display_success_message(
1393
  'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
1394
  '" target="_blank">' . $fb_product_item_id . '</a> on Facebook.'
1395
  );
1396
  }
1397
+ */
1398
  }
1399
 
1400
  /**
1414
  return;
1415
  }
1416
 
1417
+ \WC_Facebookcommerce_Utils::log( 'Saving settings via AJAX' );
1418
+
1419
+ // listen for a feed migrated event for FBE 1.5
1420
+ if ( isset( $_REQUEST['feed_migrated'] ) ) {
1421
+
1422
+ $this->set_feed_migrated( wc_string_to_bool( $_REQUEST['feed_migrated'] ) );
1423
+
1424
+ // don't save anything else if already connected
1425
+ if ( $this->get_external_merchant_settings_id() ) {
1426
+ wp_send_json_success();
1427
+ }
1428
+ }
1429
+
1430
  if ( isset( $_REQUEST['api_key'] ) ) {
1431
 
1432
  $api_key = sanitize_text_field( wp_unslash( $_REQUEST['api_key'] ) );
1436
  }
1437
  }
1438
 
1439
+ if ( isset( $_REQUEST['external_merchant_settings_id'] ) ) {
1440
+
1441
+ $external_merchant_settings_id = sanitize_text_field( wp_unslash( $_REQUEST['external_merchant_settings_id'] ) );
1442
+
1443
+ if ( is_numeric( $external_merchant_settings_id ) ) {
1444
+ $this->update_external_merchant_settings_id( $external_merchant_settings_id );
1445
+ }
1446
+ }
1447
+
1448
  if ( isset( $_REQUEST['product_catalog_id'] ) ) {
1449
 
1450
  $product_catalog_id = sanitize_text_field( wp_unslash( $_REQUEST['product_catalog_id'] ) );
1466
  if ( ctype_digit( $pixel_id ) ) {
1467
 
1468
  // to prevent race conditions with pixel-only settings, only save a pixel if we already have an access token
1469
+ if ( $this->get_external_merchant_settings_id() ) {
1470
 
1471
  if ( $this->get_facebook_pixel_id() !== $pixel_id ) {
1472
  $this->update_pixel_install_time( time() );
1477
  } else {
1478
 
1479
  WC_Facebookcommerce_Utils::log( 'Got pixel-only settings, doing nothing' );
 
1480
 
1481
+ wp_send_json_error();
1482
  }
1483
  }
1484
  }
1496
  }
1497
  }
1498
 
 
 
 
 
 
 
 
 
 
1499
  if ( isset( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) {
1500
  $this->settings[ self::SETTING_ENABLE_MESSENGER ] = wc_bool_to_string( wc_clean( wp_unslash( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) );
1501
  }
1520
  update_option( $this->get_option_key(), apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings ) );
1521
 
1522
  WC_Facebookcommerce_Utils::log( 'Settings saved!' );
 
1523
 
1524
+ wp_send_json_success();
1525
  }
1526
 
1527
  /**
1557
  $this->init_settings();
1558
  $this->update_page_access_token( '' );
1559
  $this->update_product_catalog_id( '' );
1560
+ $this->set_feed_migrated( false );
1561
 
1562
  $this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = '';
1563
  $this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = 'no';
1571
  $this->update_external_merchant_settings_id( '' );
1572
  $this->update_pixel_install_time( 0 );
1573
  $this->update_feed_id( '' );
1574
+ $this->update_upload_id( '' );
1575
  $this->settings['upload_end_time'] = '';
1576
 
1577
  WC_Facebookcommerce_Pixel::set_pixel_id( 0 );
1602
  }
1603
 
1604
  /**
1605
+ * Checks the feed upload status (FBE v1.0).
1606
+ *
1607
+ * @internal
1608
+ */
1609
+ public function ajax_check_feed_upload_status() {
1610
+ $response = array(
1611
+ 'connected' => true,
1612
+ 'status' => 'complete',
1613
+ );
1614
+ printf( json_encode( $response ) );
1615
+ wp_die();
1616
+ }
1617
+
1618
+
1619
+ /**
1620
+ * Check Feed Upload Status (FBE v2.0)
1621
+ * TODO: When migrating to FBE v2.0, remove above function and rename
1622
+ * below function to ajax_check_feed_upload_status()
1623
  **/
1624
+ public function ajax_check_feed_upload_status_v2() {
1625
+
1626
+ \WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
1627
+
1628
  check_ajax_referer( 'wc_facebook_settings_jsx' );
1629
+
1630
  if ( $this->get_page_access_token() ) {
1631
+
1632
+ $response = [
1633
  'connected' => true,
1634
  'status' => 'in progress',
1635
+ ];
1636
+
1637
+ if ( ! empty( $this->get_upload_id() ) ) {
1638
+
1639
  if ( ! isset( $this->fbproductfeed ) ) {
1640
+
1641
  if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
1642
  include_once 'includes/fbproductfeed.php';
1643
  }
1644
+
1645
+ $this->fbproductfeed = new \WC_Facebook_Product_Feed(
1646
  $this->get_product_catalog_id(),
1647
  $this->fbgraph
1648
  );
1649
  }
1650
+
1651
  $status = $this->fbproductfeed->is_upload_complete( $this->settings );
1652
 
1653
  $response['status'] = $status;
1654
+
1655
  } else {
1656
+
1657
+ $response = [
1658
  'connected' => true,
1659
  'status' => 'error',
1660
+ ];
1661
  }
1662
+
1663
+ if ( 'complete' === $response['status'] ) {
1664
+
1665
  update_option(
1666
  $this->get_option_key(),
1667
  apply_filters(
1670
  )
1671
  );
1672
  }
1673
+
1674
  } else {
1675
+
1676
+ $response = [ 'connected' => false ];
 
1677
  }
1678
+
1679
  printf( json_encode( $response ) );
1680
  wp_die();
1681
  }
1915
  }
1916
 
1917
  // check required fields
1918
+ if ( ! $this->is_configured() ) {
1919
 
1920
  $message = sprintf(
1921
  /* translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag */
2421
  }
2422
 
2423
  $this->update_feed_id( $this->fbproductfeed->feed_id );
2424
+ $this->update_upload_id( $this->fbproductfeed->upload_id );
 
2425
 
2426
  update_option(
2427
  $this->get_option_key(),
2551
  /** @see \WC_Facebookcommerce_Integration::generate_product_sync_title_html() */
2552
  [
2553
  'type' => 'product_sync_title',
2554
+ 'title' => __( 'Product sync', 'facebook-for-woocommerce' ),
2555
  ],
2556
 
2557
  self::SETTING_ENABLE_PRODUCT_SYNC => [
2602
 
2603
  /** @see \WC_Facebookcommerce_Integration::generate_resync_schedule_html() */
2604
  /** @see \WC_Facebookcommerce_Integration::validate_resync_schedule_field() */
2605
+ //self::SETTING_SCHEDULED_RESYNC_OFFSET => [
2606
+ // 'title' => __( 'Force daily resync at', 'facebook-for-woocommerce' ),
2607
+ // 'class' => 'product-sync-field resync-schedule-fieldset',
2608
+ // 'type' => 'resync_schedule',
2609
+ //],
2610
 
2611
  [
2612
  'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
2706
  onclick="facebookConfig();"
2707
  ><?php esc_html_e( 'Manage connection', 'facebook-for-woocommerce' ); ?></a>
2708
  </h3>
2709
+ <?php // if ( empty( $this->get_page_name() ) ) : ?>
2710
+ <?php
2711
+ /**
2712
  <div id="connection-message-invalid">
2713
  <p style="color: #DC3232;">
2714
  <?php esc_html_e( 'Your connection has expired.', 'facebook-for-woocommerce' ); ?>
2725
  </strong>
2726
  </p>
2727
  </div>
2728
+ */
2729
+ ?>
2730
+ <?php // endif; ?>
2731
  <table class="form-table">
2732
  <?php
2733
 
2749
  protected function generate_facebook_page_name_html( $key, array $args = [] ) {
2750
 
2751
  $key = $this->get_field_key( $key );
2752
+ // $page_name = $this->get_page_name();
2753
+ // $page_url = $this->get_page_url();
2754
 
2755
  ob_start();
2756
 
2757
+ /*?>
2758
  <tr valign="top">
2759
  <th scope="row" class="titledesc">
2760
  <?php esc_html_e( 'Facebook page', 'facebook-for-woocommerce' ); ?>
2785
 
2786
  &mdash;
2787
 
2788
+ <?php endif;*/ ?>
2789
  <input
2790
  type="hidden"
2791
  name="<?php echo esc_attr( $key ); ?>"
2792
  id="<?php echo esc_attr( $key ); ?>"
2793
  value="<?php echo esc_attr( $this->get_facebook_page_id() ); ?>"
2794
  />
2795
+ <?php /*
2796
  </td>
2797
  </tr>
2798
+ <?php */
2799
 
2800
  return ob_get_clean();
2801
  }
2897
  ?>
2898
  </table>
2899
  <h3 class="wc-settings-sub-title" id="<?php echo esc_attr( $key ); ?>">
2900
+
2901
  <?php esc_html_e( 'Product sync', 'facebook-for-woocommerce' ); ?>
2902
+
2903
+ <?php if ( $this->get_page_name() ) : ?>
2904
+ <a
2905
+ id="woocommerce-facebook-settings-sync-products"
2906
+ class="button product-sync-field"
2907
+ href="#"
2908
+ style="vertical-align: middle; margin-left: 20px;"
2909
+ ><?php esc_html_e( 'Sync products', 'facebook-for-woocommerce' ); ?></a>
2910
+ <?php endif; ?>
2911
+
2912
  </h3>
2913
  <div><p id="sync_progress" style="display: none"></p></div>
2914
  <table class="form-table">
3285
  * @param string $page_access_token Facebook page access token
3286
  * @param \WC_Facebookcommerce_Integration $integration the integration instance
3287
  */
3288
+ return (string) apply_filters( 'wc_facebook_page_access_token', ! $this->is_feed_migrated() ? $this->page_access_token : '', $this );
3289
  }
3290
 
3291
 
3373
  }
3374
 
3375
 
3376
+ /***
3377
+ * Gets the Facebook Upload ID.
3378
+ *
3379
+ * @since 1.11.0
3380
+ *
3381
+ * @return string
3382
+ */
3383
+ public function get_upload_id() {
3384
+
3385
+ if ( ! is_string( $this->upload_id ) ) {
3386
+
3387
+ $value = get_option( self::OPTION_UPLOAD_ID, '' );
3388
+
3389
+ $this->upload_id = is_string( $value ) ? $value : '';
3390
+ }
3391
+
3392
+ /**
3393
+ * Filters the Facebook upload ID.
3394
+ *
3395
+ * @since 1.11.0
3396
+ *
3397
+ * @param string $upload_id Facebook upload ID
3398
+ * @param \WC_Facebookcommerce_Integration $integration the integration instance
3399
+ */
3400
+ return (string) apply_filters( 'wc_facebook_upload_id', $this->upload_id, $this );
3401
+ }
3402
+
3403
+
3404
  /**
3405
  * Gets the Facebook pixel install time in UTC seconds.
3406
  *
3758
  }
3759
 
3760
 
3761
+ /**
3762
+ * Updates the Facebook upload ID.
3763
+ *
3764
+ * @since 1.11.0
3765
+ *
3766
+ * @param string $value upload ID value
3767
+ */
3768
+ public function update_upload_id( $value ) {
3769
+
3770
+ $this->upload_id = $this->sanitize_facebook_credential( $value );
3771
+
3772
+ update_option( self::OPTION_UPLOAD_ID, $this->upload_id );
3773
+ }
3774
+
3775
+
3776
  /**
3777
  * Updates the Facebook pixel install time.
3778
  *
3819
  }
3820
 
3821
 
3822
+ /**
3823
+ * Sets whether the feed has been migrated from FBE 1 to FBE 1.5.
3824
+ *
3825
+ * @since 1.11.0
3826
+ *
3827
+ * @param bool $is_migrated whether the feed has been migrated from FBE 1 to FBE 1.5
3828
+ */
3829
+ private function set_feed_migrated( $is_migrated ) {
3830
+
3831
+ $this->feed_migrated = (bool) $is_migrated;
3832
+
3833
+ update_option( 'wc_facebook_feed_migrated', wc_bool_to_string( $this->feed_migrated ) );
3834
+ }
3835
+
3836
+
3837
  /** Conditional methods *******************************************************************************************/
3838
 
3839
 
3846
  */
3847
  public function is_configured() {
3848
 
3849
+ return (bool) $this->get_external_merchant_settings_id();
3850
  }
3851
 
3852
 
3955
  }
3956
 
3957
 
3958
+ /***
3959
+ * Determines if the feed has been migrated from FBE 1 to FBE 1.5
3960
+ *
3961
+ * @since 1.11.0
3962
+ *
3963
+ * @return bool
3964
+ */
3965
+ public function is_feed_migrated() {
3966
+
3967
+ if ( ! is_bool( $this->feed_migrated ) ) {
3968
+
3969
+ $value = get_option( 'wc_facebook_feed_migrated', 'no' );
3970
+
3971
+ $this->feed_migrated = wc_string_to_bool( $value );
3972
+ }
3973
+
3974
+ return $this->feed_migrated;
3975
+ }
3976
+
3977
+
3978
  /**
3979
  * Gets message HTML.
3980
  *
4068
  */
4069
  public function get_page_name() {
4070
 
4071
+ // TODO: replace with `if ( $this->is_configured() ) {` when access tokens become available again {WV 2020-03-31}
4072
+ if ( $this->get_facebook_page_id() && $this->get_page_access_token() ) {
4073
  $page_name = $this->fbgraph->get_page_name( $this->get_facebook_page_id(), $this->get_page_access_token() );
4074
  } else {
4075
  $page_name = '';
4246
  * Helper function to update FB visibility.
4247
  */
4248
  function update_fb_visibility( $wp_id, $visibility ) {
4249
+
4250
+ // bail if we don't have a page access token or a catalog ID configured
4251
+ if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
4252
+ return;
4253
+ }
4254
+
4255
  $woo_product = new WC_Facebook_Product( $wp_id );
4256
+
4257
  if ( ! $woo_product->exists() ) {
4258
  // This function can be called for non-woo products.
4259
  return;
4290
  }
4291
  }
4292
 
4293
+
4294
+ /**
4295
+ * Sync product upon quick or bulk edit save action.
4296
+ *
4297
+ * @internal
4298
+ *
4299
+ * @param \WC_Product $product product object
4300
+ */
4301
+ public function on_quick_and_bulk_edit_save( $product ) {
4302
 
4303
  // bail if not a product or product is not enabled for sync
4304
+ if ( ! $product instanceof \WC_Product || ! Products::product_should_be_synced( $product ) ) {
4305
  return;
4306
  }
4307
 
4308
  $wp_id = $product->get_id();
4309
+ $visibility = get_post_status( $wp_id ) === 'publish' ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN;
4310
+
 
 
4311
  if ( $visibility === self::FB_SHOP_PRODUCT_VISIBLE ) {
4312
+ // - new status is 'publish' regardless of old status, sync to Facebook
4313
  $this->on_product_publish( $wp_id );
4314
  } else {
4315
+ // - product never published to Facebook, new status is not publish
4316
+ // - product new status is not publish but may have been published before
4317
  $this->update_fb_visibility( $wp_id, $visibility );
4318
  }
4319
  }
4573
  }
4574
 
4575
 
4576
+ /**
4577
+ * Handles the schedule feed generation action, triggered by the REST API.
4578
+ *
4579
+ * @since 1.11.0
4580
+ */
4581
+ public function handle_generate_product_catalog_feed() {
4582
+
4583
+ $feed_handler = new WC_Facebook_Product_Feed();
4584
+
4585
+ try {
4586
+
4587
+ $feed_handler->generate_feed();
4588
+
4589
+ } catch ( \Exception $exception ) {
4590
+
4591
+ WC_Facebookcommerce_Utils::log( 'Error generating product catalog feed. ' . $exception->getMessage() );
4592
+ }
4593
+ }
4594
+
4595
+ /**
4596
+ * Enables product sync delay admin notice.
4597
+ *
4598
+ * @since 1.11.0
4599
+ */
4600
+ private function enable_product_sync_delay_admin_notice() {
4601
+
4602
+ set_transient( 'wc_' . facebook_for_woocommerce()->get_id() . '_show_product_sync_delay_notice_' . get_current_user_id(), true, MINUTE_IN_SECONDS );
4603
+ }
4604
+
4605
+
4606
  }
facebook-for-woocommerce.php CHANGED
@@ -10,11 +10,10 @@
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
17
- * WC tested up to: 4.0.0
18
  *
19
  * @package FacebookCommerce
20
  */
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.11.0
 
14
  * Text Domain: facebook-for-woocommerce
15
  * WC requires at least: 3.5.0
16
+ * WC tested up to: 4.0.1
17
  *
18
  * @package FacebookCommerce
19
  */
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.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"
@@ -13,48 +13,64 @@ msgstr ""
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  #. Plugin Name of the plugin/theme
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,43 +78,43 @@ 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 "
@@ -106,19 +122,19 @@ msgid ""
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,160 +143,144 @@ 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,62 +291,62 @@ msgstr ""
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 ""
326
 
327
- #: includes/AJAX.php:76
328
  msgid "Hide Product"
329
  msgstr ""
330
 
331
- #: includes/AJAX.php:80
332
  msgid "Do Not Hide Product"
333
  msgstr ""
334
 
335
- #: includes/AJAX.php:86
336
  msgid ""
337
  "This product will no longer be updated in your Facebook catalog. Would you "
338
  "like to hide this product from your Facebook shop?"
339
  msgstr ""
340
 
341
- #: includes/AJAX.php:125 includes/AJAX.php:220
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
 
349
- #: includes/AJAX.php:138
350
  #. translators: Placeholder %s - <br/> tag
351
  msgid ""
352
  "This product belongs to a category or tag that is excluded from the "
@@ -355,21 +355,21 @@ msgid ""
355
  "or click Cancel and update the product's category / tag assignments."
356
  msgstr ""
357
 
358
- #: includes/AJAX.php:179
359
  msgid "Hide Products"
360
  msgstr ""
361
 
362
- #: includes/AJAX.php:183
363
  msgid "Do Not Hide Products"
364
  msgstr ""
365
 
366
- #: includes/AJAX.php:189
367
  msgid ""
368
  "The selected products will no longer be updated in your Facebook catalog. "
369
  "Would you like to hide these products from your Facebook shop?"
370
  msgstr ""
371
 
372
- #: includes/AJAX.php:231
373
  msgid ""
374
  "One or more of the selected products belongs to a category or tag that is "
375
  "excluded from the Facebook catalog sync. To sync these products to "
@@ -377,15 +377,15 @@ msgid ""
377
  "settings."
378
  msgstr ""
379
 
380
- #: includes/AJAX.php:284
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,7 +394,7 @@ msgid ""
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."
@@ -402,16 +402,16 @@ 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"
@@ -421,112 +421,108 @@ msgid ""
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,61 +531,86 @@ 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
 
@@ -609,6 +630,14 @@ msgstr ""
609
  msgid "Dismiss"
610
  msgstr ""
611
 
 
 
 
 
 
 
 
 
612
  #: includes/fbwpml.php:105
613
  msgid "Facebook Visibility"
614
  msgstr ""
@@ -646,7 +675,7 @@ 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 ""
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.11.0\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
+ "POT-Creation-Date: 2020-04-23 23:00:28+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
15
 
16
+ #: class-wc-facebookcommerce.php:142
17
+ #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
18
+ #. <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag
19
+ msgid ""
20
+ "%1$sHeads up!%2$s Facebook for WooCommerce is migrating to a more secure "
21
+ "connection experience. Please %3$sclick here%4$s and go to %1$sAdvanced "
22
+ "Options%2$s > %1$sReconnect Catalog%2$s to securely reconnect. %5$sLearn "
23
+ "more%6$s."
24
+ msgstr ""
25
+
26
+ #: class-wc-facebookcommerce.php:152
27
+ #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
28
+ #. <a> tag, %4$s - </a> tag, %5$s - <a> tag, %6$s - </a> tag
29
+ msgid ""
30
+ "%1$sHeads up!%2$s Facebook for WooCommerce is migrating to a more secure "
31
+ "connection experience. Please %3$sclick here%4$s and go to %1$sManage "
32
+ "Connection%2$s > %1$sAdvanced Options%2$s > %1$sReconnect Catalog%2$s to "
33
+ "securely reconnect. %5$sLearn more%6$s."
34
+ msgstr ""
35
+
36
  #. Plugin Name of the plugin/theme
37
  msgid "Facebook for WooCommerce"
38
  msgstr ""
39
 
40
+ #: facebook-commerce.php:198
41
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
42
  msgstr ""
43
 
44
+ #: facebook-commerce.php:598
45
  msgid "Facebook ID:"
46
  msgstr ""
47
 
48
+ #: facebook-commerce.php:609
49
  msgid "Variant IDs:"
50
  msgstr ""
51
 
52
+ #: facebook-commerce.php:649
 
 
 
 
53
  msgid "Reset Facebook metadata"
54
  msgstr ""
55
 
56
+ #: facebook-commerce.php:654
57
  msgid "Delete product(s) on Facebook"
58
  msgstr ""
59
 
60
+ #: facebook-commerce.php:662
61
  msgid "This product is not yet synced to Facebook."
62
  msgstr ""
63
 
64
+ #: facebook-commerce.php:1340
65
  msgid "Nothing to update for product group for %1$s"
66
  msgstr ""
67
 
68
+ #: facebook-commerce.php:1788
69
  #. translators: Placeholders %1$s - original error message from Facebook API
70
  msgid "There was an issue connecting to the Facebook API: %1$s"
71
  msgstr ""
72
 
73
+ #: facebook-commerce.php:1922
74
  #. translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing
75
  #. strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag
76
  msgid ""
78
  "configuration, %3$scomplete the setup steps%4$s."
79
  msgstr ""
80
 
81
+ #: facebook-commerce.php:1941
82
  #. translators: Placeholders %1$s - WooCommerce version
83
  msgid ""
84
  "Facebook product sync may not work correctly in WooCommerce version %1$s. "
85
  "Please upgrade to WooCommerce 3."
86
  msgstr ""
87
 
88
+ #: facebook-commerce.php:2160
89
  msgid "Your connection has expired."
90
  msgstr ""
91
 
92
+ #: facebook-commerce.php:2160
93
  msgid ""
94
  "Please click Manage connection > Advanced Options > Update Token to refresh "
95
  "your connection to Facebook."
96
  msgstr ""
97
 
98
+ #: facebook-commerce.php:2167
99
  #. translators: Placeholders %s - error message
100
  msgid "There was an error trying to sync the products to Facebook. %s"
101
  msgstr ""
102
 
103
+ #: facebook-commerce.php:2190 facebook-commerce.php:2369
104
  msgid "Product sync is disabled."
105
  msgstr ""
106
 
107
+ #: facebook-commerce.php:2197 facebook-commerce.php:2376
108
  msgid "The page access token or product catalog ID are missing."
109
  msgstr ""
110
 
111
+ #: facebook-commerce.php:2220
112
  msgid ""
113
  "A product sync is in progress. Please wait until the sync finishes before "
114
  "starting a new one."
115
  msgstr ""
116
 
117
+ #: facebook-commerce.php:2232 facebook-commerce.php:2390
118
  msgid ""
119
  "We've detected that your Facebook Product Catalog is no longer valid. This "
120
  "may happen if it was deleted, but could also be a temporary error. If the "
122
  "and setup the plugin again."
123
  msgstr ""
124
 
125
+ #: facebook-commerce.php:2420
126
  msgid "We couldn't create the feed or upload the product information."
127
  msgstr ""
128
 
129
+ #: facebook-commerce.php:2511
130
  msgid "Hi! We're here to answer any questions you may have."
131
  msgstr ""
132
 
133
+ #: facebook-commerce.php:2534
134
  msgid "Use Advanced Matching"
135
  msgstr ""
136
 
137
+ #: facebook-commerce.php:2537
138
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
139
  #. </a> HTML link tag
140
  msgid ""
143
  "number). %1$sLearn more%2$s."
144
  msgstr ""
145
 
146
+ #: facebook-commerce.php:2554 facebook-commerce.php:2901
147
+ msgid "Product sync"
148
+ msgstr ""
149
+
150
+ #: facebook-commerce.php:2558
151
  msgid "Enable product sync"
152
  msgstr ""
153
 
154
+ #: facebook-commerce.php:2566
155
  msgid "Exclude categories from sync"
156
  msgstr ""
157
 
158
+ #: facebook-commerce.php:2570
159
  msgid "Products in one or more of these categories will not sync to Facebook."
160
  msgstr ""
161
 
162
+ #: facebook-commerce.php:2574
163
  msgid "Search for a product category&hellip;"
164
  msgstr ""
165
 
166
+ #: facebook-commerce.php:2579
167
  msgid "Exclude tags from sync"
168
  msgstr ""
169
 
170
+ #: facebook-commerce.php:2583
171
  msgid "Products with one or more of these tags will not sync to Facebook."
172
  msgstr ""
173
 
174
+ #: facebook-commerce.php:2587
175
  msgid "Search for a product tag&hellip;"
176
  msgstr ""
177
 
178
+ #: facebook-commerce.php:2592
179
  msgid "Product description sync"
180
  msgstr ""
181
 
182
+ #: facebook-commerce.php:2595
183
  msgid "Choose which product description to display in the Facebook catalog."
184
  msgstr ""
185
 
186
+ #: facebook-commerce.php:2598
187
  msgid "Standard description"
188
  msgstr ""
189
 
190
+ #: facebook-commerce.php:2599
191
  msgid "Short description"
192
  msgstr ""
193
 
194
+ #: facebook-commerce.php:2612
 
 
 
 
195
  msgid "Messenger"
196
  msgstr ""
197
 
198
+ #: facebook-commerce.php:2617
199
  msgid "Enable Messenger"
200
  msgstr ""
201
 
202
+ #: facebook-commerce.php:2621
203
  msgid "Enable and customize Facebook Messenger on your store."
204
  msgstr ""
205
 
206
+ #: facebook-commerce.php:2626
207
  msgid "Language"
208
  msgstr ""
209
 
210
+ #: facebook-commerce.php:2639
211
  msgid "Greeting"
212
  msgstr ""
213
 
214
+ #: facebook-commerce.php:2651
215
  msgid "Colors"
216
  msgstr ""
217
 
218
+ #: facebook-commerce.php:2662
219
  msgid "Debug"
220
  msgstr ""
221
 
222
+ #: facebook-commerce.php:2667
223
  msgid "Enable debug mode"
224
  msgstr ""
225
 
226
+ #: facebook-commerce.php:2669
227
  msgid "Log plugin events for debugging"
228
  msgstr ""
229
 
230
+ #: facebook-commerce.php:2670
231
  msgid "Only enable this if you are experiencing problems with the plugin."
232
  msgstr ""
233
 
234
+ #: facebook-commerce.php:2700
235
  msgid "Connection"
236
  msgstr ""
237
 
238
+ #: facebook-commerce.php:2707
239
  msgid "Manage connection"
240
  msgstr ""
241
 
242
+ #: facebook-commerce.php:2825
 
 
 
 
 
 
 
 
 
 
 
 
243
  msgid "Pixel"
244
  msgstr ""
245
 
246
+ #: facebook-commerce.php:2871
247
  msgid "Create ad"
248
  msgstr ""
249
 
250
+ #: facebook-commerce.php:2909
 
 
 
 
251
  msgid "Sync products"
252
  msgstr ""
253
 
254
+ #: facebook-commerce.php:3111
255
  msgid "am"
256
  msgstr ""
257
 
258
+ #: facebook-commerce.php:3116
259
  msgid "pm"
260
  msgstr ""
261
 
262
+ #: facebook-commerce.php:3236
263
  msgid "The greeting hasn't been updated."
264
  msgstr ""
265
 
266
+ #: facebook-commerce.php:3255
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:4008
272
  msgid "Facebook for WooCommerce error:"
273
  msgstr ""
274
 
275
+ #: facebook-commerce.php:4114
276
  msgid "Get started with Messenger Customer Chat"
277
  msgstr ""
278
 
279
+ #: facebook-commerce.php:4115
280
  msgid "Get started with Instagram Shopping"
281
  msgstr ""
282
 
283
+ #: facebook-commerce.php:4165
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:4174
295
  msgid "Control how WooCommerce integrates with your Facebook store."
296
  msgstr ""
297
 
298
+ #: facebook-commerce.php:4188
299
  msgid "Grow your business on Facebook"
300
  msgstr ""
301
 
302
+ #: facebook-commerce.php:4191
303
  msgid "Use this WooCommerce and Facebook integration to:"
304
  msgstr ""
305
 
306
+ #: facebook-commerce.php:4195
307
  msgid "Easily install a tracking pixel"
308
  msgstr ""
309
 
310
+ #: facebook-commerce.php:4196
311
  msgid "Upload your products and create a shop"
312
  msgstr ""
313
 
314
+ #: facebook-commerce.php:4197
315
  msgid "Create dynamic ads with your products and pixel"
316
  msgstr ""
317
 
318
+ #: facebook-commerce.php:4205
319
  msgid "Get Started"
320
  msgstr ""
321
 
322
+ #: facebook-commerce.php:4372
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 ""
326
 
327
+ #: includes/AJAX.php:78
328
  msgid "Hide Product"
329
  msgstr ""
330
 
331
+ #: includes/AJAX.php:82
332
  msgid "Do Not Hide Product"
333
  msgstr ""
334
 
335
+ #: includes/AJAX.php:88
336
  msgid ""
337
  "This product will no longer be updated in your Facebook catalog. Would you "
338
  "like to hide this product from your Facebook shop?"
339
  msgstr ""
340
 
341
+ #: includes/AJAX.php:127 includes/AJAX.php:222
342
  msgid "Go to Settings"
343
  msgstr ""
344
 
345
+ #: includes/AJAX.php:132 includes/AJAX.php:227 includes/AJAX.php:298
346
  msgid "Cancel"
347
  msgstr ""
348
 
349
+ #: includes/AJAX.php:140
350
  #. translators: Placeholder %s - <br/> tag
351
  msgid ""
352
  "This product belongs to a category or tag that is excluded from the "
355
  "or click Cancel and update the product's category / tag assignments."
356
  msgstr ""
357
 
358
+ #: includes/AJAX.php:181
359
  msgid "Hide Products"
360
  msgstr ""
361
 
362
+ #: includes/AJAX.php:185
363
  msgid "Do Not Hide Products"
364
  msgstr ""
365
 
366
+ #: includes/AJAX.php:191
367
  msgid ""
368
  "The selected products will no longer be updated in your Facebook catalog. "
369
  "Would you like to hide these products from your Facebook shop?"
370
  msgstr ""
371
 
372
+ #: includes/AJAX.php:233
373
  msgid ""
374
  "One or more of the selected products belongs to a category or tag that is "
375
  "excluded from the Facebook catalog sync. To sync these products to "
377
  "settings."
378
  msgstr ""
379
 
380
+ #: includes/AJAX.php:286
381
  msgid "Exclude Products"
382
  msgstr ""
383
 
384
+ #: includes/AJAX.php:292
385
  msgid "Exclude Products and Hide in Facebook"
386
  msgstr ""
387
 
388
+ #: includes/AJAX.php:306
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:121
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."
402
  msgstr[0] ""
403
  msgstr[1] ""
404
 
405
+ #: includes/Admin.php:129
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:130
411
  msgid "Launch Test?"
412
  msgstr ""
413
 
414
+ #: includes/Admin.php:131
415
  msgid ""
416
  "Facebook for WooCommerce automatically syncs your products on "
417
  "create/update. Are you sure you want to force product resync?\n"
421
  "did not sync."
422
  msgstr ""
423
 
424
+ #: includes/Admin.php:132
425
  msgid "Syncing... Keep this browser open until sync is complete. %s"
426
  msgstr ""
427
 
428
+ #: includes/Admin.php:136
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:138
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:140
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:141
447
  msgid "There was an error trying to sync the products to Facebook."
448
  msgstr ""
449
 
450
+ #: includes/Admin.php:142
451
  msgid ""
452
  "Something went wrong while uploading the product information, please try "
453
  "again."
454
  msgstr ""
455
 
456
+ #: includes/Admin.php:162
457
  msgid "FB Sync Enabled"
458
  msgstr ""
459
 
460
+ #: includes/Admin.php:186
 
 
 
 
461
  msgid "Enabled"
462
  msgstr ""
463
 
464
+ #: includes/Admin.php:188
465
  msgid "Disabled"
466
  msgstr ""
467
 
468
+ #: includes/Admin.php:206
469
  #. translators: Points to a product that was never synced with Facebook
470
  msgid "Never synced with Facebook."
471
  msgstr ""
472
 
473
+ #: includes/Admin.php:218
474
  #. translators: Action to hide a product (currently synced with Facebook) from
475
  #. the Facebook catalog
476
  msgid "Hide from Facebook catalog. Currently synced with Facebook."
477
  msgstr ""
478
 
479
+ #: includes/Admin.php:220
480
  #. translators: Action to publish a product (currently synced with Facebook) in
481
  #. the Facebook catalog
482
  msgid "Publish in Facebook catalog. Currently synced with Facebook."
483
  msgstr ""
484
 
485
+ #: includes/Admin.php:223
486
  #. translators: Action to hide a product (currently not synced with Facebook)
487
  #. from the Facebook catalog
488
  msgid "Hide from Facebook catalog. Not synced with Facebook."
489
  msgstr ""
490
 
491
+ #: includes/Admin.php:225
492
  #. translators: Action to publish a product (currently not synced with
493
  #. Facebook) in the Facebook catalog
494
  msgid "Publish in Facebook catalog. Not synced with Facebook."
495
  msgstr ""
496
 
497
+ #: includes/Admin.php:236
498
  msgid "Show"
499
  msgstr ""
500
 
501
+ #: includes/Admin.php:244
502
  msgid "Hide"
503
  msgstr ""
504
 
505
+ #: includes/Admin.php:272
506
  msgid "Filter by Facebook sync setting"
507
  msgstr ""
508
 
509
+ #: includes/Admin.php:273
510
  msgid "Facebook sync enabled"
511
  msgstr ""
512
 
513
+ #: includes/Admin.php:274
514
  msgid "Facebook sync disabled"
515
  msgstr ""
516
 
517
+ #: includes/Admin.php:458 includes/Admin.php:692 includes/Admin.php:784
518
  msgid "Include in Facebook sync"
519
  msgstr ""
520
 
521
+ #: includes/Admin.php:459
522
  msgid "Exclude from Facebook sync"
523
  msgstr ""
524
 
525
+ #: includes/Admin.php:546
526
  #. translators: Placeholders: %1$s - Facebook for Woocommerce, %2$s - opening
527
  #. HTML <a> link tag, %3$s - closing HTML </a> link tag
528
  msgid ""
531
  "checkout URLs on Facebook%3$s."
532
  msgstr ""
533
 
534
+ #: includes/Admin.php:579
535
+ #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing
536
+ #. HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a>
537
+ #. tag
538
+ msgid ""
539
+ "%1$sHeads up!%2$s Product sync is temporarily changed as we migrate to a "
540
+ "more secure experience. An automated sync from Facebook will run every hour "
541
+ "to update the catalog with any changes you've made. %3$sLearn more%4$s"
542
+ msgstr ""
543
+
544
+ #: includes/Admin.php:586
545
+ msgid "Don't show this notice again"
546
+ msgstr ""
547
+
548
+ #: includes/Admin.php:632
549
+ #. translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing
550
+ #. HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a>
551
+ #. tag
552
+ msgid ""
553
+ "%1$sHeads up!%2$s Catalog visibility settings have been temporarily removed "
554
+ "as we migrate to a more secure experience. To remove products from your "
555
+ "Facebook catalog, please disable syncing to Facebook for the product. "
556
+ "%3$sLearn more%4$s"
557
+ msgstr ""
558
+
559
+ #: includes/Admin.php:698 includes/Admin.php:793
560
  msgid "Facebook Description"
561
  msgstr ""
562
 
563
+ #: includes/Admin.php:700 includes/Admin.php:795
564
  msgid ""
565
  "Custom (plain-text only) description for product on Facebook. If blank, "
566
  "product description will be used. If product description is blank, "
567
  "shortname will be used."
568
  msgstr ""
569
 
570
+ #: includes/Admin.php:709 includes/Admin.php:806
571
  msgid "Facebook Product Image"
572
  msgstr ""
573
 
574
+ #: includes/Admin.php:711 includes/Admin.php:808
575
  msgid ""
576
  "Choose the product image that should be synced to the Facebook catalog for "
577
  "this product. If using a custom image, please enter an absolute URL (e.g. "
578
  "https://domain.com/image.jpg)."
579
  msgstr ""
580
 
581
+ #: includes/Admin.php:713
582
  msgid "Use WooCommerce image"
583
  msgstr ""
584
 
585
+ #: includes/Admin.php:714 includes/Admin.php:812
586
  msgid "Use custom image"
587
  msgstr ""
588
 
589
+ #: includes/Admin.php:723 includes/Admin.php:822
590
  msgid "Custom Image URL"
591
  msgstr ""
592
 
593
+ #: includes/Admin.php:732 includes/Admin.php:833
594
  #. translators: Placeholders %1$s - WC currency symbol
595
  msgid "Facebook Price (%1$s)"
596
  msgstr ""
597
 
598
+ #: includes/Admin.php:736 includes/Admin.php:837
599
  msgid ""
600
  "Custom price for product on Facebook. Please enter in monetary decimal (.) "
601
  "format without thousand separators and currency symbols. If blank, product "
602
  "price will be used."
603
  msgstr ""
604
 
605
+ #: includes/Admin.php:810
606
  msgid "Use variation image"
607
  msgstr ""
608
 
609
+ #: includes/Admin.php:811
610
  msgid "Use parent image"
611
  msgstr ""
612
 
613
+ #: includes/Admin.php:942
614
  msgid "Close modal panel"
615
  msgstr ""
616
 
630
  msgid "Dismiss"
631
  msgstr ""
632
 
633
+ #: includes/fbproductfeed.php:432
634
+ msgid "Could not create product catalog feed directory"
635
+ msgstr ""
636
+
637
+ #: includes/fbproductfeed.php:498
638
+ msgid "Could not open the product catalog feed file for writing"
639
+ msgstr ""
640
+
641
  #: includes/fbwpml.php:105
642
  msgid "Facebook Visibility"
643
  msgstr ""
675
  msgid "https://www.facebook.com/"
676
  msgstr ""
677
 
678
+ #: facebook-commerce-messenger-chat.php:258 facebook-commerce.php:2507
679
  msgctxt "language"
680
  msgid "English (United States)"
681
  msgstr ""
includes/AJAX.php CHANGED
@@ -51,15 +51,17 @@ class AJAX {
51
  check_ajax_referer( 'set-product-sync-prompt', 'security' );
52
 
53
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
54
- $product_id = isset( $_POST['product'] ) ? (int) $_POST['product'] : 0;
55
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
56
- $sync_enabled = isset( $_POST['sync_enabled'] ) ? (string) $_POST['sync_enabled'] : '';
57
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
58
- $product_cats = isset( $_POST['categories'] ) ? (array) $_POST['categories'] : [];
 
 
59
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
60
- $product_tags = isset( $_POST['tags'] ) ? (array) $_POST['tags'] : [];
61
 
62
- if ( $product_id > 0 && in_array( $sync_enabled, [ 'enabled', 'disabled' ], true ) ) {
63
 
64
  $product = wc_get_product( $product_id );
65
 
@@ -87,7 +89,7 @@ class AJAX {
87
  'buttons' => $buttons,
88
  ] );
89
 
90
- } elseif ( 'enabled' === $sync_enabled ) {
91
 
92
  $has_excluded_terms = false;
93
 
@@ -282,10 +284,13 @@ class AJAX {
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"
51
  check_ajax_referer( 'set-product-sync-prompt', 'security' );
52
 
53
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
54
+ $product_id = isset( $_POST['product'] ) ? (int) $_POST['product'] : 0;
55
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
56
+ $sync_enabled = isset( $_POST['sync_enabled'] ) ? (string) $_POST['sync_enabled'] : '';
57
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
58
+ $var_sync_enabled = isset( $_POST['var_sync_enabled'] ) ? (string) $_POST['var_sync_enabled'] : '';
59
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
60
+ $product_cats = isset( $_POST['categories'] ) ? (array) $_POST['categories'] : [];
61
  // phpcs:ignore WordPress.Security.NonceVerification.Recommended
62
+ $product_tags = isset( $_POST['tags'] ) ? (array) $_POST['tags'] : [];
63
 
64
+ if ( $product_id > 0 && in_array( $var_sync_enabled, [ 'enabled', 'disabled' ], true ) && in_array( $sync_enabled, [ 'enabled', 'disabled' ], true ) ) {
65
 
66
  $product = wc_get_product( $product_id );
67
 
89
  'buttons' => $buttons,
90
  ] );
91
 
92
+ } elseif ( ( 'enabled' === $sync_enabled && ! $product->is_type( 'variable' ) ) || ( 'enabled' === $var_sync_enabled && $product->is_type( 'variable' ) ) ) {
93
 
94
  $has_excluded_terms = false;
95
 
284
  id="facebook-for-woocommerce-confirm-settings-change"
285
  class="button button-large button-primary facebook-for-woocommerce-confirm-settings-change"
286
  ><?php esc_html_e( 'Exclude Products', 'facebook-for-woocommerce' ); ?></button>
287
+
288
+ <!-- TODO: restore for FBE 2.0
289
  <button
290
  id="facebook-for-woocommerce-confirm-settings-change-hide-products"
291
  class="button button-large button-primary facebook-for-woocommerce-confirm-settings-change hide-products"
292
+ ><?php esc_html_e( 'Exclude Products and Hide in Facebook', 'facebook-for-woocommerce' ); ?></button> -->
293
+
294
  <button
295
  id="facebook-for-woocommerce-cancel-settings-change"
296
  class="button button-large button-primary"
includes/Admin.php CHANGED
@@ -10,6 +10,8 @@
10
 
11
  namespace SkyVerge\WooCommerce\Facebook;
12
 
 
 
13
  defined( 'ABSPATH' ) or exit;
14
 
15
  /**
@@ -33,7 +35,7 @@ class Admin {
33
  $integration = facebook_for_woocommerce()->get_integration();
34
 
35
  // only alter the admin UI if the plugin is connected to Facebook and ready to sync products
36
- if ( ! $integration->get_page_access_token() || ! $integration->get_product_catalog_id() ) {
37
  return;
38
  }
39
 
@@ -45,6 +47,13 @@ class Admin {
45
  // add admin notification in case of site URL change
46
  add_action( 'admin_notices', [ $this, 'validate_cart_url' ] );
47
 
 
 
 
 
 
 
 
48
  // add columns for displaying Facebook sync enabled/disabled and catalog visibility status
49
  add_filter( 'manage_product_posts_columns', [ $this, 'add_product_list_table_columns' ] );
50
  add_action( 'manage_product_posts_custom_column', [ $this, 'add_product_list_table_columns_content' ] );
@@ -151,7 +160,7 @@ class Admin {
151
  public function add_product_list_table_columns( $columns ) {
152
 
153
  $columns['facebook_sync_enabled'] = __( 'FB Sync Enabled', 'facebook-for-woocommerce' );
154
- $columns['facebook_catalog_visibility'] = __( 'FB Catalog Visibility', 'facebook-for-woocommerce' );
155
 
156
  return $columns;
157
  }
@@ -296,13 +305,38 @@ class Admin {
296
 
297
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  } else {
300
 
301
  $integration = facebook_for_woocommerce()->get_integration();
302
  $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : [];
303
- $exlcuded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : [];
304
 
305
- if ( $excluded_categories_ids || $exlcuded_tags_ids ) {
306
 
307
  // find the IDs of products that have sync enabled
308
  $products_query_vars = [
@@ -523,6 +557,91 @@ class Admin {
523
  }
524
 
525
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  /**
527
  * Adds a new tab to the Product edit page.
528
  *
@@ -665,7 +784,7 @@ class Admin {
665
  'label' => __( 'Include in Facebook sync', 'facebook-for-woocommerce' ),
666
  'value' => wc_bool_to_string( 'no' !== $sync_enabled ),
667
  'class' => 'checkbox js-variable-fb-sync-toggle',
668
- 'wrapper_class' => 'fb-sync-enabled-field'
669
  ] );
670
 
671
  woocommerce_wp_textarea_input( [
@@ -703,7 +822,7 @@ class Admin {
703
  'label' => __( 'Custom Image URL', 'facebook-for-woocommerce' ),
704
  'value' => $image_url,
705
  'class' => sprintf( 'enable-if-sync-enabled product-image-source-field show-if-product-image-source-%s', Products::PRODUCT_IMAGE_SOURCE_CUSTOM ),
706
- 'wrapper_class' => 'form-row form-row-full'
707
  ] );
708
 
709
  woocommerce_wp_text_input( [
10
 
11
  namespace SkyVerge\WooCommerce\Facebook;
12
 
13
+ use SkyVerge\WooCommerce\PluginFramework\v5_5_4\SV_WC_Helper;
14
+
15
  defined( 'ABSPATH' ) or exit;
16
 
17
  /**
35
  $integration = facebook_for_woocommerce()->get_integration();
36
 
37
  // only alter the admin UI if the plugin is connected to Facebook and ready to sync products
38
+ if ( ! $integration->get_product_catalog_id() ) {
39
  return;
40
  }
41
 
47
  // add admin notification in case of site URL change
48
  add_action( 'admin_notices', [ $this, 'validate_cart_url' ] );
49
 
50
+ // add admin notice to inform that product sync has changed
51
+ add_action( 'admin_notices', [ $this, 'add_product_sync_delay_notice' ] );
52
+ // add admin notice to inform that the catalog visibility setting was removed
53
+ add_action( 'admin_notices', [ $this, 'add_catalog_visibility_settings_removed_notice' ] );
54
+ // handle dismissal of special notices
55
+ add_action( 'wc_' . facebook_for_woocommerce()->get_id(). '_dismiss_notice', [ $this, 'handle_dismiss_notice' ], 10, 2 );
56
+
57
  // add columns for displaying Facebook sync enabled/disabled and catalog visibility status
58
  add_filter( 'manage_product_posts_columns', [ $this, 'add_product_list_table_columns' ] );
59
  add_action( 'manage_product_posts_custom_column', [ $this, 'add_product_list_table_columns_content' ] );
160
  public function add_product_list_table_columns( $columns ) {
161
 
162
  $columns['facebook_sync_enabled'] = __( 'FB Sync Enabled', 'facebook-for-woocommerce' );
163
+ // $columns['facebook_catalog_visibility'] = __( 'FB Catalog Visibility', 'facebook-for-woocommerce' );
164
 
165
  return $columns;
166
  }
305
 
306
  $query_vars = $this->add_query_vars_to_find_products_with_sync_enabled( $query_vars );
307
 
308
+ // since we record enabled status on child variations, we may need to query variable products found for their children to exclude them from query results
309
+ $exclude_products = [];
310
+ $found_ids = get_posts( array_merge( $query_vars, [ 'fields' => 'ids' ] ) );
311
+ $found_products = empty( $found_ids ) ? [] : wc_get_products( [
312
+ 'limit' => -1,
313
+ 'type' => 'variable',
314
+ 'include' => $found_ids,
315
+ ] );
316
+
317
+ /** @var \WC_Product[] $found_products */
318
+ foreach ( $found_products as $product ) {
319
+
320
+ if ( ! Products::is_sync_enabled_for_product( $product ) ) {
321
+ $exclude_products[] = $product->get_id();
322
+ }
323
+ }
324
+
325
+ if ( ! empty( $exclude_products ) ) {
326
+ if ( ! empty( $query_vars['post__not_in'] ) ) {
327
+ $query_vars['post__not_in'] = array_merge( $query_vars['post__not_in'], $exclude_products );
328
+ } else {
329
+ $query_vars['post__not_in'] = $exclude_products;
330
+ }
331
+ }
332
+
333
  } else {
334
 
335
  $integration = facebook_for_woocommerce()->get_integration();
336
  $excluded_categories_ids = $integration ? $integration->get_excluded_product_category_ids() : [];
337
+ $excluded_tags_ids = $integration ? $integration->get_excluded_product_tag_ids() : [];
338
 
339
+ if ( $excluded_categories_ids || $excluded_tags_ids ) {
340
 
341
  // find the IDs of products that have sync enabled
342
  $products_query_vars = [
557
  }
558
 
559
 
560
+ /**
561
+ * Prints a notice on products page to inform users about changes in product sync.
562
+ *
563
+ * @internal
564
+ *
565
+ * @since 1.11.0
566
+ */
567
+ public function add_product_sync_delay_notice() {
568
+ global $current_screen;
569
+
570
+ $transient_name = 'wc_' . facebook_for_woocommerce()->get_id() . '_show_product_sync_delay_notice_' . get_current_user_id();
571
+
572
+ if ( isset( $current_screen->id ) && in_array( $current_screen->id, [ 'edit-product', 'product' ], true ) && get_transient( $transient_name ) ) {
573
+
574
+ if ( isset( $_GET['message'] ) || isset( $_GET['trashed'] ) || isset( $_GET['deleted'] ) ) {
575
+
576
+ facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice(
577
+ sprintf(
578
+ /* translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a> tag */
579
+ esc_html__( '%1$sHeads up!%2$s Product sync is temporarily changed as we migrate to a more secure experience. An automated sync from Facebook will run every hour to update the catalog with any changes you\'ve made. %3$sLearn more%4$s', 'facebook-for-woocommerce' ),
580
+ '<strong>',
581
+ '</strong>',
582
+ '<a href="https://docs.woocommerce.com/document/facebook-for-woocommerce/#faq-security" target="_blank">',
583
+ '</a>'
584
+ )
585
+ . '</p><p>' // close notice paragraph and open a new one for the button
586
+ . '<button class="button notice-dismiss-permanently" type="button">' . esc_html__( "Don't show this notice again", 'facebook-for-woocommerce' ) . '</button>',
587
+ 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-product-sync-delay',
588
+ [ 'notice_class' => 'notice-info' ]
589
+ );
590
+ }
591
+
592
+ delete_transient( $transient_name );
593
+ }
594
+ }
595
+
596
+
597
+ /**
598
+ * Handles dismissed notices.
599
+ *
600
+ * @internal
601
+ *
602
+ * @since 1.11.0
603
+ *
604
+ * @param string $message_id the dismissed notice ID
605
+ * @param int $user_id the ID of the user the noticed was dismissed for
606
+ */
607
+ public function handle_dismiss_notice( $message_id, $user_id = null ) {
608
+
609
+ // undismiss product sync delay notice unless 'permanently' is included in the request
610
+ if ( ! SV_WC_Helper::get_requested_value( 'permanently' ) && 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-product-sync-delay' === $message_id ) {
611
+
612
+ facebook_for_woocommerce()->get_admin_notice_handler()->undismiss_notice( $message_id, $user_id );
613
+ }
614
+ }
615
+
616
+
617
+ /**
618
+ * Prints a notice on products page to inform users that catalog visibility settings were removed.
619
+ *
620
+ * @internal
621
+ *
622
+ * @since 1.11.0
623
+ */
624
+ public function add_catalog_visibility_settings_removed_notice() {
625
+ global $current_screen;
626
+
627
+ if ( isset( $current_screen->id ) && in_array( $current_screen->id, [ 'edit-product', 'product' ], true ) ) {
628
+
629
+ facebook_for_woocommerce()->get_admin_notice_handler()->add_admin_notice(
630
+ sprintf(
631
+ /* translators: Placeholders: %1$s - opening HTML <strong> tag, %2$s - closing HTML </strong> tag, %3$s - opening HTML <a> tag, %4$s - closing HTML </a> tag */
632
+ esc_html__( '%1$sHeads up!%2$s Catalog visibility settings have been temporarily removed as we migrate to a more secure experience. To remove products from your Facebook catalog, please disable syncing to Facebook for the product. %3$sLearn more%4$s', 'facebook-for-woocommerce' ),
633
+ '<strong>',
634
+ '</strong>',
635
+ '<a href="https://docs.woocommerce.com/document/facebook-for-woocommerce/#section-10" target="_blank">', // TODO: add link to FAQ entry {WV 2020-03-30}
636
+ '</a>'
637
+ ),
638
+ 'wc-' . facebook_for_woocommerce()->get_id_dasherized() . '-catalog-visibility-settings-removed',
639
+ [ 'notice_class' => 'notice-info' ]
640
+ );
641
+ }
642
+ }
643
+
644
+
645
  /**
646
  * Adds a new tab to the Product edit page.
647
  *
784
  'label' => __( 'Include in Facebook sync', 'facebook-for-woocommerce' ),
785
  'value' => wc_bool_to_string( 'no' !== $sync_enabled ),
786
  'class' => 'checkbox js-variable-fb-sync-toggle',
787
+ 'wrapper_class' => 'fb-sync-enabled-field',
788
  ] );
789
 
790
  woocommerce_wp_textarea_input( [
822
  'label' => __( 'Custom Image URL', 'facebook-for-woocommerce' ),
823
  'value' => $image_url,
824
  'class' => sprintf( 'enable-if-sync-enabled product-image-source-field show-if-product-image-source-%s', Products::PRODUCT_IMAGE_SOURCE_CUSTOM ),
825
+ 'wrapper_class' => 'form-row form-row-full',
826
  ] );
827
 
828
  woocommerce_wp_text_input( [
includes/Lifecycle.php CHANGED
@@ -38,6 +38,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
38
  $this->upgrade_versions = [
39
  '1.10.0',
40
  '1.10.1',
 
41
  ];
42
  }
43
 
@@ -200,4 +201,20 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
200
  }
201
 
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  }
38
  $this->upgrade_versions = [
39
  '1.10.0',
40
  '1.10.1',
41
+ '1.11.0',
42
  ];
43
  }
44
 
201
  }
202
 
203
 
204
+ /**
205
+ * Upgrades to version 1.11.0.
206
+ *
207
+ * @since 1.11.0
208
+ */
209
+ protected function upgrade_to_1_11_0() {
210
+
211
+ $settings = get_option( 'woocommerce_' . \WC_Facebookcommerce::INTEGRATION_ID . '_settings', [] );
212
+
213
+ // moves the upload ID to a standalone option
214
+ if ( ! empty( $settings['fb_upload_id'] ) ) {
215
+ $this->get_plugin()->get_integration()->update_upload_id( $settings['fb_upload_id'] );
216
+ }
217
+ }
218
+
219
+
220
  }
includes/Products/Feed.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
4
+ *
5
+ * This source code is licensed under the license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @package FacebookCommerce
9
+ */
10
+
11
+ namespace SkyVerge\WooCommerce\Facebook\Products;
12
+
13
+ defined( 'ABSPATH' ) or exit;
14
+
15
+ use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
16
+
17
+ /**
18
+ * The main product feed handler.
19
+ *
20
+ * This will eventually replace \WC_Facebook_Product_Feed as we refactor and move its functionality here.
21
+ *
22
+ * @since 1.11.0
23
+ */
24
+ class Feed {
25
+
26
+
27
+ /** @var string the action callback for generating a feed */
28
+ const GENERATE_FEED_ACTION = 'wc_facebook_regenerate_feed';
29
+
30
+ /** @var string the action slug for getting the product feed */
31
+ const REQUEST_FEED_ACTION = 'wc_facebook_get_feed_data';
32
+
33
+ /** @var string the WordPress option name where the secret included in the feed URL is stored */
34
+ const OPTION_FEED_URL_SECRET = 'wc_facebook_feed_url_secret';
35
+
36
+
37
+ /**
38
+ * Feed constructor.
39
+ *
40
+ * @since 1.11.0
41
+ */
42
+ public function __construct() {
43
+
44
+ // add the necessary action and filter hooks
45
+ $this->add_hooks();
46
+ }
47
+
48
+
49
+ /**
50
+ * Adds the necessary action and filter hooks.
51
+ *
52
+ * @since 1.11.0
53
+ */
54
+ private function add_hooks() {
55
+
56
+ // schedule the recurring feed generation
57
+ add_action( 'init', [ $this, 'schedule_feed_generation' ] );
58
+
59
+ // regenerate the product feed
60
+ add_action( self::GENERATE_FEED_ACTION, [ $this, 'regenerate_feed' ] );
61
+
62
+ // handle the feed data request
63
+ add_action( 'woocommerce_api_' . self::REQUEST_FEED_ACTION, [ $this, 'handle_feed_data_request' ] );
64
+ }
65
+
66
+
67
+ /**
68
+ * Handles the feed data request.
69
+ *
70
+ * @internal
71
+ *
72
+ * @since 1.11.0
73
+ */
74
+ public function handle_feed_data_request() {
75
+
76
+ \WC_Facebookcommerce_Utils::log( 'Facebook is requesting the product feed.' );
77
+
78
+ $feed_handler = new \WC_Facebook_Product_Feed();
79
+ $file_path = $feed_handler->get_file_path();
80
+
81
+ // regenerate if the file doesn't exist
82
+ if ( ! empty( $_GET['regenerate'] ) || ! file_exists( $file_path ) ) {
83
+ $feed_handler->generate_feed();
84
+ }
85
+
86
+ try {
87
+
88
+ // bail early if the feed secret is not included or is not valid
89
+ if ( Feed::get_feed_secret() !== Framework\SV_WC_Helper::get_requested_value( 'secret' ) ) {
90
+ throw new Framework\SV_WC_Plugin_Exception( 'Invalid feed secret provided.', 401 );
91
+ }
92
+
93
+ // bail early if the file can't be read
94
+ if ( ! is_readable( $file_path ) ) {
95
+ throw new Framework\SV_WC_Plugin_Exception( 'File is not readable.', 404 );
96
+ }
97
+
98
+ // set the download headers
99
+ header( 'Content-Type: text/csv; charset=utf-8' );
100
+ header( 'Content-Description: File Transfer' );
101
+ header( 'Content-Disposition: attachment; filename="' . basename( $file_path ) . '"' );
102
+ header( 'Expires: 0' );
103
+ header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
104
+ header( 'Pragma: public' );
105
+ header( 'Content-Length:'. filesize( $file_path ) );
106
+
107
+ $file = @fopen( $file_path, 'rb' );
108
+
109
+ if ( ! $file ) {
110
+ throw new Framework\SV_WC_Plugin_Exception( 'Could not open feed file.', 500 );
111
+ }
112
+
113
+ // fpassthru might be disabled in some hosts (like Flywheel)
114
+ if ( $this->is_fpassthru_disabled() || ! @fpassthru( $file ) ) {
115
+
116
+ \WC_Facebookcommerce_Utils::log( 'fpassthru is disabled: getting file contents' );
117
+
118
+ $contents = @stream_get_contents( $file );
119
+
120
+ if ( ! $contents ) {
121
+ throw new Framework\SV_WC_Plugin_Exception( 'Could not get feed file contents.', 500 );
122
+ }
123
+
124
+ echo $contents;
125
+ }
126
+
127
+ } catch ( \Exception $exception ) {
128
+
129
+ \WC_Facebookcommerce_Utils::log( 'Could not serve product feed. ' . $exception->getMessage() . ' (' . $exception->getCode() . ')' );
130
+
131
+ status_header( $exception->getCode() );
132
+ }
133
+
134
+ exit;
135
+ }
136
+
137
+
138
+ /**
139
+ * Regenerates the product feed.
140
+ *
141
+ * @internal
142
+ *
143
+ * @since 1.11.0
144
+ */
145
+ public function regenerate_feed() {
146
+
147
+ $feed_handler = new \WC_Facebook_Product_Feed();
148
+
149
+ $feed_handler->generate_feed();
150
+ }
151
+
152
+
153
+ /**
154
+ * Schedules the recurring feed generation.
155
+ *
156
+ * @internal
157
+ *
158
+ * @since 1.11.0
159
+ */
160
+ public function schedule_feed_generation() {
161
+
162
+ $integration = facebook_for_woocommerce()->get_integration();
163
+
164
+ // only schedule if configured
165
+ if ( ! $integration || ! $integration->is_configured() || ! $integration->is_product_sync_enabled() ) {
166
+ as_unschedule_all_actions( self::GENERATE_FEED_ACTION );
167
+ return;
168
+ }
169
+
170
+ /**
171
+ * Filters the frequency with which the product feed data is generated.
172
+ *
173
+ * @since 1.11.0
174
+ *
175
+ * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes.
176
+ */
177
+ $interval = apply_filters( 'wc_facebook_feed_generation_interval', MINUTE_IN_SECONDS * 15 );
178
+
179
+ if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) {
180
+ as_schedule_recurring_action( time(), max( 2, $interval ), self::GENERATE_FEED_ACTION, [], facebook_for_woocommerce()->get_id_dasherized() );
181
+ }
182
+ }
183
+
184
+
185
+ /**
186
+ * Checks whether fpassthru has been disabled in PHP.
187
+ *
188
+ * Helper method, do not open to public.
189
+ *
190
+ * @since 1.11.0
191
+ *
192
+ * @return bool
193
+ */
194
+ private function is_fpassthru_disabled() {
195
+
196
+ $disabled = false;
197
+
198
+ if ( function_exists( 'ini_get' ) ) {
199
+
200
+ $disabled_functions = @ini_get( 'disable_functions' );
201
+
202
+ $disabled = is_string( $disabled_functions ) && in_array( 'fpassthru', explode( ',', $disabled_functions ), false );
203
+ }
204
+
205
+ return $disabled;
206
+ }
207
+
208
+
209
+ /**
210
+ * Gets the URL for retrieving the product feed data.
211
+ *
212
+ * @since 1.11.0
213
+ *
214
+ * @return string
215
+ */
216
+ public static function get_feed_data_url() {
217
+
218
+ $query_args = [
219
+ 'wc-api' => self::REQUEST_FEED_ACTION,
220
+ 'secret' => self::get_feed_secret(),
221
+ ];
222
+
223
+ return add_query_arg( $query_args, home_url( '/' ) );
224
+ }
225
+
226
+
227
+ /**
228
+ * Gets the secret value that should be included in the Feed URL.
229
+ *
230
+ * Generates a new secret and stores it in the database if no value is set.
231
+ *
232
+ * @since 1.11.0
233
+ *
234
+ * @return string
235
+ */
236
+ public static function get_feed_secret() {
237
+
238
+ $secret = get_option( self::OPTION_FEED_URL_SECRET, '' );
239
+
240
+ if ( ! $secret ) {
241
+
242
+ $secret = wp_hash( 'products-feed-' . time() );
243
+
244
+ update_option( self::OPTION_FEED_URL_SECRET, $secret );
245
+ }
246
+
247
+ return $secret;
248
+ }
249
+
250
+
251
+ }
includes/fbproduct.php CHANGED
@@ -611,50 +611,54 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
611
 
612
 
613
  /**
614
- * Modify Woo variant/taxonomies to be FB compatible
615
- **/
 
 
 
616
  public function prepare_variants_for_item( &$product_data ) {
617
- if ( ! WC_Facebookcommerce_Utils::is_variation_type(
618
- $this->get_type()
619
- ) ) {
620
- return;
 
 
621
  }
622
 
623
- $attributes = $this->get_variation_attributes();
 
624
  if ( ! $attributes ) {
625
- return;
626
  }
627
 
628
  $variant_names = array_keys( $attributes );
629
- $variant_array = array();
630
 
631
  // Loop through variants (size, color, etc) if they exist
632
  // For each product field type, pull the single variant
633
- foreach ( $variant_names as $orig_name ) {
 
634
  // Retrieve label name for attribute
635
- $label = wc_attribute_label( $orig_name, $this );
636
 
637
  // Clean up variant name (e.g. pa_color should be color)
638
  // Replace "custom_data:foo" with just "foo" so we can use the key
639
  // Product item API expects "custom_data" instead of "custom_data:foo"
640
- $new_name = str_replace(
641
- 'custom_data:',
642
- '',
643
- WC_Facebookcommerce_Utils::sanitize_variant_name( $orig_name )
644
- );
645
 
646
  // Sometimes WC returns an array, sometimes it's an assoc array, depending
647
  // on what type of taxonomy it's using. array_values will guarantee we
648
  // only get a flat array of values.
649
- $options = $this->get_variant_option_name(
650
- $label,
651
- $attributes[ $orig_name ]
652
- );
653
- if ( isset( $options ) ) {
654
  if ( is_array( $options ) ) {
 
655
  $option_values = array_values( $options );
 
656
  } else {
657
- $option_values = array( $options );
 
 
658
  // If this attribute has value 'any', options will be empty strings
659
  // Redirect to product page to select variants.
660
  // Reset checkout url since checkout_url (build from query data will
@@ -664,148 +668,160 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
664
  $product_data['checkout_url'] = $product_data['url'];
665
  }
666
  }
667
- if ( $new_name === WC_Facebookcommerce_Utils::FB_VARIANT_GENDER ) {
 
 
668
  // If we can't validate the gender, this will be null.
669
- $product_data[ $new_name ] =
670
- WC_Facebookcommerce_Utils::validateGender( $option_values[0] );
671
  }
672
 
673
  switch ( $new_name ) {
674
- case WC_Facebookcommerce_Utils::FB_VARIANT_SIZE:
675
- case WC_Facebookcommerce_Utils::FB_VARIANT_COLOR:
676
- case WC_Facebookcommerce_Utils::FB_VARIANT_PATTERN:
677
- array_push(
678
- $variant_array,
679
- array(
680
- 'product_field' => $new_name,
681
- 'label' => $label,
682
- 'options' => $option_values,
683
- )
684
- );
685
  $product_data[ $new_name ] = $option_values[0];
686
- break;
687
- case WC_Facebookcommerce_Utils::FB_VARIANT_GENDER:
 
 
 
688
  // If we can't validate the GENDER field, we'll fall through to the
689
  // default case and set the gender into custom data.
690
  if ( $product_data[ $new_name ] ) {
691
- array_push(
692
- $variant_array,
693
- array(
694
- 'product_field' => $new_name,
695
- 'label' => $label,
696
- 'options' => $option_values,
697
- )
698
- );
699
- break;
700
  }
701
 
 
 
702
  default:
 
703
  // This is for any custom_data.
704
  if ( ! isset( $product_data['custom_data'] ) ) {
705
- $product_data['custom_data'] = array();
706
  }
707
- $product_data['custom_data'][ $new_name ]
708
- = urldecode( $option_values[0] );
709
- break;
 
710
  }
 
711
  } else {
712
- WC_Facebookcommerce_Utils::log(
713
- $this->get_id() . ': No options for ' . $orig_name
714
- );
715
  continue;
716
  }
717
  }
718
- return $variant_array;
 
719
  }
720
 
 
721
  /**
722
- * Modify Woo variant/taxonomies for variable products to be FB compatible
723
- **/
 
 
 
724
  public function prepare_variants_for_group( $feed_data = false ) {
725
- if ( ! WC_Facebookcommerce_Utils::is_variable_type(
726
- $this->get_type()
727
- ) ) {
728
- WC_Facebookcommerce_Utils::fblog(
729
- 'prepare_variants_for_group called on non-variable product'
730
- );
731
- return;
732
- }
733
 
734
- $variation_attributes = $this->get_variation_attributes();
735
- if ( ! $variation_attributes ) {
736
- return;
737
- }
738
- $final_variants = array();
739
 
740
- $attrs = array_keys( $this->get_attributes() );
741
- foreach ( $attrs as $name ) {
742
- $label = wc_attribute_label( $name, $this );
743
 
744
- if ( taxonomy_is_product_attribute( $name ) ) {
745
- $key = $name;
746
- } else {
747
- // variation_attributes keys are labels for custom attrs for some reason
748
- $key = $label;
749
  }
750
 
751
- if ( ! $key ) {
752
- WC_Facebookcommerce_Utils::fblog(
753
- "Critical error: can't get attribute name or label!"
754
- );
755
- return;
756
- }
757
 
758
- if ( isset( $variation_attributes[ $key ] ) ) {
759
- // Array of the options (e.g. small, medium, large)
760
- $option_values = $variation_attributes[ $key ];
761
- } else {
762
- WC_Facebookcommerce_Utils::log(
763
- $this->get_id() . ': No options for ' . $name
764
- );
765
- continue; // Skip variations without valid attribute options
766
  }
767
 
768
- // If this is a wc_product_variable, check default attrib.
769
- // If it's being used, show it as the first option on Facebook.
770
- $first_option = $this->get_variation_default_attribute( $key );
771
- if ( $first_option ) {
772
- $idx = array_search( $first_option, $option_values );
773
- unset( $option_values[ $idx ] );
774
- array_unshift( $option_values, $first_option );
775
- }
776
 
777
- if (
778
- function_exists( 'taxonomy_is_product_attribute' ) &&
779
- taxonomy_is_product_attribute( $name )
780
- ) {
781
- $option_values = $this->get_grouped_product_option_names(
782
- $key,
783
- $option_values
784
- );
785
- }
786
 
787
- // https://developers.facebook.com/docs/marketing-api/reference/product-variant/
788
- // For API approach, product_field need to start with 'custom_data:'
789
- // Clean up variant name (e.g. pa_color should be color)
790
- $name = WC_Facebookcommerce_Utils::sanitize_variant_name( $name );
 
 
791
 
792
- // For feed uploading, product field should remove prefix 'custom_data:'
793
- if ( $feed_data ) {
794
- $name = str_replace( 'custom_data:', '', $name );
795
- }
796
- array_push(
797
- $final_variants,
798
- array(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
799
  'product_field' => $name,
800
  'label' => $label,
801
  'options' => $option_values,
802
- )
803
- );
 
 
 
 
 
 
804
  }
805
 
806
  return $final_variants;
807
-
808
  }
 
 
809
  }
810
 
811
  endif;
611
 
612
 
613
  /**
614
+ * Normalizes variant data for Facebook.
615
+ *
616
+ * @param array $product_data variation product data
617
+ * @return array
618
+ */
619
  public function prepare_variants_for_item( &$product_data ) {
620
+
621
+ /** @var \WC_Product_Variation $product */
622
+ $product = $this;
623
+
624
+ if ( ! $product->is_type( 'variation' ) ) {
625
+ return [];
626
  }
627
 
628
+ $attributes = $product->get_variation_attributes();
629
+
630
  if ( ! $attributes ) {
631
+ return [];
632
  }
633
 
634
  $variant_names = array_keys( $attributes );
635
+ $variant_data = [];
636
 
637
  // Loop through variants (size, color, etc) if they exist
638
  // For each product field type, pull the single variant
639
+ foreach ( $variant_names as $original_variant_name ) {
640
+
641
  // Retrieve label name for attribute
642
+ $label = wc_attribute_label( $original_variant_name, $product );
643
 
644
  // Clean up variant name (e.g. pa_color should be color)
645
  // Replace "custom_data:foo" with just "foo" so we can use the key
646
  // Product item API expects "custom_data" instead of "custom_data:foo"
647
+ $new_name = str_replace( 'custom_data:', '', \WC_Facebookcommerce_Utils::sanitize_variant_name( $original_variant_name ) );
 
 
 
 
648
 
649
  // Sometimes WC returns an array, sometimes it's an assoc array, depending
650
  // on what type of taxonomy it's using. array_values will guarantee we
651
  // only get a flat array of values.
652
+ if ( $options = $this->get_variant_option_name( $label, $attributes[ $original_variant_name ] ) ) {
653
+
 
 
 
654
  if ( is_array( $options ) ) {
655
+
656
  $option_values = array_values( $options );
657
+
658
  } else {
659
+
660
+ $option_values = [ $options ];
661
+
662
  // If this attribute has value 'any', options will be empty strings
663
  // Redirect to product page to select variants.
664
  // Reset checkout url since checkout_url (build from query data will
668
  $product_data['checkout_url'] = $product_data['url'];
669
  }
670
  }
671
+
672
+ if ( \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER === $new_name ) {
673
+
674
  // If we can't validate the gender, this will be null.
675
+ $product_data[ $new_name ] = \WC_Facebookcommerce_Utils::validateGender( $option_values[0] );
 
676
  }
677
 
678
  switch ( $new_name ) {
679
+
680
+ case \WC_Facebookcommerce_Utils::FB_VARIANT_SIZE:
681
+ case \WC_Facebookcommerce_Utils::FB_VARIANT_COLOR:
682
+ case \WC_Facebookcommerce_Utils::FB_VARIANT_PATTERN:
683
+
684
+ $variant_data[] = [
685
+ 'product_field' => $new_name,
686
+ 'label' => $label,
687
+ 'options' => $option_values,
688
+ ];
689
+
690
  $product_data[ $new_name ] = $option_values[0];
691
+
692
+ break;
693
+
694
+ case \WC_Facebookcommerce_Utils::FB_VARIANT_GENDER:
695
+
696
  // If we can't validate the GENDER field, we'll fall through to the
697
  // default case and set the gender into custom data.
698
  if ( $product_data[ $new_name ] ) {
699
+
700
+ $variant_data[] = [
701
+ 'product_field' => $new_name,
702
+ 'label' => $label,
703
+ 'options' => $option_values,
704
+ ];
 
 
 
705
  }
706
 
707
+ break;
708
+
709
  default:
710
+
711
  // This is for any custom_data.
712
  if ( ! isset( $product_data['custom_data'] ) ) {
713
+ $product_data['custom_data'] = [];
714
  }
715
+
716
+ $product_data['custom_data'][ $new_name ] = urldecode( $option_values[0] );
717
+
718
+ break;
719
  }
720
+
721
  } else {
722
+
723
+ \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $original_variant_name );
 
724
  continue;
725
  }
726
  }
727
+
728
+ return $variant_data;
729
  }
730
 
731
+
732
  /**
733
+ * Normalizes variable product variations data for Facebook.
734
+ *
735
+ * @param bool $feed_data whether this is used for feed data
736
+ * @return array
737
+ */
738
  public function prepare_variants_for_group( $feed_data = false ) {
 
 
 
 
 
 
 
 
739
 
740
+ /** @var \WC_Product_Variable $product */
741
+ $product = $this;
742
+ $final_variants = [];
 
 
743
 
744
+ try {
 
 
745
 
746
+ if ( ! $product->is_type( 'variable' ) ) {
747
+ throw new \Exception( 'prepare_variants_for_group called on non-variable product' );
 
 
 
748
  }
749
 
750
+ $variation_attributes = $product->get_variation_attributes();
 
 
 
 
 
751
 
752
+ if ( ! $variation_attributes ) {
753
+ return [];
 
 
 
 
 
 
754
  }
755
 
756
+ foreach ( array_keys( $product->get_attributes() ) as $name ) {
 
 
 
 
 
 
 
757
 
758
+ $label = wc_attribute_label( $name, $product );
 
 
 
 
 
 
 
 
759
 
760
+ if ( taxonomy_is_product_attribute( $name ) ) {
761
+ $key = $name;
762
+ } else {
763
+ // variation_attributes keys are labels for custom attrs for some reason
764
+ $key = $label;
765
+ }
766
 
767
+ if ( ! $key ) {
768
+ throw new \Exception( "Critical error: can't get attribute name or label!" );
769
+ }
770
+
771
+ if ( isset( $variation_attributes[ $key ] ) ) {
772
+ // array of the options (e.g. small, medium, large)
773
+ $option_values = $variation_attributes[ $key ];
774
+ } else {
775
+ // skip variations without valid attribute options
776
+ \WC_Facebookcommerce_Utils::log( $product->get_id() . ': No options for ' . $name );
777
+ continue;
778
+ }
779
+
780
+ // If this is a variable product, check default attribute.
781
+ // If it's being used, show it as the first option on Facebook.
782
+ if ( $first_option = $product->get_variation_default_attribute( $key ) ) {
783
+
784
+ $index = array_search( $first_option, $option_values, false );
785
+
786
+ unset( $option_values[ $index ] );
787
+
788
+ array_unshift( $option_values, $first_option );
789
+ }
790
+
791
+ if ( function_exists( 'taxonomy_is_product_attribute' ) && taxonomy_is_product_attribute( $name ) ) {
792
+ $option_values = $this->get_grouped_product_option_names( $key, $option_values );
793
+ }
794
+
795
+ /**
796
+ * For API approach, product_field need to start with 'custom_data:'
797
+ * @link https://developers.facebook.com/docs/marketing-api/reference/product-variant/
798
+ * Clean up variant name (e.g. pa_color should be color):
799
+ */
800
+ $name = \WC_Facebookcommerce_Utils::sanitize_variant_name( $name );
801
+
802
+ // for feed uploading, product field should remove prefix 'custom_data:'
803
+ if ( $feed_data ) {
804
+ $name = str_replace( 'custom_data:', '', $name );
805
+ }
806
+
807
+ $final_variants[] = [
808
  'product_field' => $name,
809
  'label' => $label,
810
  'options' => $option_values,
811
+ ];
812
+ }
813
+
814
+ } catch ( \Exception $e ) {
815
+
816
+ \WC_Facebookcommerce_Utils::fblog( $e->getMessage() );
817
+
818
+ return [];
819
  }
820
 
821
  return $final_variants;
 
822
  }
823
+
824
+
825
  }
826
 
827
  endif;
includes/fbproductfeed.php CHANGED
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
14
 
 
 
 
15
  if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
16
 
17
  /**
@@ -19,6 +22,17 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
19
  */
20
  class WC_Facebook_Product_Feed {
21
 
 
 
 
 
 
 
 
 
 
 
 
22
  const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
23
  const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
24
  const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
@@ -28,32 +42,307 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
28
  private $has_default_product_count = 0;
29
  private $no_default_product_count = 0;
30
 
31
- public function __construct(
32
- $facebook_catalog_id,
33
- $fbgraph,
34
- $feed_id = null ) {
 
 
 
 
 
35
  $this->facebook_catalog_id = $facebook_catalog_id;
36
  $this->fbgraph = $fbgraph;
37
  $this->feed_id = $feed_id;
38
  }
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  public function sync_all_products_using_feed() {
41
  $start_time = microtime( true );
42
  $this->log_feed_progress( 'Sync all products using feed' );
43
 
44
- if ( ! is_writable( dirname( __FILE__ ) ) ) {
45
- $this->log_feed_progress(
46
- 'Failure - Sync all products using feed, folder is not writable'
47
- );
48
- return false;
49
- }
 
50
 
51
- if ( ! $this->generate_productfeed_file() ) {
52
  $this->log_feed_progress(
53
- 'Failure - Sync all products using feed, feed file not generated'
54
  );
55
  return false;
56
  }
 
57
  $this->log_feed_progress( 'Sync all products using feed, feed file generated' );
58
 
59
  if ( ! $this->feed_id ) {
@@ -84,11 +373,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
84
  'Sync all products using feed, facebook upload created'
85
  );
86
 
87
- unlink(
88
- dirname( __FILE__ ) .
89
- DIRECTORY_SEPARATOR . ( self::FACEBOOK_CATALOG_FEED_FILENAME )
90
- );
91
-
92
  $total_product_count =
93
  $this->has_default_product_count + $this->no_default_product_count;
94
  $default_product_percentage =
@@ -110,34 +394,112 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
110
  return true;
111
  }
112
 
113
- public function generate_productfeed_file() {
114
- $this->log_feed_progress( 'Generating product feed file' );
 
 
 
 
 
 
 
 
115
  $post_ids = $this->get_product_wpid();
116
  $all_parent_product = array_map(
117
  function( $post_id ) {
118
- if ( get_post_type( $post_id ) == 'product_variation' ) {
119
  return wp_get_post_parent_id( $post_id );
120
  }
121
  },
122
  $post_ids
123
  );
 
124
  $all_parent_product = array_filter( array_unique( $all_parent_product ) );
125
- $product_ids = array_diff( $post_ids, $all_parent_product );
126
- return $this->write_product_feed_file( $product_ids );
127
  }
128
 
129
- public function write_product_feed_file( $wp_ids ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  try {
132
 
133
- $feed_file =
134
- fopen(
135
- dirname( __FILE__ ) . DIRECTORY_SEPARATOR .
136
- ( self::FACEBOOK_CATALOG_FEED_FILENAME ),
137
- 'w'
138
- );
139
 
140
- fwrite( $feed_file, $this->get_product_feed_header_row() );
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  $product_group_attribute_variants = array();
143
 
@@ -154,7 +516,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
154
  }
155
 
156
  // skip if not enabled for sync
157
- if ( $woo_product->woo_product instanceof \WC_Product && ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
158
  continue;
159
  }
160
 
@@ -162,15 +524,28 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
162
  $woo_product,
163
  $product_group_attribute_variants
164
  );
165
- fwrite( $feed_file, $product_data_as_feed_row );
 
 
 
166
  }
167
- fclose( $feed_file );
168
  wp_reset_postdata();
169
- return true;
 
 
170
  } catch ( Exception $e ) {
 
171
  WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
172
- return false;
 
173
  }
 
 
 
 
 
 
174
  }
175
 
176
  public function get_product_feed_header_row() {
@@ -180,44 +555,57 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
180
  'visibility,default_product,variant' . PHP_EOL;
181
  }
182
 
 
183
  /**
184
- * Assemble product payload in feed upload for initial sync.
185
- **/
186
- private function prepare_product_for_feed(
187
- $woo_product,
188
- &$attribute_variants ) {
 
 
 
189
  $product_data = $woo_product->prepare_product( null, true );
190
  $item_group_id = $product_data['retailer_id'];
 
191
  // prepare variant column for variable products
192
  $product_data['variant'] = '';
193
- if (
194
- WC_Facebookcommerce_Utils::is_variation_type( $woo_product->get_type() )
195
- ) {
196
  $parent_id = $woo_product->get_parent_id();
197
 
198
  if ( ! isset( $attribute_variants[ $parent_id ] ) ) {
199
- $parent_product = new WC_Facebook_Product( $parent_id );
200
-
201
- $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
202
- $variation_id = $parent_product->find_matching_product_variation();
203
- $variants_for_group = $parent_product->prepare_variants_for_group( true );
204
- $parent_attribute_values = array();
205
- $parent_attribute_values['gallery_urls'] = $gallery_urls;
206
- $parent_attribute_values['default_variant_id'] = $variation_id;
207
- $parent_attribute_values['item_group_id'] =
208
- WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product );
 
209
  foreach ( $variants_for_group as $variant ) {
210
- $parent_attribute_values[ $variant['product_field'] ] =
211
- $variant['options'];
 
212
  }
 
213
  // cache product group variants
214
  $attribute_variants[ $parent_id ] = $parent_attribute_values;
 
 
 
 
215
  }
216
- $parent_attribute_values = $attribute_variants[ $parent_id ];
217
- $variants_for_item =
218
- $woo_product->prepare_variants_for_item( $product_data );
219
- $variant_feed_column = array();
220
  foreach ( $variants_for_item as $variant_array ) {
 
221
  static::format_variant_for_feed(
222
  $variant_array['product_field'],
223
  $variant_array['options'][0],
@@ -225,8 +613,11 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
225
  $variant_feed_column
226
  );
227
  }
228
- if ( isset( $product_data['custom_data'] ) ) {
 
 
229
  foreach ( $product_data['custom_data'] as $product_field => $value ) {
 
230
  static::format_variant_for_feed(
231
  $product_field,
232
  $value,
@@ -235,29 +626,23 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
235
  );
236
  }
237
  }
 
238
  if ( ! empty( $variant_feed_column ) ) {
239
- $product_data['variant'] =
240
- '"' . implode( ',', $variant_feed_column ) . '"';
241
  }
 
242
  if ( isset( $parent_attribute_values['gallery_urls'] ) ) {
243
- $product_data['additional_image_urls'] =
244
- array_merge(
245
- $product_data['additional_image_urls'],
246
- $parent_attribute_values['gallery_urls']
247
- );
248
  }
 
249
  if ( isset( $parent_attribute_values['item_group_id'] ) ) {
250
  $item_group_id = $parent_attribute_values['item_group_id'];
251
  }
252
 
253
- $product_data['default_product'] =
254
- $parent_attribute_values['default_variant_id'] == $woo_product->id
255
- ? 'default'
256
- : '';
257
 
258
  // If this group has default variant value, log this product item
259
- if ( isset( $parent_attribute_values['default_variant_id'] ) &&
260
- ! empty( $parent_attribute_values['default_variant_id'] ) ) {
261
  $this->has_default_product_count++;
262
  } else {
263
  $this->no_default_product_count++;
@@ -266,7 +651,9 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
266
 
267
  // log simple product
268
  if ( ! isset( $product_data['default_product'] ) ) {
 
269
  $this->no_default_product_count++;
 
270
  $product_data['default_product'] = '';
271
  }
272
 
@@ -299,6 +686,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
299
  $product_data['variant'] . PHP_EOL;
300
  }
301
 
 
302
  private function create_feed() {
303
  $result = $this->fbgraph->create_feed(
304
  $this->facebook_catalog_id,
@@ -322,8 +710,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
322
  private function create_upload( $facebook_feed_id ) {
323
  $result = $this->fbgraph->create_upload(
324
  $facebook_feed_id,
325
- dirname( __FILE__ ) . DIRECTORY_SEPARATOR .
326
- ( self::FACEBOOK_CATALOG_FEED_FILENAME )
327
  );
328
  if ( is_null( $result ) || ! isset( $result['id'] ) || ! $result['id'] ) {
329
  $this->log_feed_progress( json_encode( $result ) );
@@ -386,9 +773,14 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
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
 
12
  exit;
13
  }
14
 
15
+ use SkyVerge\WooCommerce\Facebook\Products\Feed;
16
+ use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
17
+
18
  if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
19
 
20
  /**
22
  */
23
  class WC_Facebook_Product_Feed {
24
 
25
+
26
+ /** @var string transient name for storing the average feed generation time */
27
+ const TRANSIENT_AVERAGE_FEED_GENERATION_TIME = 'wc_facebook_average_feed_generation_time';
28
+
29
+ /** @var string product catalog feed file directory inside the uploads folder */
30
+ const UPLOADS_DIRECTORY = 'facebook_for_woocommerce';
31
+
32
+ /** @var string product catalog feed file name - %s will be replaced with a hash */
33
+ const FILE_NAME = 'product_catalog_%s.csv';
34
+
35
+
36
  const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
37
  const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
38
  const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
42
  private $has_default_product_count = 0;
43
  private $no_default_product_count = 0;
44
 
45
+ /**
46
+ * WC_Facebook_Product_Feed constructor.
47
+ *
48
+ * @param string|null $facebook_catalog_id Facebook catalog ID, if any
49
+ * @param \WC_Facebookcommerce_Graph_API|null $fbgraph Facebook Graph API instance
50
+ * @param string|null $feed_id Facebook feed ID, if any
51
+ */
52
+ public function __construct( $facebook_catalog_id = null, $fbgraph = null, $feed_id = null ) {
53
+
54
  $this->facebook_catalog_id = $facebook_catalog_id;
55
  $this->fbgraph = $fbgraph;
56
  $this->feed_id = $feed_id;
57
  }
58
 
59
+
60
+ /**
61
+ * Schedules a new feed generation.
62
+ *
63
+ * @since 1.11.0
64
+ */
65
+ public function schedule_feed_generation() {
66
+
67
+ // don't schedule another if one's already scheduled or in progress
68
+ if ( false !== as_next_scheduled_action( 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' ) ) {
69
+ return;
70
+ }
71
+
72
+ \WC_Facebookcommerce_Utils::log( 'Scheduling product catalog feed file generation' );
73
+
74
+ // if async priority actions are supported (AS 3.0+)
75
+ if ( function_exists( 'as_enqueue_async_action' ) ) {
76
+ as_enqueue_async_action( 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' );
77
+ } else {
78
+ as_schedule_single_action( time(), 'wc_facebook_generate_product_catalog_feed', [], 'facebook-for-woocommerce' );
79
+ }
80
+ }
81
+
82
+
83
+ /**
84
+ * Generates the product catalog feed.
85
+ *
86
+ * This replaces any previously generated feed file.
87
+ *
88
+ * @since 1.11.0
89
+ */
90
+ public function generate_feed() {
91
+
92
+ \WC_Facebookcommerce_Utils::log( 'Generating a fresh product feed file' );
93
+
94
+ try {
95
+
96
+ $start_time = microtime( true );
97
+
98
+ $this->generate_productfeed_file();
99
+
100
+ $generation_time = microtime( true ) - $start_time;
101
+
102
+ $this->set_feed_generation_time_with_decay( $generation_time );
103
+
104
+ \WC_Facebookcommerce_Utils::log( 'Product feed file generated' );
105
+
106
+ } catch ( \Exception $exception ) {
107
+
108
+ \WC_Facebookcommerce_Utils::log( $exception->getMessage() );
109
+ }
110
+ }
111
+
112
+
113
+ /**
114
+ * Sets the average feed generation time with a 25% decay.
115
+ *
116
+ * @since 1.11.0
117
+ *
118
+ * @param float $generation_time last generation time
119
+ */
120
+ private function set_feed_generation_time_with_decay( $generation_time ) {
121
+
122
+ // update feed generation time estimate w/ 25% decay.
123
+ $existing_generation_time = $this->get_average_feed_generation_time();
124
+
125
+ if ( $generation_time < $existing_generation_time ) {
126
+ $generation_time = $generation_time * 0.25 + $existing_generation_time * 0.75;
127
+ }
128
+
129
+ $this->set_average_feed_generation_time( $generation_time );
130
+ }
131
+
132
+
133
+ /**
134
+ * Sets the average feed generation time.
135
+ *
136
+ * @since 1.11.0
137
+ *
138
+ * @param float $time generation time
139
+ */
140
+ private function set_average_feed_generation_time( $time ) {
141
+
142
+ set_transient( self::TRANSIENT_AVERAGE_FEED_GENERATION_TIME, $time );
143
+ }
144
+
145
+
146
+ /**
147
+ * Gets the estimated feed generation time.
148
+ *
149
+ * Performs a dry run and returns either the dry run time or last average estimated time, whichever is higher.
150
+ *
151
+ * @since 1.11.0
152
+ *
153
+ * @return int
154
+ */
155
+ public function get_estimated_feed_generation_time() {
156
+
157
+ $estimate = $this->estimate_generation_time();
158
+ $average = $this->get_average_feed_generation_time();
159
+
160
+ return (int) max( $estimate, $average );
161
+ }
162
+
163
+
164
+ /**
165
+ * Estimates the feed generation time.
166
+ *
167
+ * Runs a dry-run generation of a subset of products, then extrapolates that out to the full catalog size. Also
168
+ * adds a bit of buffer time.
169
+ *
170
+ * @since 1.11.0
171
+ *
172
+ * @return float
173
+ */
174
+ private function estimate_generation_time() {
175
+
176
+ $product_ids = $this->get_product_ids();
177
+ $total_products = count( $product_ids );
178
+ $sample_size = $this->get_feed_generation_estimate_sample_size();
179
+ $buffer_time = $this->get_feed_generation_buffer_time();
180
+
181
+ if ( $total_products > 0 ) {
182
+
183
+ if ( $total_products < $sample_size ) {
184
+
185
+ $sample_size = $total_products;
186
+
187
+ } else {
188
+
189
+ $product_ids = array_slice( $product_ids, 0, $sample_size );
190
+ }
191
+
192
+ $start_time = microtime( true );
193
+
194
+ $this->write_product_feed_file( $product_ids, true );
195
+
196
+ $end_time = microtime( true );
197
+
198
+ $time_spent = $end_time - $start_time;
199
+
200
+ // estimated Time = 150% of Linear extrapolation of the time to generate n products + buffer time.
201
+ $time_estimate = $time_spent * $total_products / $sample_size * 1.5 + $buffer_time;
202
+
203
+ } else {
204
+
205
+ $time_estimate = $buffer_time;
206
+ }
207
+
208
+ WC_Facebookcommerce_Utils::log( 'Feed Generation Time Estimate: '. $time_estimate );
209
+
210
+ return $time_estimate;
211
+ }
212
+
213
+
214
+ /**
215
+ * Gets the average feed generation time.
216
+ *
217
+ * @since 1.11.0
218
+ *
219
+ * @return float
220
+ */
221
+ private function get_average_feed_generation_time() {
222
+
223
+ return get_transient( self::TRANSIENT_AVERAGE_FEED_GENERATION_TIME );
224
+ }
225
+
226
+
227
+ /**
228
+ * Gets the number of products to use when estimating the feed file generation time.
229
+ *
230
+ * @since 1.11.0
231
+ *
232
+ * @return int
233
+ */
234
+ private function get_feed_generation_estimate_sample_size() {
235
+
236
+ /**
237
+ * Filters the number of products to use when estimating the feed file generation time.
238
+ *
239
+ * @since 1.11.0
240
+ *
241
+ * @param int $sample_size number of products to use when estimating the feed file generation time
242
+ */
243
+ $sample_size = (int) apply_filters( 'wc_facebook_product_catalog_feed_generation_estimate_sample_size', 200 );
244
+
245
+ return max( $sample_size, 100 );
246
+ }
247
+
248
+
249
+ /**
250
+ * Gets the number of seconds to add as a buffer when estimating the feed file generation time.
251
+ *
252
+ * @since 1.11.0
253
+ *
254
+ * @return int
255
+ */
256
+ private function get_feed_generation_buffer_time() {
257
+
258
+ /**
259
+ * Filters the number of seconds to add as a buffer when estimating the feed file generation time.
260
+ *
261
+ * @since 1.11.0
262
+ *
263
+ * @param int $time number of seconds to add as a buffer when estimating the feed file generation time
264
+ */
265
+ $buffer_time = (int) apply_filters( 'wc_facebook_product_catalog_feed_generation_buffer_time', 30 );
266
+
267
+ return max( $buffer_time, 5 );
268
+ }
269
+
270
+
271
+ /**
272
+ * Gets the product catalog feed file path.
273
+ *
274
+ * @since 1.11.0
275
+ *
276
+ * @return string
277
+ */
278
+ public function get_file_path() {
279
+
280
+ /**
281
+ * Filters the product catalog feed file path.
282
+ *
283
+ * @since 1.11.0
284
+ *
285
+ * @param string $file_path the file path
286
+ */
287
+ return apply_filters( 'wc_facebook_product_catalog_feed_file_path', "{$this->get_file_directory()}/{$this->get_file_name()}" );
288
+ }
289
+
290
+
291
+ /**
292
+ * Gets the product catalog feed file directory.
293
+ *
294
+ * @since 1.11.0
295
+ *
296
+ * @return string
297
+ */
298
+ public function get_file_directory() {
299
+
300
+ $uploads_directory = wp_upload_dir( null, false );
301
+
302
+ return trailingslashit( $uploads_directory['basedir'] ) . self::UPLOADS_DIRECTORY;
303
+ }
304
+
305
+
306
+ /**
307
+ * Gets the product catalog feed file name.
308
+ *
309
+ * @since 1.11.0
310
+ *
311
+ * @return string
312
+ */
313
+ public function get_file_name() {
314
+
315
+ $file_name = sprintf( self::FILE_NAME, wp_hash( Feed::get_feed_secret() ) );
316
+
317
+ /**
318
+ * Filters the product catalog feed file name.
319
+ *
320
+ * @since 1.11.0
321
+ *
322
+ * @param string $file_name the file name
323
+ */
324
+ return apply_filters( 'wc_facebook_product_catalog_feed_file_name', $file_name );
325
+ }
326
+
327
+
328
  public function sync_all_products_using_feed() {
329
  $start_time = microtime( true );
330
  $this->log_feed_progress( 'Sync all products using feed' );
331
 
332
+ try {
333
+
334
+ if ( ! $this->generate_productfeed_file() ) {
335
+ throw new Framework\SV_WC_Plugin_Exception( 'Feed file not generated' );
336
+ }
337
+
338
+ } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
339
 
 
340
  $this->log_feed_progress(
341
+ 'Failure - Sync all products using feed. ' . $exception->getMessage()
342
  );
343
  return false;
344
  }
345
+
346
  $this->log_feed_progress( 'Sync all products using feed, feed file generated' );
347
 
348
  if ( ! $this->feed_id ) {
373
  'Sync all products using feed, facebook upload created'
374
  );
375
 
 
 
 
 
 
376
  $total_product_count =
377
  $this->has_default_product_count + $this->no_default_product_count;
378
  $default_product_percentage =
394
  return true;
395
  }
396
 
397
+
398
+ /**
399
+ * Gets the product IDs that will be included in the feed file.
400
+ *
401
+ * @since 1.11.0
402
+ *
403
+ * @return int[]
404
+ */
405
+ private function get_product_ids() {
406
+
407
  $post_ids = $this->get_product_wpid();
408
  $all_parent_product = array_map(
409
  function( $post_id ) {
410
+ if ( 'product_variation' === get_post_type( $post_id ) ) {
411
  return wp_get_post_parent_id( $post_id );
412
  }
413
  },
414
  $post_ids
415
  );
416
+
417
  $all_parent_product = array_filter( array_unique( $all_parent_product ) );
418
+
419
+ return array_diff( $post_ids, $all_parent_product );
420
  }
421
 
422
+
423
+ /**
424
+ * Generates the product catalog feed file.
425
+ *
426
+ * @return bool
427
+ * @throws Framework\SV_WC_Plugin_Exception
428
+ */
429
+ public function generate_productfeed_file() {
430
+
431
+ if ( ! wp_mkdir_p( $this->get_file_directory() ) ) {
432
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Could not create product catalog feed directory', 'facebook-for-woocommerce' ), 500 );
433
+ }
434
+
435
+ $this->create_files_to_protect_product_feed_directory();
436
+
437
+ return $this->write_product_feed_file( $this->get_product_ids() );
438
+ }
439
+
440
+
441
+ /**
442
+ * Creates files in the catalog feed directory to prevent directory listing and hotlinking.
443
+ *
444
+ * @since 1.11.0
445
+ */
446
+ private function create_files_to_protect_product_feed_directory() {
447
+
448
+ $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
449
+
450
+ $files = [
451
+ [
452
+ 'base' => $catalog_feed_directory,
453
+ 'file' => 'index.html',
454
+ 'content' => '',
455
+ ],
456
+ [
457
+ 'base' => $catalog_feed_directory,
458
+ 'file' => '.htaccess',
459
+ 'content' => 'deny from all',
460
+ ],
461
+ ];
462
+
463
+ foreach ( $files as $file ) {
464
+
465
+ if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
466
+
467
+ if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
468
+
469
+ fwrite( $file_handle, $file['content'] );
470
+ fclose( $file_handle );
471
+ }
472
+ }
473
+ }
474
+ }
475
+
476
+
477
+ /**
478
+ * Writes the product catalog feed file with data for the given product IDs.
479
+ *
480
+ * @since 1.11.0
481
+ *
482
+ * @param int[] $wp_ids product IDs
483
+ * @param bool $is_dry_run whether this is a dry run or the file should be written
484
+ * @return bool
485
+ */
486
+ public function write_product_feed_file( $wp_ids, $is_dry_run = false ) {
487
 
488
  try {
489
 
 
 
 
 
 
 
490
 
491
+ if ( ! $is_dry_run ) {
492
+
493
+ $file_path = $this->get_file_path();
494
+
495
+ $feed_file = @fopen( $this->get_file_path(), 'w' );
496
+
497
+ if ( false === $feed_file || ! is_writable( $file_path ) ) {
498
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog feed file for writing', 'facebook-for-woocommerce' ), 500 );
499
+ }
500
+
501
+ fwrite( $feed_file, $this->get_product_feed_header_row() );
502
+ }
503
 
504
  $product_group_attribute_variants = array();
505
 
516
  }
517
 
518
  // skip if not enabled for sync
519
+ if ( $woo_product->woo_product instanceof \WC_Product && ! SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
520
  continue;
521
  }
522
 
524
  $woo_product,
525
  $product_group_attribute_variants
526
  );
527
+
528
+ if ( ! empty( $feed_file ) ) {
529
+ fwrite( $feed_file, $product_data_as_feed_row );
530
+ }
531
  }
532
+
533
  wp_reset_postdata();
534
+
535
+ $written = true;
536
+
537
  } catch ( Exception $e ) {
538
+
539
  WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
540
+
541
+ $written = false;
542
  }
543
+
544
+ if ( ! empty( $feed_file ) ) {
545
+ fclose( $feed_file );
546
+ }
547
+
548
+ return $written;
549
  }
550
 
551
  public function get_product_feed_header_row() {
555
  'visibility,default_product,variant' . PHP_EOL;
556
  }
557
 
558
+
559
  /**
560
+ * Assembles product payload in feed upload for initial sync.
561
+ *
562
+ * @param \WC_Facebook_Product $woo_product WooCommerce product object normalized by Facebook
563
+ * @param array $attribute_variants passed by reference
564
+ * @return string product feed line data
565
+ */
566
+ private function prepare_product_for_feed( $woo_product, &$attribute_variants ) {
567
+
568
  $product_data = $woo_product->prepare_product( null, true );
569
  $item_group_id = $product_data['retailer_id'];
570
+
571
  // prepare variant column for variable products
572
  $product_data['variant'] = '';
573
+
574
+ if ( $woo_product->is_type( 'variation' ) ) {
575
+
576
  $parent_id = $woo_product->get_parent_id();
577
 
578
  if ( ! isset( $attribute_variants[ $parent_id ] ) ) {
579
+
580
+ $parent_product = new \WC_Facebook_Product( $parent_id );
581
+ $gallery_urls = array_filter( $parent_product->get_gallery_urls() );
582
+ $variation_id = $parent_product->find_matching_product_variation();
583
+ $variants_for_group = $parent_product->prepare_variants_for_group( true );
584
+ $parent_attribute_values = [
585
+ 'gallery_urls' => $gallery_urls,
586
+ 'default_variant_id' => $variation_id,
587
+ 'item_group_id' => \WC_Facebookcommerce_Utils::get_fb_retailer_id( $parent_product ),
588
+ ];
589
+
590
  foreach ( $variants_for_group as $variant ) {
591
+ if ( isset( $variant['product_field'], $variant['options'] ) ) {
592
+ $parent_attribute_values[ $variant['product_field'] ] = $variant['options'];
593
+ }
594
  }
595
+
596
  // cache product group variants
597
  $attribute_variants[ $parent_id ] = $parent_attribute_values;
598
+
599
+ } else {
600
+
601
+ $parent_attribute_values = $attribute_variants[ $parent_id ];
602
  }
603
+
604
+ $variants_for_item = $woo_product->prepare_variants_for_item( $product_data );
605
+ $variant_feed_column = [];
606
+
607
  foreach ( $variants_for_item as $variant_array ) {
608
+
609
  static::format_variant_for_feed(
610
  $variant_array['product_field'],
611
  $variant_array['options'][0],
613
  $variant_feed_column
614
  );
615
  }
616
+
617
+ if ( isset( $product_data['custom_data'] ) && is_array( $product_data['custom_data'] ) ) {
618
+
619
  foreach ( $product_data['custom_data'] as $product_field => $value ) {
620
+
621
  static::format_variant_for_feed(
622
  $product_field,
623
  $value,
626
  );
627
  }
628
  }
629
+
630
  if ( ! empty( $variant_feed_column ) ) {
631
+ $product_data['variant'] = '"' . implode( ',', $variant_feed_column ) . '"';
 
632
  }
633
+
634
  if ( isset( $parent_attribute_values['gallery_urls'] ) ) {
635
+ $product_data['additional_image_urls'] = array_merge( $product_data['additional_image_urls'], $parent_attribute_values['gallery_urls'] );
 
 
 
 
636
  }
637
+
638
  if ( isset( $parent_attribute_values['item_group_id'] ) ) {
639
  $item_group_id = $parent_attribute_values['item_group_id'];
640
  }
641
 
642
+ $product_data['default_product'] = $parent_attribute_values['default_variant_id'] == $woo_product->id ? 'default' : '';
 
 
 
643
 
644
  // If this group has default variant value, log this product item
645
+ if ( isset( $parent_attribute_values['default_variant_id'] ) && ! empty( $parent_attribute_values['default_variant_id'] ) ) {
 
646
  $this->has_default_product_count++;
647
  } else {
648
  $this->no_default_product_count++;
651
 
652
  // log simple product
653
  if ( ! isset( $product_data['default_product'] ) ) {
654
+
655
  $this->no_default_product_count++;
656
+
657
  $product_data['default_product'] = '';
658
  }
659
 
686
  $product_data['variant'] . PHP_EOL;
687
  }
688
 
689
+
690
  private function create_feed() {
691
  $result = $this->fbgraph->create_feed(
692
  $this->facebook_catalog_id,
710
  private function create_upload( $facebook_feed_id ) {
711
  $result = $this->fbgraph->create_upload(
712
  $facebook_feed_id,
713
+ $this->get_file_path()
 
714
  );
715
  if ( is_null( $result ) || ! isset( $result['id'] ) || ! $result['id'] ) {
716
  $this->log_feed_progress( json_encode( $result ) );
773
  * @return string
774
  */
775
  public function is_upload_complete( &$settings ) {
776
+
777
+ $upload_id = facebook_for_woocommerce()->get_integration()->get_upload_id();
778
+ $result = $this->fbgraph->get_upload_status( $upload_id );
779
+
780
  if ( is_wp_error( $result ) || ! isset( $result['body'] ) ) {
781
+
782
  $this->log_feed_progress( json_encode( $result ) );
783
+
784
  return 'error';
785
  }
786
 
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.2
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
@@ -39,6 +39,17 @@ When opening a bug on GitHub, please give us as many details as possible.
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
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.11.0
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
39
 
40
  == Changelog ==
41
 
42
+ = 2020.04.23 - version 1.11.0 =
43
+ * Tweak - Sync products using Facebook's feed pull method
44
+ * Fix - When filtering products by sync enabled status, make sure variable products with sync disabled status do not show up in results
45
+ * Fix - Make sure that the Facebook sync enabled and catalog visibility columns are properly displayed on narrow screen sizes on some browsers
46
+ * Fix - Do not show a confirmation modal when saving a variable product that was previously synced but belongs now to a term excluded from sync
47
+ * Fix - Ensure variable products excluded from sync are not synced in Facebook
48
+ * Fix - Trigger a modal prompt when attempting to enable sync for variations of a variable product that belongs to a term excluded from sync
49
+ * Fix - Address potential PHP warnings in the product feed with non-standard product variations introduced by third party plugins
50
+ * Fix - Fix a JavaScript error triggered on the settings page while trying to excluded terms from sync
51
+ * Fix - Fix a JavaScript error triggered when saving a product and using checkboxes for tags
52
+
53
  = 2020.03.17 - version 1.10.2 =
54
  * Tweak - Add a setting to easily enable debug logging
55
  * Tweak - Allow third party plugins and themes to track an add-to-cart event on added_to_cart JS event