Facebook for WooCommerce - Version 2.6.6

Version Description

  • 2021-11-03 =
  • New - Memory improved feed generation process. #2099
  • New - Add compatibility with the WooCommerce checkout block. #2095
  • New - Track batched feed generation time in the tracker snapshots. #2104
  • New - Track usage of the new style feed generator in the tracker snapshots. #2103
  • New - Hide headers in logs for better visibility. #2093
  • Dev - Update composer dependencies. #2090
  • New - Add no synchronization reason to the product edit screen in the Facebook meta box. #1937
  • Fix - Use published variations only for the default variation. #2091
Download this release

Release Info

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

Code changes from version 2.6.5 to 2.6.6

README.md CHANGED
@@ -11,6 +11,9 @@ The best place to get support is the [WordPress.org Facebook for WooCommerce for
11
 
12
  If you have a WooCommerce.com account, you can [start a chat or open a ticket on WooCommerce.com](https://woocommerce.com/my-account/create-a-ticket/).
13
 
 
 
 
14
  ## Development
15
  ### Developing
16
  - Clone this repository into the `wp-content/plugins/` folder your WooCommerce development environment.
11
 
12
  If you have a WooCommerce.com account, you can [start a chat or open a ticket on WooCommerce.com](https://woocommerce.com/my-account/create-a-ticket/).
13
 
14
+ ### Logging
15
+ The plugin offers logging that can help debug various problems. You can enable debug mode in the main plugin settings panel under the `Enable debug mode` section.
16
+ By default plugin omits headers in the requests to make the logs more readable. If debugging with headers is necessary you can enable the headers in the logs by setting `wc_facebook_request_headers_in_debug_log` option to true.
17
  ## Development
18
  ### Developing
19
  - Clone this repository into the `wp-content/plugins/` folder your WooCommerce development environment.
changelog.txt CHANGED
@@ -1,6 +1,16 @@
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
- 2021-09-16 - version 2.6.5
 
 
 
 
 
 
 
 
 
 
4
  * Fix - Incorrect `is_readable()` usage when loading Integration classes.
5
  * Tweak - WC 5.7 compatibility.
6
  * Tweak - WP 5.8 compatibility.
1
  *** Facebook for WooCommerce Changelog ***
2
 
3
+ 2021-11-03 - version 2.6.6
4
+ * New - Memory improved feed generation process. #2099
5
+ * New - Add compatibility with the WooCommerce checkout block. #2095
6
+ * New - Track batched feed generation time in the tracker snapshots. #2104
7
+ * New - Track usage of the new style feed generator in the tracker snapshots. #2103
8
+ * New - Hide headers in logs for better visibility. #2093
9
+ * Dev - Update composer dependencies. #2090
10
+ * New - Add no synchronization reason to the product edit screen in the Facebook meta box. #1937
11
+ * Fix - Use published variations only for the default variation. #2091
12
+
13
+ 2021-09-16 - version 2.6.5
14
  * Fix - Incorrect `is_readable()` usage when loading Integration classes.
15
  * Tweak - WC 5.7 compatibility.
16
  * Tweak - WP 5.8 compatibility.
class-wc-facebookcommerce.php CHANGED
@@ -435,7 +435,17 @@ if ( ! class_exists( 'WC_Facebookcommerce' ) ) :
435
  return;
436
  }
437
 
438
- parent::log_api_request( $request, $response, $log_id );
 
 
 
 
 
 
 
 
 
 
439
  }
440
 
441
  /**
435
  return;
436
  }
437
 
438
+ // Maybe remove headers from the debug log.
439
+ if( ! $this->get_integration()->are_headers_requested_for_debug() ) {
440
+ unset( $request['headers'] );
441
+ unset( $response['headers'] );
442
+ }
443
+
444
+ $this->log( $this->get_api_log_message( $request ), $log_id );
445
+
446
+ if ( ! empty( $response ) ) {
447
+ $this->log( $this->get_api_log_message( $response ), $log_id );
448
+ }
449
  }
450
 
451
  /**
facebook-commerce-events-tracker.php CHANGED
@@ -130,6 +130,9 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
130
  add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'inject_purchase_event' ) );
131
  add_action( 'woocommerce_thankyou', array( $this, 'inject_purchase_event' ), 40 );
132
 
 
 
 
133
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
134
  add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );
135
  }
@@ -938,6 +941,29 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
938
  $order->save_meta_data();
939
  }
940
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
941
 
942
  /**
943
  * Triggers a Subscribe event when a given order contains subscription products.
@@ -981,26 +1007,6 @@ if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) :
981
  }
982
 
983
 
984
- /**
985
- * Triggers a Purchase event.
986
- *
987
- * Duplicate of {@see \WC_Facebookcommerce_EventsTracker::inject_purchase_event()}
988
- *
989
- * TODO remove this deprecated method by version 2.0.0 or by March 2020 {FN 2020-03-20}
990
- *
991
- * @internal
992
- * @deprecated since 1.11.0
993
- *
994
- * @param int $order_id order identifier
995
- */
996
- public function inject_gateway_purchase_event( $order_id ) {
997
-
998
- wc_deprecated_function( __METHOD__, '1.11.0', __CLASS__ . '::inject_purchase_event()' );
999
-
1000
- $this->inject_purchase_event( $order_id );
1001
- }
1002
-
1003
-
1004
  /** Contact Form 7 Support **/
1005
  public function inject_lead_event_hook() {
1006
  add_action( 'wp_footer', array( $this, 'inject_lead_event' ), 11 );
130
  add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'inject_purchase_event' ) );
131
  add_action( 'woocommerce_thankyou', array( $this, 'inject_purchase_event' ), 40 );
132
 
133
+ // Checkout update order meta from the Checkout Block.
134
+ add_action( '__experimental_woocommerce_blocks_checkout_update_order_meta', array( $this, 'inject_order_meta_event_for_checkout_block_flow' ) );
135
+
136
  // TODO move this in some 3rd party plugin integrations handler at some point {FN 2020-03-20}
137
  add_action( 'wpcf7_contact_form', array( $this, 'inject_lead_event_hook' ), 11 );
138
  }
941
  $order->save_meta_data();
942
  }
943
 
944
+ /**
945
+ * Inject order meta gor WooCommerce Checkout Blocks flow.
946
+ * The blocks flow does not trigger the woocommerce_checkout_update_order_meta so we can't rely on it.
947
+ * The Checkout Block has its own ( so far ) experimental hook that allows us to inject the meta at
948
+ * the appropriate moment: __experimental_woocommerce_blocks_checkout_update_order_meta.
949
+ *
950
+ * @since 2.6.6
951
+ *
952
+ * @param WC_Order $order Order object.
953
+ */
954
+ public function inject_order_meta_event_for_checkout_block_flow( $order ) {
955
+
956
+ $event_name = 'Purchase';
957
+
958
+ if ( ! $this->is_pixel_enabled() || $this->pixel->is_last_event( $event_name ) ) {
959
+ return;
960
+ }
961
+
962
+ $order_placed_meta = '_wc_' . facebook_for_woocommerce()->get_id() . '_order_placed';
963
+ $order->update_meta_data( $order_placed_meta, 'yes' );
964
+ $order->save_meta_data();
965
+ }
966
+
967
 
968
  /**
969
  * Triggers a Subscribe event when a given order contains subscription products.
1007
  }
1008
 
1009
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1010
  /** Contact Form 7 Support **/
1011
  public function inject_lead_event_hook() {
1012
  add_action( 'wp_footer', array( $this, 'inject_lead_event' ), 11 );
facebook-commerce.php CHANGED
@@ -101,6 +101,12 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
101
  /** @var string the "debug mode" setting ID */
102
  const SETTING_ENABLE_DEBUG_MODE = 'wc_facebook_enable_debug_mode';
103
 
 
 
 
 
 
 
104
  /** @var string the standard product description mode name */
105
  const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';
106
 
@@ -369,7 +375,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
369
 
370
  add_action( 'before_delete_post', array( $this, 'on_product_delete' ) );
371
 
372
- add_action( 'add_meta_boxes', array( $this, 'fb_product_metabox' ), 10, 1 );
373
 
374
  add_action(
375
  'transition_post_status',
@@ -416,9 +422,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
416
  // Must be outside of admin for cron to schedule correctly.
417
  add_action( 'sync_all_fb_products_using_feed', array( $this, 'handle_scheduled_resync_action' ), self::FB_PRIORITY_MID );
418
 
419
- // Handle the special background feed generation action.
420
- add_action( 'wc_facebook_generate_product_catalog_feed', array( $this, 'handle_generate_product_catalog_feed' ) );
421
-
422
  if ( $this->get_facebook_pixel_id() ) {
423
  $aam_settings = $this->load_aam_settings_of_pixel();
424
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
@@ -591,102 +594,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
591
  wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_columns_content()' );
592
  }
593
 
594
-
595
- public function fb_product_metabox() {
596
- $ajax_data = array(
597
- 'nonce' => wp_create_nonce( 'wc_facebook_metabox_jsx' ),
598
- );
599
- wp_enqueue_script(
600
- 'wc_facebook_metabox_jsx',
601
- facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/metabox.js',
602
- array(),
603
- \WC_Facebookcommerce::PLUGIN_VERSION
604
- );
605
- wp_localize_script(
606
- 'wc_facebook_metabox_jsx',
607
- 'wc_facebook_metabox_jsx',
608
- $ajax_data
609
- );
610
-
611
- add_meta_box(
612
- 'facebook_metabox', // Meta box ID
613
- 'Facebook', // Meta box Title
614
- array( $this, 'fb_product_meta_box_html' ), // Callback
615
- 'product', // Screen to which to add the meta box
616
- 'side' // Context
617
- );
618
- }
619
-
620
-
621
- /**
622
- * Renders the content of the product meta box.
623
- */
624
- public function fb_product_meta_box_html() {
625
- global $post;
626
-
627
- $woo_product = new WC_Facebook_Product( $post->ID );
628
- $fb_product_group_id = null;
629
-
630
- if ( $woo_product->woo_product instanceof \WC_Product && Products::product_should_be_synced( $woo_product->woo_product ) ) {
631
- $fb_product_group_id = $this->get_product_fbid( self::FB_PRODUCT_GROUP_ID, $post->ID, $woo_product );
632
- }
633
-
634
- ?>
635
- <span id="fb_metadata">
636
- <?php
637
-
638
- if ( $fb_product_group_id ) {
639
-
640
- ?>
641
-
642
- <?php echo esc_html__( 'Facebook ID:', 'facebook-for-woocommerce' ); ?> <a href="https://facebook.com/<?php echo esc_attr( $fb_product_group_id ); ?>"
643
- target="_blank"><?php echo esc_html( $fb_product_group_id ); ?></a>
644
-
645
- <?php if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) : ?>
646
-
647
- <?php if ( $product_item_ids_by_variation_id = $this->get_variation_product_item_ids( $woo_product, $fb_product_group_id ) ) : ?>
648
-
649
- <p>
650
- <?php echo esc_html__( 'Variant IDs:', 'facebook-for-woocommerce' ); ?><br/>
651
-
652
- <?php foreach ( $product_item_ids_by_variation_id as $variation_id => $product_item_id ) : ?>
653
-
654
- <?php echo esc_html( $variation_id ); ?>: <a href="https://facebook.com/<?php echo esc_attr( $product_item_id ); ?>"
655
- target="_blank"><?php echo esc_html( $product_item_id ); ?></a><br/>
656
-
657
- <?php endforeach; ?>
658
- </p>
659
-
660
- <?php endif; ?>
661
-
662
- <?php endif; ?>
663
-
664
- <input name="is_product_page" type="hidden" value="1"/>
665
-
666
- <p/>
667
- <a href="#" onclick="fb_reset_product( <?php echo esc_js( $post->ID ); ?> )">
668
- <?php echo esc_html__( 'Reset Facebook metadata', 'facebook-for-woocommerce' ); ?>
669
- </a>
670
-
671
- <p/>
672
- <a href="#" onclick="fb_delete_product( <?php echo esc_js( $post->ID ); ?> )">
673
- <?php echo esc_html__( 'Delete product(s) on Facebook', 'facebook-for-woocommerce' ); ?>
674
- </a>
675
-
676
- <?php
677
-
678
- } else {
679
-
680
- ?>
681
- <b><?php echo esc_html__( 'This product is not yet synced to Facebook.', 'facebook-for-woocommerce' ); ?></b>
682
- <?php
683
- }
684
-
685
- ?>
686
- </span>
687
- <?php
688
- }
689
-
690
  /**
691
  * Returns graph API client object.
692
  *
@@ -706,7 +613,7 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
706
  * @param string $product_group_id product group ID
707
  * @return array
708
  */
709
- private function get_variation_product_item_ids( $product, $product_group_id ) {
710
 
711
  $product_item_ids_by_variation_id = array();
712
  $missing_product_item_ids = array();
@@ -1515,21 +1422,14 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1515
  return;
1516
  }
1517
 
1518
- // figure out the matching default variation
1519
- $default_product_fbid = null;
1520
- $woo_default_variation = $this->get_product_group_default_variation( $woo_product );
1521
-
1522
- if ( $woo_default_variation ) {
1523
- $default_product_fbid = $this->get_product_fbid(
1524
- self::FB_PRODUCT_ITEM_ID,
1525
- $woo_default_variation['variation_id']
1526
- );
1527
- }
1528
 
1529
  $product_group_data = array(
1530
  'variants' => $variants,
1531
  );
1532
 
 
 
 
1533
  if ( $default_product_fbid ) {
1534
  $product_group_data['default_product_id'] = $default_product_fbid;
1535
  }
@@ -1555,13 +1455,17 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1555
 
1556
  /**
1557
  * Determines if there is a matching variation for the default attributes.
 
 
 
 
1558
  *
1559
  * @since 2.1.2
1560
  *
1561
  * @param \WC_Facebook_Product $woo_product
1562
- * @return array|null
1563
  */
1564
- private function get_product_group_default_variation( $woo_product ) {
1565
 
1566
  $default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' );
1567
 
@@ -1569,19 +1473,41 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
1569
  return null;
1570
  }
1571
 
1572
- $default_variation = null;
1573
- $product_variations = $woo_product->woo_product->get_available_variations();
 
 
 
 
1574
 
 
1575
  foreach ( $product_variations as $variation ) {
1576
 
1577
- $variation_attributes = $this->get_product_variation_attributes( $variation );
 
 
 
 
 
 
 
 
 
1578
 
1579
- $matching_attributes = array_intersect_assoc( $default_attributes, $variation_attributes );
 
 
1580
 
1581
- if ( count( $matching_attributes ) === count( $variation_attributes ) ) {
1582
- $default_variation = $variation;
 
 
1583
  break;
 
 
 
1584
  }
 
1585
  }
1586
 
1587
  return $default_variation;
@@ -3267,6 +3193,29 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3267
  return (bool) apply_filters( 'wc_facebook_is_debug_mode_enabled', 'yes' === get_option( self::SETTING_ENABLE_DEBUG_MODE ), $this );
3268
  }
3269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3270
 
3271
  /***
3272
  * Determines if the feed has been migrated from FBE 1 to FBE 1.5
@@ -3870,26 +3819,6 @@ class WC_Facebookcommerce_Integration extends WC_Integration {
3870
  }
3871
  }
3872
 
3873
- /**
3874
- * Handles the schedule feed generation action, triggered by the REST API.
3875
- *
3876
- * @since 1.11.0
3877
- */
3878
- public function handle_generate_product_catalog_feed() {
3879
-
3880
- $feed_handler = new WC_Facebook_Product_Feed();
3881
-
3882
- try {
3883
-
3884
- $feed_handler->generate_feed();
3885
-
3886
- } catch ( \Exception $exception ) {
3887
-
3888
- WC_Facebookcommerce_Utils::log( 'Error generating product catalog feed. ' . $exception->getMessage() );
3889
- }
3890
- }
3891
-
3892
-
3893
  /** Deprecated methods ********************************************************************************************/
3894
 
3895
 
101
  /** @var string the "debug mode" setting ID */
102
  const SETTING_ENABLE_DEBUG_MODE = 'wc_facebook_enable_debug_mode';
103
 
104
+ /** @var string the "debug mode" setting ID */
105
+ const SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR = 'wc_facebook_enable_new_style_feed_generator';
106
+
107
+ /** @var string request headers in the debug log */
108
+ const SETTING_REQUEST_HEADERS_IN_DEBUG_MODE = 'wc_facebook_request_headers_in_debug_log';
109
+
110
  /** @var string the standard product description mode name */
111
  const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';
112
 
375
 
376
  add_action( 'before_delete_post', array( $this, 'on_product_delete' ) );
377
 
378
+ add_action( 'add_meta_boxes', 'SkyVerge\WooCommerce\Facebook\Admin\Product_Sync_Meta_Box::register', 10, 1 );
379
 
380
  add_action(
381
  'transition_post_status',
422
  // Must be outside of admin for cron to schedule correctly.
423
  add_action( 'sync_all_fb_products_using_feed', array( $this, 'handle_scheduled_resync_action' ), self::FB_PRIORITY_MID );
424
 
 
 
 
425
  if ( $this->get_facebook_pixel_id() ) {
426
  $aam_settings = $this->load_aam_settings_of_pixel();
427
  $user_info = WC_Facebookcommerce_Utils::get_user_info( $aam_settings );
594
  wc_deprecated_function( __METHOD__, '1.10.0', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_columns_content()' );
595
  }
596
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  /**
598
  * Returns graph API client object.
599
  *
613
  * @param string $product_group_id product group ID
614
  * @return array
615
  */
616
+ public function get_variation_product_item_ids( $product, $product_group_id ) {
617
 
618
  $product_item_ids_by_variation_id = array();
619
  $missing_product_item_ids = array();
1422
  return;
1423
  }
1424
 
 
 
 
 
 
 
 
 
 
 
1425
 
1426
  $product_group_data = array(
1427
  'variants' => $variants,
1428
  );
1429
 
1430
+ // Figure out the matching default variation.
1431
+ $default_product_fbid = $this->get_product_group_default_variation( $woo_product, $fb_product_group_id );
1432
+
1433
  if ( $default_product_fbid ) {
1434
  $product_group_data['default_product_id'] = $default_product_fbid;
1435
  }
1455
 
1456
  /**
1457
  * Determines if there is a matching variation for the default attributes.
1458
+ * Select closest matching if best can't be found.
1459
+ *
1460
+ * @since 2.6.6
1461
+ * The algorithm only considers the variations that already have been synchronized to the catalog successfully.
1462
  *
1463
  * @since 2.1.2
1464
  *
1465
  * @param \WC_Facebook_Product $woo_product
1466
+ * @return integer|null Facebook Catalog variation id.
1467
  */
1468
+ private function get_product_group_default_variation( $woo_product, $fb_product_group_id ) {
1469
 
1470
  $default_attributes = $woo_product->woo_product->get_default_attributes( 'edit' );
1471
 
1473
  return null;
1474
  }
1475
 
1476
+ $default_variation = null;
1477
+ // Fetch variations that exist in the catalog.
1478
+ $existing_catalog_variations = $this->find_variation_product_item_ids( $fb_product_group_id );
1479
+ $existing_catalog_variations_retailer_ids = array_keys( $existing_catalog_variations );
1480
+ // All woocommerce variations for the product.
1481
+ $product_variations = $woo_product->woo_product->get_available_variations();
1482
 
1483
+ $best_match_count = 0;
1484
  foreach ( $product_variations as $variation ) {
1485
 
1486
+ $fb_retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id(
1487
+ wc_get_product(
1488
+ $variation['variation_id']
1489
+ )
1490
+ );
1491
+
1492
+ // Check if currently processed variation exist in the catalog.
1493
+ if ( ! in_array( $fb_retailer_id, $existing_catalog_variations_retailer_ids ) ) {
1494
+ continue;
1495
+ }
1496
 
1497
+ $variation_attributes = $this->get_product_variation_attributes( $variation );
1498
+ $variation_attributes_count = count( $variation_attributes );
1499
+ $matching_attributes_count = count( array_intersect_assoc( $default_attributes, $variation_attributes ) );
1500
 
1501
+ // Check how much current variation matches the selected default attributes.
1502
+ if ( $matching_attributes_count === $variation_attributes_count ) {
1503
+ // We found a perfect match;
1504
+ $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1505
  break;
1506
+ } else if ( $matching_attributes_count > $best_match_count ) {
1507
+ // We found a better match.
1508
+ $default_variation = $existing_catalog_variations[ $fb_retailer_id ];
1509
  }
1510
+
1511
  }
1512
 
1513
  return $default_variation;
3193
  return (bool) apply_filters( 'wc_facebook_is_debug_mode_enabled', 'yes' === get_option( self::SETTING_ENABLE_DEBUG_MODE ), $this );
3194
  }
3195
 
3196
+ /**
3197
+ * Determines whether debug mode is enabled.
3198
+ *
3199
+ * @since 2.6.6
3200
+ *
3201
+ * @return bool
3202
+ */
3203
+ public function is_new_style_feed_generation_enabled() {
3204
+ return (bool) ( 'yes' === get_option( self::SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR ) );
3205
+ }
3206
+
3207
+ /**
3208
+ * Check if logging headers is requested.
3209
+ * For a typical troubleshooting session the request headers bring zero value except making the log unreadable.
3210
+ * They will be disabled by default. Enabling them will require setting an option in the options table.
3211
+ *
3212
+ * @since 2.6.6
3213
+ *
3214
+ */
3215
+ public function are_headers_requested_for_debug() {
3216
+ return (bool) get_option( self::SETTING_REQUEST_HEADERS_IN_DEBUG_MODE, false );
3217
+ }
3218
+
3219
 
3220
  /***
3221
  * Determines if the feed has been migrated from FBE 1 to FBE 1.5
3819
  }
3820
  }
3821
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3822
  /** Deprecated methods ********************************************************************************************/
3823
 
3824
 
facebook-for-woocommerce.php CHANGED
@@ -11,7 +11,7 @@
11
  * Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
12
  * Author: Facebook
13
  * Author URI: https://www.facebook.com/
14
- * Version: 2.6.5
15
  * Text Domain: facebook-for-woocommerce
16
  * Tested up to: 5.8
17
  * WC requires at least: 3.5.0
@@ -33,7 +33,7 @@ class WC_Facebook_Loader {
33
  /**
34
  * @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
35
  */
36
- const PLUGIN_VERSION = '2.6.5'; // WRCS: DEFINED_VERSION.
37
 
38
  // Minimum PHP version required by this plugin.
39
  const MINIMUM_PHP_VERSION = '7.0.0';
11
  * Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
12
  * Author: Facebook
13
  * Author URI: https://www.facebook.com/
14
+ * Version: 2.6.6
15
  * Text Domain: facebook-for-woocommerce
16
  * Tested up to: 5.8
17
  * WC requires at least: 3.5.0
33
  /**
34
  * @var string the plugin version. This must be in the main plugin file to be automatically bumped by Woorelease.
35
  */
36
+ const PLUGIN_VERSION = '2.6.6'; // WRCS: DEFINED_VERSION.
37
 
38
  // Minimum PHP version required by this plugin.
39
  const MINIMUM_PHP_VERSION = '7.0.0';
i18n/languages/facebook-for-woocommerce.pot CHANGED
@@ -2,10 +2,10 @@
2
  # This file is distributed under the same license as the Facebook for WooCommerce package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Facebook for WooCommerce 2.6.5\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
- "POT-Creation-Date: 2021-09-16 08:53:33+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -61,45 +61,45 @@ msgstr ""
61
  msgid "Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu."
62
  msgstr ""
63
 
64
- #: class-wc-facebookcommerce.php:457
65
  msgid "FB Product Sets"
66
  msgstr ""
67
 
68
- #: class-wc-facebookcommerce.php:458
69
  msgid "FB Product Set"
70
  msgstr ""
71
 
72
- #: class-wc-facebookcommerce.php:466
73
  #. translators: Edit item label
74
  msgid "Edit %s"
75
  msgstr ""
76
 
77
- #: class-wc-facebookcommerce.php:468
78
  #. translators: Add new label
79
  msgid "Add new %s"
80
  msgstr ""
81
 
82
- #: class-wc-facebookcommerce.php:471
83
  #. translators: No items found text
84
  msgid "No %s found."
85
  msgstr ""
86
 
87
- #: class-wc-facebookcommerce.php:473
88
  #. translators: Search label
89
  msgid "Search %s."
90
  msgstr ""
91
 
92
- #: class-wc-facebookcommerce.php:475
93
  #. translators: Text label
94
  msgid "Separate %s with commas"
95
  msgstr ""
96
 
97
- #: class-wc-facebookcommerce.php:477
98
  #. translators: Text label
99
  msgid "Choose from the most used %s"
100
  msgstr ""
101
 
102
- #: class-wc-facebookcommerce.php:593
103
  msgid "Cannot create the API instance because the access token is missing."
104
  msgstr ""
105
 
@@ -107,69 +107,49 @@ msgstr ""
107
  msgid "Facebook for WooCommerce"
108
  msgstr ""
109
 
110
- #: facebook-commerce.php:230
111
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
112
  msgstr ""
113
 
114
- #: facebook-commerce.php:642
115
- msgid "Facebook ID:"
116
- msgstr ""
117
-
118
- #: facebook-commerce.php:650
119
- msgid "Variant IDs:"
120
- msgstr ""
121
-
122
- #: facebook-commerce.php:668
123
- msgid "Reset Facebook metadata"
124
- msgstr ""
125
-
126
- #: facebook-commerce.php:673
127
- msgid "Delete product(s) on Facebook"
128
- msgstr ""
129
-
130
- #: facebook-commerce.php:681
131
- msgid "This product is not yet synced to Facebook."
132
- msgstr ""
133
-
134
- #: facebook-commerce.php:1508
135
  msgid "Nothing to update for product group for %1$s"
136
  msgstr ""
137
 
138
- #: facebook-commerce.php:1902
139
  #. translators: Placeholders %1$s - original error message from Facebook API
140
  msgid "There was an issue connecting to the Facebook API: %1$s"
141
  msgstr ""
142
 
143
- #: facebook-commerce.php:2238
144
  msgid "Your connection has expired."
145
  msgstr ""
146
 
147
- #: facebook-commerce.php:2238
148
  msgid ""
149
  "Please click Manage connection > Advanced Options > Update Token to refresh "
150
  "your connection to Facebook."
151
  msgstr ""
152
 
153
- #: facebook-commerce.php:2245
154
  #. translators: Placeholders %s - error message
155
  msgid "There was an error trying to sync the products to Facebook. %s"
156
  msgstr ""
157
 
158
- #: facebook-commerce.php:2268 facebook-commerce.php:2441
159
  msgid "Product sync is disabled."
160
  msgstr ""
161
 
162
- #: facebook-commerce.php:2275 facebook-commerce.php:2448
163
  msgid "The plugin is not configured or the Catalog ID is missing."
164
  msgstr ""
165
 
166
- #: facebook-commerce.php:2298
167
  msgid ""
168
  "A product sync is in progress. Please wait until the sync finishes before "
169
  "starting a new one."
170
  msgstr ""
171
 
172
- #: facebook-commerce.php:2310 facebook-commerce.php:2462
173
  msgid ""
174
  "We've detected that your Facebook Product Catalog is no longer valid. This "
175
  "may happen if it was deleted, but could also be a temporary error. If the "
@@ -177,33 +157,33 @@ msgid ""
177
  "and setup the plugin again."
178
  msgstr ""
179
 
180
- #: facebook-commerce.php:2486
181
  msgid "We couldn't create the feed or upload the product information."
182
  msgstr ""
183
 
184
- #: facebook-commerce.php:2954
185
  msgid "Hi! We're here to answer any questions you may have."
186
  msgstr ""
187
 
188
- #: facebook-commerce.php:3321
189
  msgid "Facebook for WooCommerce error:"
190
  msgstr ""
191
 
192
- #: facebook-commerce.php:3403
193
  msgid ""
194
  "There was an error trying to retrieve information about the Facebook page: "
195
  "%s"
196
  msgstr ""
197
 
198
- #: facebook-commerce.php:3454
199
  msgid "Get started with Messenger Customer Chat"
200
  msgstr ""
201
 
202
- #: facebook-commerce.php:3455
203
  msgid "Get started with Instagram Shopping"
204
  msgstr ""
205
 
206
- #: facebook-commerce.php:3690
207
  #. translators: Placeholders %1$s - original error message from Facebook API
208
  msgid "There was an issue connecting to the Facebook API: %s"
209
  msgstr ""
@@ -342,6 +322,30 @@ msgstr ""
342
  msgid "Map FB Product Set to WC Product Categories"
343
  msgstr ""
344
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  #: includes/Admin/Products.php:101
346
  msgid "Select values for enhanced attributes for this product"
347
  msgstr ""
@@ -521,13 +525,27 @@ msgid "Enable debug mode"
521
  msgstr ""
522
 
523
  #: includes/Admin/Settings_Screens/Connection.php:330
524
- msgid "Log plugin events for debugging"
525
  msgstr ""
526
 
527
  #: includes/Admin/Settings_Screens/Connection.php:331
528
  msgid "Only enable this if you are experiencing problems with the plugin."
529
  msgstr ""
530
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
  #: includes/Admin/Settings_Screens/Messenger.php:122
532
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
533
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
@@ -990,38 +1008,62 @@ msgstr ""
990
  msgid "You do not have permission to finish App Store login."
991
  msgstr ""
992
 
993
- #: includes/ProductSync/ProductValidator.php:148
994
  msgid "Product sync is globally disabled."
995
  msgstr ""
996
 
997
- #: includes/ProductSync/ProductValidator.php:161
998
  msgid "Product is not published."
999
  msgstr ""
1000
 
1001
- #: includes/ProductSync/ProductValidator.php:172
1002
  msgid "Product must be in stock."
1003
  msgstr ""
1004
 
1005
- #: includes/ProductSync/ProductValidator.php:187
1006
  msgid "Product is hidden from catalog and search."
1007
  msgstr ""
1008
 
1009
- #: includes/ProductSync/ProductValidator.php:202
1010
  msgid "Product excluded because of categories."
1011
  msgstr ""
1012
 
1013
- #: includes/ProductSync/ProductValidator.php:209
1014
  msgid "Product excluded because of tags."
1015
  msgstr ""
1016
 
1017
- #: includes/ProductSync/ProductValidator.php:220
1018
  msgid "Sync disabled in product field."
1019
  msgstr ""
1020
 
1021
- #: includes/ProductSync/ProductValidator.php:255
1022
  msgid "If product is not simple, variable or variation it must have a price."
1023
  msgstr ""
1024
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  #: includes/Products/Sync/Background.php:69
1026
  msgid "Job data key \"%s\" not set"
1027
  msgstr ""
@@ -1052,19 +1094,19 @@ msgstr ""
1052
  msgid "Dismiss"
1053
  msgstr ""
1054
 
1055
- #: includes/fbproductfeed.php:470
1056
  msgid "Could not create product catalog feed directory"
1057
  msgstr ""
1058
 
1059
- #: includes/fbproductfeed.php:535
1060
  msgid "Could not open the product catalog temporary feed file for writing"
1061
  msgstr ""
1062
 
1063
- #: includes/fbproductfeed.php:542
1064
  msgid "Could not open the product catalog feed file for writing"
1065
  msgstr ""
1066
 
1067
- #: includes/fbproductfeed.php:585
1068
  msgid "Could not rename the product catalog feed file"
1069
  msgstr ""
1070
 
2
  # This file is distributed under the same license as the Facebook for WooCommerce package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Facebook for WooCommerce 2.6.6\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://woocommerce.com/my-account/marketplace-ticket-form/\n"
8
+ "POT-Creation-Date: 2021-11-03 07:29: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"
61
  msgid "Heads up! The Facebook menu is now located under the %1$sMarketing%2$s menu."
62
  msgstr ""
63
 
64
+ #: class-wc-facebookcommerce.php:467
65
  msgid "FB Product Sets"
66
  msgstr ""
67
 
68
+ #: class-wc-facebookcommerce.php:468
69
  msgid "FB Product Set"
70
  msgstr ""
71
 
72
+ #: class-wc-facebookcommerce.php:476
73
  #. translators: Edit item label
74
  msgid "Edit %s"
75
  msgstr ""
76
 
77
+ #: class-wc-facebookcommerce.php:478
78
  #. translators: Add new label
79
  msgid "Add new %s"
80
  msgstr ""
81
 
82
+ #: class-wc-facebookcommerce.php:481
83
  #. translators: No items found text
84
  msgid "No %s found."
85
  msgstr ""
86
 
87
+ #: class-wc-facebookcommerce.php:483
88
  #. translators: Search label
89
  msgid "Search %s."
90
  msgstr ""
91
 
92
+ #: class-wc-facebookcommerce.php:485
93
  #. translators: Text label
94
  msgid "Separate %s with commas"
95
  msgstr ""
96
 
97
+ #: class-wc-facebookcommerce.php:487
98
  #. translators: Text label
99
  msgid "Choose from the most used %s"
100
  msgstr ""
101
 
102
+ #: class-wc-facebookcommerce.php:603
103
  msgid "Cannot create the API instance because the access token is missing."
104
  msgstr ""
105
 
107
  msgid "Facebook for WooCommerce"
108
  msgstr ""
109
 
110
+ #: facebook-commerce.php:236
111
  msgid "Facebook Commerce and Dynamic Ads (Pixel) Extension"
112
  msgstr ""
113
 
114
+ #: facebook-commerce.php:1415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  msgid "Nothing to update for product group for %1$s"
116
  msgstr ""
117
 
118
+ #: facebook-commerce.php:1828
119
  #. translators: Placeholders %1$s - original error message from Facebook API
120
  msgid "There was an issue connecting to the Facebook API: %1$s"
121
  msgstr ""
122
 
123
+ #: facebook-commerce.php:2164
124
  msgid "Your connection has expired."
125
  msgstr ""
126
 
127
+ #: facebook-commerce.php:2164
128
  msgid ""
129
  "Please click Manage connection > Advanced Options > Update Token to refresh "
130
  "your connection to Facebook."
131
  msgstr ""
132
 
133
+ #: facebook-commerce.php:2171
134
  #. translators: Placeholders %s - error message
135
  msgid "There was an error trying to sync the products to Facebook. %s"
136
  msgstr ""
137
 
138
+ #: facebook-commerce.php:2194 facebook-commerce.php:2367
139
  msgid "Product sync is disabled."
140
  msgstr ""
141
 
142
+ #: facebook-commerce.php:2201 facebook-commerce.php:2374
143
  msgid "The plugin is not configured or the Catalog ID is missing."
144
  msgstr ""
145
 
146
+ #: facebook-commerce.php:2224
147
  msgid ""
148
  "A product sync is in progress. Please wait until the sync finishes before "
149
  "starting a new one."
150
  msgstr ""
151
 
152
+ #: facebook-commerce.php:2236 facebook-commerce.php:2388
153
  msgid ""
154
  "We've detected that your Facebook Product Catalog is no longer valid. This "
155
  "may happen if it was deleted, but could also be a temporary error. If the "
157
  "and setup the plugin again."
158
  msgstr ""
159
 
160
+ #: facebook-commerce.php:2412
161
  msgid "We couldn't create the feed or upload the product information."
162
  msgstr ""
163
 
164
+ #: facebook-commerce.php:2880
165
  msgid "Hi! We're here to answer any questions you may have."
166
  msgstr ""
167
 
168
+ #: facebook-commerce.php:3270
169
  msgid "Facebook for WooCommerce error:"
170
  msgstr ""
171
 
172
+ #: facebook-commerce.php:3352
173
  msgid ""
174
  "There was an error trying to retrieve information about the Facebook page: "
175
  "%s"
176
  msgstr ""
177
 
178
+ #: facebook-commerce.php:3403
179
  msgid "Get started with Messenger Customer Chat"
180
  msgstr ""
181
 
182
+ #: facebook-commerce.php:3404
183
  msgid "Get started with Instagram Shopping"
184
  msgstr ""
185
 
186
+ #: facebook-commerce.php:3639
187
  #. translators: Placeholders %1$s - original error message from Facebook API
188
  msgid "There was an issue connecting to the Facebook API: %s"
189
  msgstr ""
322
  msgid "Map FB Product Set to WC Product Categories"
323
  msgstr ""
324
 
325
+ #: includes/Admin/Product_Sync_Meta_Box.php:37
326
+ msgid "Facebook Product Sync"
327
+ msgstr ""
328
+
329
+ #: includes/Admin/Product_Sync_Meta_Box.php:78
330
+ msgid "Facebook ID:"
331
+ msgstr ""
332
+
333
+ #: includes/Admin/Product_Sync_Meta_Box.php:89
334
+ msgid "Variant IDs:"
335
+ msgstr ""
336
+
337
+ #: includes/Admin/Product_Sync_Meta_Box.php:121
338
+ msgid "Reset Facebook metadata"
339
+ msgstr ""
340
+
341
+ #: includes/Admin/Product_Sync_Meta_Box.php:126
342
+ msgid "Delete product(s) on Facebook"
343
+ msgstr ""
344
+
345
+ #: includes/Admin/Product_Sync_Meta_Box.php:138
346
+ msgid "This product is not yet synced to Facebook."
347
+ msgstr ""
348
+
349
  #: includes/Admin/Products.php:101
350
  msgid "Select values for enhanced attributes for this product"
351
  msgstr ""
525
  msgstr ""
526
 
527
  #: includes/Admin/Settings_Screens/Connection.php:330
528
+ msgid "Log plugin events for debugging."
529
  msgstr ""
530
 
531
  #: includes/Admin/Settings_Screens/Connection.php:331
532
  msgid "Only enable this if you are experiencing problems with the plugin."
533
  msgstr ""
534
 
535
+ #: includes/Admin/Settings_Screens/Connection.php:337
536
+ msgid "Experimental! Enable new style feed generation"
537
+ msgstr ""
538
+
539
+ #: includes/Admin/Settings_Screens/Connection.php:339
540
+ msgid "Use new, memory improved, feed generation process."
541
+ msgstr ""
542
+
543
+ #: includes/Admin/Settings_Screens/Connection.php:340
544
+ msgid ""
545
+ "Experimental feature. Only enable this if you are experiencing problems "
546
+ "with feed generation. This is an experimental feature in testing phase."
547
+ msgstr ""
548
+
549
  #: includes/Admin/Settings_Screens/Messenger.php:122
550
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
551
  msgid "%1$sClick here%2$s to manage your Messenger greeting and colors."
1008
  msgid "You do not have permission to finish App Store login."
1009
  msgstr ""
1010
 
1011
+ #: includes/ProductSync/ProductValidator.php:185
1012
  msgid "Product sync is globally disabled."
1013
  msgstr ""
1014
 
1015
+ #: includes/ProductSync/ProductValidator.php:198
1016
  msgid "Product is not published."
1017
  msgstr ""
1018
 
1019
+ #: includes/ProductSync/ProductValidator.php:209
1020
  msgid "Product must be in stock."
1021
  msgstr ""
1022
 
1023
+ #: includes/ProductSync/ProductValidator.php:224
1024
  msgid "Product is hidden from catalog and search."
1025
  msgstr ""
1026
 
1027
+ #: includes/ProductSync/ProductValidator.php:239
1028
  msgid "Product excluded because of categories."
1029
  msgstr ""
1030
 
1031
+ #: includes/ProductSync/ProductValidator.php:246
1032
  msgid "Product excluded because of tags."
1033
  msgstr ""
1034
 
1035
+ #: includes/ProductSync/ProductValidator.php:257
1036
  msgid "Sync disabled in product field."
1037
  msgstr ""
1038
 
1039
+ #: includes/ProductSync/ProductValidator.php:292
1040
  msgid "If product is not simple, variable or variation it must have a price."
1041
  msgstr ""
1042
 
1043
+ #: includes/ProductSync/ProductValidator.php:318
1044
+ msgid ""
1045
+ "Product description is all capital letters. Please change the description "
1046
+ "to sentence case in order to allow synchronization of your product."
1047
+ msgstr ""
1048
+
1049
+ #: includes/ProductSync/ProductValidator.php:321
1050
+ msgid "Product description is too long. Maximum allowed length is 5000 characters."
1051
+ msgstr ""
1052
+
1053
+ #: includes/ProductSync/ProductValidator.php:340
1054
+ msgid ""
1055
+ "Product title is all capital letters. Please change the title to sentence "
1056
+ "case in order to allow synchronization of your product."
1057
+ msgstr ""
1058
+
1059
+ #: includes/ProductSync/ProductValidator.php:343
1060
+ msgid "Product title is too long. Maximum allowed length is 150 characters."
1061
+ msgstr ""
1062
+
1063
+ #: includes/ProductSync/ProductValidator.php:367
1064
+ msgid "Too many attributes selected for product. Use 4 or less."
1065
+ msgstr ""
1066
+
1067
  #: includes/Products/Sync/Background.php:69
1068
  msgid "Job data key \"%s\" not set"
1069
  msgstr ""
1094
  msgid "Dismiss"
1095
  msgstr ""
1096
 
1097
+ #: includes/fbproductfeed.php:278
1098
  msgid "Could not create product catalog feed directory"
1099
  msgstr ""
1100
 
1101
+ #: includes/fbproductfeed.php:382
1102
  msgid "Could not open the product catalog temporary feed file for writing"
1103
  msgstr ""
1104
 
1105
+ #: includes/fbproductfeed.php:389
1106
  msgid "Could not open the product catalog feed file for writing"
1107
  msgstr ""
1108
 
1109
+ #: includes/fbproductfeed.php:453
1110
  msgid "Could not rename the product catalog feed file"
1111
  msgstr ""
1112
 
includes/Admin/Product_Sync_Meta_Box.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\Admin;
4
+
5
+ defined( 'ABSPATH' ) || exit;
6
+
7
+ /**
8
+ * Class responsible for display and operations of product sync status metabox.
9
+ *
10
+ * @since 2.6.6
11
+ */
12
+ class Product_Sync_Meta_Box {
13
+
14
+ /**
15
+ * Register metabox assets and add the metabox.
16
+ */
17
+ public static function register() {
18
+ $ajax_data = array(
19
+ 'nonce' => wp_create_nonce( 'wc_facebook_metabox_jsx' ),
20
+ );
21
+
22
+ wp_enqueue_script(
23
+ 'wc_facebook_metabox_jsx',
24
+ facebook_for_woocommerce()->get_asset_build_dir_url() . '/admin/metabox.js',
25
+ array(),
26
+ \WC_Facebookcommerce::PLUGIN_VERSION
27
+ );
28
+
29
+ wp_localize_script(
30
+ 'wc_facebook_metabox_jsx',
31
+ 'wc_facebook_metabox_jsx',
32
+ $ajax_data
33
+ );
34
+
35
+ add_meta_box(
36
+ 'facebook_metabox',
37
+ __( 'Facebook Product Sync', 'facebook-for-woocommerce' ),
38
+ __CLASS__ . '::output',
39
+ 'product',
40
+ 'side'
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Renders the content of the product meta box.
46
+ *
47
+ * @since 2.6.6
48
+ */
49
+ public static function output() {
50
+ global $post;
51
+
52
+ $fb_integration = facebook_for_woocommerce()->get_integration();
53
+ $fb_product = new \WC_Facebook_Product( $post->ID );
54
+ $fb_product_group_id = null;
55
+ $should_sync = true;
56
+ $no_sync_reason = '';
57
+
58
+ if ( $fb_product->woo_product instanceof \WC_Product ) {
59
+ try {
60
+ facebook_for_woocommerce()->get_product_sync_validator( $fb_product->woo_product )->validate();
61
+ } catch ( \Exception $e ) {
62
+ $should_sync = false;
63
+ $no_sync_reason = $e->getMessage();
64
+ }
65
+ }
66
+
67
+ if ( $should_sync || $fb_product->woo_product->is_type( 'variable' ) ) {
68
+ $fb_product_group_id = $fb_integration->get_product_fbid( $fb_integration::FB_PRODUCT_GROUP_ID, $post->ID, $fb_product->woo_product );
69
+ }
70
+ ?>
71
+ <span id="fb_metadata">
72
+ <?php
73
+
74
+ if ( $fb_product_group_id ) {
75
+
76
+ ?>
77
+
78
+ <?php echo esc_html__( 'Facebook ID:', 'facebook-for-woocommerce' ); ?>
79
+ <a href="https://facebook.com/<?php echo esc_attr( $fb_product_group_id ); ?>" target="_blank"><?php echo esc_html( $fb_product_group_id ); ?></a>
80
+
81
+ <?php if ( \WC_Facebookcommerce_Utils::is_variable_type( $fb_product->get_type() ) ) : ?>
82
+
83
+ <?php
84
+ $product_item_ids_by_variation_id = $fb_integration->get_variation_product_item_ids( $fb_product, $fb_product_group_id );
85
+ if ( $product_item_ids_by_variation_id ) :
86
+ ?>
87
+
88
+ <p>
89
+ <?php echo esc_html__( 'Variant IDs:', 'facebook-for-woocommerce' ); ?><br/>
90
+
91
+ <?php
92
+ foreach ( $product_item_ids_by_variation_id as $variation_id => $product_item_id ) :
93
+ $variation = wc_get_product( $variation_id );
94
+ $show_link = true;
95
+
96
+ try {
97
+ facebook_for_woocommerce()->get_product_sync_validator( $variation )->validate();
98
+ } catch ( \Exception $e ) {
99
+ $info = $e->getMessage();
100
+ $show_link = false;
101
+ }
102
+ ?>
103
+ <?php echo esc_html( $variation_id ); ?>:
104
+ <?php if ( $show_link ) : ?>
105
+ <a href="https://facebook.com/<?php echo esc_attr( $product_item_id ); ?>" target="_blank"><?php echo esc_html( $product_item_id ); ?></a>
106
+ <?php else : ?>
107
+ <?php echo esc_html( $info ); ?>
108
+ <?php endif; ?>
109
+ <br/>
110
+ <?php endforeach; ?>
111
+ </p>
112
+
113
+ <?php endif; ?>
114
+
115
+ <?php endif; ?>
116
+
117
+ <input name="is_product_page" type="hidden" value="1"/>
118
+
119
+ <p/>
120
+ <a href="#" onclick="fb_reset_product( <?php echo esc_js( $post->ID ); ?> )">
121
+ <?php echo esc_html__( 'Reset Facebook metadata', 'facebook-for-woocommerce' ); ?>
122
+ </a>
123
+
124
+ <p/>
125
+ <a href="#" onclick="fb_delete_product( <?php echo esc_js( $post->ID ); ?> )">
126
+ <?php echo esc_html__( 'Delete product(s) on Facebook', 'facebook-for-woocommerce' ); ?>
127
+ </a>
128
+
129
+ <?php
130
+
131
+ } elseif ( ! $should_sync ) {
132
+ ?>
133
+ <b><?php echo esc_html( $no_sync_reason ); ?></b>
134
+ <?php
135
+ } else {
136
+
137
+ ?>
138
+ <b><?php echo esc_html__( 'This product is not yet synced to Facebook.', 'facebook-for-woocommerce' ); ?></b>
139
+ <?php
140
+ }
141
+
142
+ ?>
143
+ </span>
144
+ <?php
145
+ }
146
+ }
includes/Admin/Settings_Screens/Connection.php CHANGED
@@ -327,11 +327,20 @@ class Connection extends Admin\Abstract_Settings_Screen {
327
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
328
  'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ),
329
  'type' => 'checkbox',
330
- 'desc' => __( 'Log plugin events for debugging', 'facebook-for-woocommerce' ),
331
  'desc_tip' => __( 'Only enable this if you are experiencing problems with the plugin.', 'facebook-for-woocommerce' ),
332
  'default' => 'no',
333
  ),
334
 
 
 
 
 
 
 
 
 
 
335
  array( 'type' => 'sectionend' ),
336
 
337
  );
327
  'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_DEBUG_MODE,
328
  'title' => __( 'Enable debug mode', 'facebook-for-woocommerce' ),
329
  'type' => 'checkbox',
330
+ 'desc' => __( 'Log plugin events for debugging.', 'facebook-for-woocommerce' ),
331
  'desc_tip' => __( 'Only enable this if you are experiencing problems with the plugin.', 'facebook-for-woocommerce' ),
332
  'default' => 'no',
333
  ),
334
 
335
+ array(
336
+ 'id' => \WC_Facebookcommerce_Integration::SETTING_ENABLE_NEW_STYLE_FEED_GENERATOR,
337
+ 'title' => __( 'Experimental! Enable new style feed generation', 'facebook-for-woocommerce' ),
338
+ 'type' => 'checkbox',
339
+ 'desc' => __( 'Use new, memory improved, feed generation process.', 'facebook-for-woocommerce' ),
340
+ 'desc_tip' => __( 'Experimental feature. Only enable this if you are experiencing problems with feed generation. This is an experimental feature in testing phase.', 'facebook-for-woocommerce' ),
341
+ 'default' => 'no',
342
+ ),
343
+
344
  array( 'type' => 'sectionend' ),
345
 
346
  );
includes/Jobs/GenerateProductFeed.php CHANGED
@@ -23,14 +23,19 @@ class GenerateProductFeed extends AbstractChainedJob {
23
  * Called before starting the job.
24
  */
25
  protected function handle_start() {
26
- // Optionally override this method in child class.
 
 
 
27
  }
28
 
29
  /**
30
  * Called after the finishing the job.
31
  */
32
  protected function handle_end() {
33
- // Optionally override this method in child class.
 
 
34
  }
35
 
36
  /**
@@ -50,8 +55,8 @@ class GenerateProductFeed extends AbstractChainedJob {
50
  $product_ids = $wpdb->get_col(
51
  $wpdb->prepare(
52
  "SELECT post.ID
53
- FROM wp_posts as post
54
- LEFT JOIN wp_posts as parent ON post.post_parent = parent.ID
55
  WHERE
56
  ( post.post_type = 'product_variation' AND parent.post_status = 'publish' )
57
  OR
@@ -66,55 +71,47 @@ class GenerateProductFeed extends AbstractChainedJob {
66
  return array_map( 'intval', $product_ids );
67
  }
68
 
69
- /**
70
- * Filter-like function that runs before items in a batch are processed.
71
  *
72
- * For example, this could be useful for pre-fetching full objects.
73
  *
74
- * @param array $items
 
75
  *
76
- * @return array
77
  */
78
- protected function filter_items_before_processing( array $items ): array {
79
- // Pre-fetch full product objects.
80
- // Variable products will be filtered out here since we don't need them for the feed. It's important to not
81
- // filter out variable products in ::get_items_for_batch() because if a batch only contains variable products
82
- // the job will end prematurely thinking it has nothing more to process.
83
- return wc_get_products(
84
- [
85
- 'type' => [ 'simple', 'variation' ],
 
 
 
 
86
  'include' => $items,
87
  'orderby' => 'none',
88
- ]
 
89
  );
 
 
 
 
 
 
 
90
  }
91
 
92
  /**
93
- * Process a single item.
94
- *
95
- * @param WC_Product $product A single item from the get_items_for_batch() method.
96
- * @param array $args The args for the job.
97
- *
98
- * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
99
  */
100
- protected function process_item( $product, array $args ) {
101
- try {
102
- if ( ! $product ) {
103
- throw new Exception( 'Product not found.' );
104
- }
105
-
106
- $this->log( $product->get_id() );
107
-
108
- } catch ( Exception $e ) {
109
- $this->log(
110
- sprintf(
111
- 'Error processing item #%d - %s',
112
- $product instanceof WC_Product ? $product->get_id() : 0,
113
- $e->getMessage()
114
- )
115
- );
116
- }
117
- }
118
 
119
  /**
120
  * Get the name/slug of the job.
23
  * Called before starting the job.
24
  */
25
  protected function handle_start() {
26
+ $feed_handler = new \WC_Facebook_Product_Feed();
27
+ $feed_handler->create_files_to_protect_product_feed_directory();
28
+ $feed_handler->prepare_temporary_feed_file();
29
+ facebook_for_woocommerce()->get_tracker()->reset_batch_generation_time();
30
  }
31
 
32
  /**
33
  * Called after the finishing the job.
34
  */
35
  protected function handle_end() {
36
+ $feed_handler = new \WC_Facebook_Product_Feed();
37
+ $feed_handler->rename_temporary_feed_file_to_final_feed_file();
38
+ facebook_for_woocommerce()->get_tracker()->save_batch_generation_time();
39
  }
40
 
41
  /**
55
  $product_ids = $wpdb->get_col(
56
  $wpdb->prepare(
57
  "SELECT post.ID
58
+ FROM {$wpdb->posts} as post
59
+ LEFT JOIN {$wpdb->posts} as parent ON post.post_parent = parent.ID
60
  WHERE
61
  ( post.post_type = 'product_variation' AND parent.post_status = 'publish' )
62
  OR
71
  return array_map( 'intval', $product_ids );
72
  }
73
 
74
+ /**
75
+ * Processes a batch of items.
76
  *
77
+ * @since 1.1.0
78
  *
79
+ * @param array $items The items of the current batch.
80
+ * @param array $args The args for the job.
81
  *
82
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
83
  */
84
+ protected function process_items( array $items, array $args ) {
85
+ // Grab start time.
86
+ $start_time = microtime( true );
87
+ /*
88
+ * Pre-fetch full product objects.
89
+ * Variable products will be filtered out here since we don't need them for the feed. It's important to not
90
+ * filter out variable products in ::get_items_for_batch() because if a batch only contains variable products
91
+ * the job will end prematurely thinking it has nothing more to process.
92
+ */
93
+ $products = wc_get_products(
94
+ array(
95
+ 'type' => array( 'simple', 'variation' ),
96
  'include' => $items,
97
  'orderby' => 'none',
98
+ 'limit' => $this->get_batch_size(),
99
+ )
100
  );
101
+ $feed_handler = new \WC_Facebook_Product_Feed();
102
+ $temp_feed_file = fopen( $feed_handler->get_temp_file_path(), 'a' );
103
+ $feed_handler->write_products_feed_to_temp_file( $products, $temp_feed_file );
104
+ if ( is_resource( $temp_feed_file ) ) {
105
+ fclose( $temp_feed_file );
106
+ }
107
+ facebook_for_woocommerce()->get_tracker()->increment_batch_generation_time( microtime( true ) - $start_time );
108
  }
109
 
110
  /**
111
+ * Empty function to satisfy parent class requirements.
112
+ * We don't use it because we are processing the whole batch at once in process_items.
 
 
 
 
113
  */
114
+ protected function process_item( $product, $args ) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  /**
117
  * Get the name/slug of the job.
includes/ProductSync/ProductInvalidException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
+
5
+ use Exception;
6
+
7
+ /**
8
+ * Class ProductInvalidException
9
+ *
10
+ * Exception for when a product configuration is not correct in terms of Facebook product sync.
11
+ * There are limitations that will exclude product from Facebook catalog. We want to inform
12
+ * user as early as possible.
13
+ */
14
+ class ProductInvalidException extends Exception {}
includes/ProductSync/ProductValidator.php CHANGED
@@ -3,9 +3,14 @@
3
  namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
 
5
  use SkyVerge\WooCommerce\Facebook\Products;
 
6
  use WC_Product;
7
  use WC_Facebookcommerce_Integration;
8
 
 
 
 
 
9
  /**
10
  * Class ProductValidator
11
  *
@@ -22,6 +27,27 @@ class ProductValidator {
22
  */
23
  const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * The FB integration instance.
27
  *
@@ -50,7 +76,8 @@ class ProductValidator {
50
  * @param WC_Product $product The product to validate. Accepts both variations and variable products.
51
  */
52
  public function __construct( WC_Facebookcommerce_Integration $integration, WC_Product $product ) {
53
- $this->product = $product;
 
54
 
55
  if ( $product->get_parent_id() ) {
56
  $parent_product = wc_get_product( $product->get_parent_id() );
@@ -75,6 +102,8 @@ class ProductValidator {
75
  $this->validate_product_price();
76
  $this->validate_product_visibility();
77
  $this->validate_product_terms();
 
 
78
  }
79
 
80
  /**
@@ -91,6 +120,8 @@ class ProductValidator {
91
  $this->validate_product_price();
92
  $this->validate_product_visibility();
93
  $this->validate_product_terms();
 
 
94
  }
95
 
96
  /**
@@ -103,6 +134,8 @@ class ProductValidator {
103
  $this->validate();
104
  } catch ( ProductExcludedException $e ) {
105
  return false;
 
 
106
  }
107
 
108
  return true;
@@ -118,6 +151,8 @@ class ProductValidator {
118
  $this->validate_product_terms();
119
  } catch ( ProductExcludedException $e ) {
120
  return false;
 
 
121
  }
122
 
123
  return true;
@@ -133,6 +168,8 @@ class ProductValidator {
133
  $this->validate_product_sync_field();
134
  } catch ( ProductExcludedException $e ) {
135
  return false;
 
 
136
  }
137
 
138
  return true;
@@ -256,4 +293,79 @@ class ProductValidator {
256
  }
257
  }
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
3
  namespace SkyVerge\WooCommerce\Facebook\ProductSync;
4
 
5
  use SkyVerge\WooCommerce\Facebook\Products;
6
+ use WC_Facebook_Product;
7
  use WC_Product;
8
  use WC_Facebookcommerce_Integration;
9
 
10
+ if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
11
+ include_once '../fbutils.php';
12
+ }
13
+
14
  /**
15
  * Class ProductValidator
16
  *
27
  */
28
  const SYNC_ENABLED_META_KEY = '_wc_facebook_sync_enabled';
29
 
30
+ /**
31
+ * Maximum length of product description.
32
+ *
33
+ * @var int
34
+ */
35
+ const MAX_DESCRIPTION_LENGTH = 5000;
36
+
37
+ /**
38
+ * Maximum length of product title.
39
+ *
40
+ * @var int
41
+ */
42
+ const MAX_TITLE_LENGTH = 150;
43
+
44
+ /**
45
+ * Maximum allowed attributes in a variation;
46
+ *
47
+ * @var int
48
+ */
49
+ const MAX_NUMBER_OF_ATTRIBUTES_IN_VARIATION = 4;
50
+
51
  /**
52
  * The FB integration instance.
53
  *
76
  * @param WC_Product $product The product to validate. Accepts both variations and variable products.
77
  */
78
  public function __construct( WC_Facebookcommerce_Integration $integration, WC_Product $product ) {
79
+ $this->product = $product;
80
+ $this->facebook_product = new WC_Facebook_Product( $product->get_id() );
81
 
82
  if ( $product->get_parent_id() ) {
83
  $parent_product = wc_get_product( $product->get_parent_id() );
102
  $this->validate_product_price();
103
  $this->validate_product_visibility();
104
  $this->validate_product_terms();
105
+ $this->validate_product_description();
106
+ $this->validate_product_title();
107
  }
108
 
109
  /**
120
  $this->validate_product_price();
121
  $this->validate_product_visibility();
122
  $this->validate_product_terms();
123
+ $this->validate_product_description();
124
+ $this->validate_product_title();
125
  }
126
 
127
  /**
134
  $this->validate();
135
  } catch ( ProductExcludedException $e ) {
136
  return false;
137
+ } catch ( ProductInvalidException $e ) {
138
+ return false;
139
  }
140
 
141
  return true;
151
  $this->validate_product_terms();
152
  } catch ( ProductExcludedException $e ) {
153
  return false;
154
+ } catch ( ProductInvalidException $e ) {
155
+ return false;
156
  }
157
 
158
  return true;
168
  $this->validate_product_sync_field();
169
  } catch ( ProductExcludedException $e ) {
170
  return false;
171
+ } catch ( ProductInvalidException $e ) {
172
+ return false;
173
  }
174
 
175
  return true;
293
  }
294
  }
295
 
296
+ /**
297
+ * Check if the description field has correct format according to:
298
+ * Product Description Specifications for Catalogs : https://www.facebook.com/business/help/2302017289821154
299
+ *
300
+ * @throws ProductInvalidException If product description does not meet the requirements.
301
+ */
302
+ protected function validate_product_description() {
303
+ /*
304
+ * First step is to select the description that we want to evaluate.
305
+ * Main description is the one provided for the product in the Facebook.
306
+ * If it is blank, product description will be used.
307
+ * If product description is blank, shortname will be used.
308
+ */
309
+ $description = $this->facebook_product->get_fb_description();
310
+
311
+ /*
312
+ * Requirements:
313
+ * - No all caps descriptions.
314
+ * - Max length 5000.
315
+ * - Min length 30 ( tested and not required, will not enforce until this will become a hard requirement )
316
+ */
317
+ if ( \WC_Facebookcommerce_Utils::is_all_caps( $description ) ) {
318
+ throw new ProductInvalidException( __( 'Product description is all capital letters. Please change the description to sentence case in order to allow synchronization of your product.', 'facebook-for-woocommerce' ) );
319
+ }
320
+ if ( strlen( $description ) > self::MAX_DESCRIPTION_LENGTH ) {
321
+ throw new ProductInvalidException( __( 'Product description is too long. Maximum allowed length is 5000 characters.', 'facebook-for-woocommerce' ) );
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Check if the title field has correct format according to:
327
+ * Product Title Specifications for Catalogs : https://www.facebook.com/business/help/2104231189874655
328
+ *
329
+ * @throws ProductInvalidException If product title does not meet the requirements.
330
+ */
331
+ protected function validate_product_title() {
332
+ $title = $this->product->get_title();
333
+
334
+ /*
335
+ * Requirements:
336
+ * - No all caps title.
337
+ * - Max length 150.
338
+ */
339
+ if ( \WC_Facebookcommerce_Utils::is_all_caps( $title ) ) {
340
+ throw new ProductInvalidException( __( 'Product title is all capital letters. Please change the title to sentence case in order to allow synchronization of your product.', 'facebook-for-woocommerce' ) );
341
+ }
342
+ if ( strlen( $title ) > self::MAX_TITLE_LENGTH ) {
343
+ throw new ProductInvalidException( __( 'Product title is too long. Maximum allowed length is 150 characters.', 'facebook-for-woocommerce' ) );
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Check if variation product has proper settings.
349
+ *
350
+ * @throws ProductInvalidException If product variation violates some requirements.
351
+ */
352
+ protected function validate_variation_structure() {
353
+ // Check if we are dealing with a variation.
354
+ if ( ! $this->product->is_type( 'variation' ) ) {
355
+ return;
356
+ }
357
+ $attributes = $this->product->get_attributes();
358
+
359
+ $used_attributes_count = count(
360
+ array_filter(
361
+ $attributes
362
+ )
363
+ );
364
+
365
+ // No more than MAX_NUMBER_OF_ATTRIBUTES_IN_VARIATION ar allowed to be used.
366
+ if ( $used_attributes_count > self::MAX_NUMBER_OF_ATTRIBUTES_IN_VARIATION ) {
367
+ throw new ProductInvalidException( __( 'Too many attributes selected for product. Use 4 or less.', 'facebook-for-woocommerce' ) );
368
+ }
369
+ }
370
+
371
  }
includes/Products/Feed.php CHANGED
@@ -143,15 +143,19 @@ class Feed {
143
  * @internal
144
  *
145
  * @since 1.11.0
 
146
  */
147
  public function regenerate_feed() {
148
-
149
- $feed_handler = new \WC_Facebook_Product_Feed();
150
-
151
- $feed_handler->generate_feed();
 
 
 
 
152
  }
153
 
154
-
155
  /**
156
  * Schedules the recurring feed generation.
157
  *
143
  * @internal
144
  *
145
  * @since 1.11.0
146
+ * @since 2.6.6 Enable new feed generation code if requested.
147
  */
148
  public function regenerate_feed() {
149
+ // Maybe use new ( experimental ), feed generation framework.
150
+ if ( facebook_for_woocommerce()->get_integration()->is_new_style_feed_generation_enabled() ) {
151
+ $generate_feed_job = facebook_for_woocommerce()->job_registry->generate_product_feed_job;
152
+ $generate_feed_job->queue_start();
153
+ } else {
154
+ $feed_handler = new \WC_Facebook_Product_Feed();
155
+ $feed_handler->generate_feed();
156
+ }
157
  }
158
 
 
159
  /**
160
  * Schedules the recurring feed generation.
161
  *
includes/Utilities/Tracker.php CHANGED
@@ -29,12 +29,33 @@ class Tracker {
29
  const TRANSIENT_WCTRACKER_LIFE_TIME = 2 * WEEK_IN_SECONDS;
30
 
31
  /**
32
- * Transient key name; how long it took to generate the most recent feed file, or zero if it failed.
33
  *
34
  * @var string
35
  */
36
  const TRANSIENT_WCTRACKER_FEED_GENERATION_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_time';
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  /**
39
  * Transient key name; true if feed has been requested by Facebook.
40
  *
@@ -103,13 +124,27 @@ class Tracker {
103
  $data['extensions']['facebook-for-woocommerce']['messenger-enabled'] = wc_bool_to_string( $messenger_enabled );
104
 
105
  /**
106
- * How long did the last feed generation take (or did it fail - 0)?
 
107
  *
108
  * @since 2.6.0
109
  */
110
  $feed_generation_time = get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_TIME );
111
  $data['extensions']['facebook-for-woocommerce']['feed-generation-time'] = floatval( $feed_generation_time );
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  /**
114
  * Has the feed file been requested since the last snapshot?
115
  *
@@ -136,6 +171,13 @@ class Tracker {
136
  */
137
  $data['extensions']['facebook-for-woocommerce']['product-feed-config'] = get_transient( self::TRANSIENT_WCTRACKER_FB_FEED_CONFIG );
138
 
 
 
 
 
 
 
 
139
  return $data;
140
  }
141
 
@@ -151,6 +193,58 @@ class Tracker {
151
  set_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_TIME, $time_in_seconds, self::TRANSIENT_WCTRACKER_LIFE_TIME );
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  /**
155
  * Store the fact that the feed has been requested by Facebook in a transient.
156
  * This will later be added to next tracker snapshot.
29
  const TRANSIENT_WCTRACKER_LIFE_TIME = 2 * WEEK_IN_SECONDS;
30
 
31
  /**
32
+ * Transient key name; how long it took to generate the most recent feed file, or minus one if it failed.
33
  *
34
  * @var string
35
  */
36
  const TRANSIENT_WCTRACKER_FEED_GENERATION_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_time';
37
 
38
+ /**
39
+ * Transient key name; how long it took to generate already generated batches.
40
+ *
41
+ * @var string
42
+ */
43
+ const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_time';
44
+
45
+ /**
46
+ * Transient key name; how much wall ( clock ) time it took to generate the feed file.
47
+ *
48
+ * @var string
49
+ */
50
+ const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
51
+
52
+ /**
53
+ * Transient key name; the time when was the batched feed generation started.
54
+ *
55
+ * @var string
56
+ */
57
+ const TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_START_TIME = 'facebook_for_woocommerce_wctracker_feed_generation_batch_wall_time';
58
+
59
  /**
60
  * Transient key name; true if feed has been requested by Facebook.
61
  *
124
  $data['extensions']['facebook-for-woocommerce']['messenger-enabled'] = wc_bool_to_string( $messenger_enabled );
125
 
126
  /**
127
+ * How long did the last feed generation take (or did it fail - 0)? This counts just the time when the batches have been generated.
128
+ * It does not take into account the time between the batches.
129
  *
130
  * @since 2.6.0
131
  */
132
  $feed_generation_time = get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_TIME );
133
  $data['extensions']['facebook-for-woocommerce']['feed-generation-time'] = floatval( $feed_generation_time );
134
 
135
+ /**
136
+ * How long did the last feed generation take in wall time. This is the whole duration. Batches plus time in between.
137
+ *
138
+ * @since 2.6.6
139
+ */
140
+ if ( facebook_for_woocommerce()->get_integration()->is_new_style_feed_generation_enabled() ) {
141
+ $feed_generation_batch_wall_time = intval( get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME ) );
142
+ } else {
143
+ // For old feed generator this is the same thing $feed_generation_time because the process is done in one step.
144
+ $feed_generation_batch_wall_time = intval( $feed_generation_time );
145
+ }
146
+ $data['extensions']['facebook-for-woocommerce']['feed-generation-wall-time'] = $feed_generation_batch_wall_time;
147
+
148
  /**
149
  * Has the feed file been requested since the last snapshot?
150
  *
171
  */
172
  $data['extensions']['facebook-for-woocommerce']['product-feed-config'] = get_transient( self::TRANSIENT_WCTRACKER_FB_FEED_CONFIG );
173
 
174
+ /**
175
+ * Detect if the user has enabled the new experimental feed generator feature.
176
+ *
177
+ * @since 2.6.6
178
+ */
179
+ $data['extensions']['facebook-for-woocommerce']['new-feed-generator-enabled'] = wc_bool_to_string( facebook_for_woocommerce()->get_integration()->is_new_style_feed_generation_enabled() );
180
+
181
  return $data;
182
  }
183
 
193
  set_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_TIME, $time_in_seconds, self::TRANSIENT_WCTRACKER_LIFE_TIME );
194
  }
195
 
196
+ /**
197
+ * Reset the feed generation in batch time counter.
198
+ *
199
+ * @since 2.6.6
200
+ */
201
+ public function reset_batch_generation_time() {
202
+ // Reset the main counter.
203
+ $this->track_feed_file_generation_time( -1 );
204
+ set_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME, 0, self::TRANSIENT_WCTRACKER_LIFE_TIME );
205
+ set_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME, 0, self::TRANSIENT_WCTRACKER_LIFE_TIME );
206
+ set_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_START_TIME, time(), self::TRANSIENT_WCTRACKER_LIFE_TIME );
207
+ }
208
+
209
+ /**
210
+ * Add time to batch feed_generation_time.
211
+ * This accumulates time over all of the calculated feed batches.
212
+ *
213
+ * @param float $time_in_seconds Time to add to the generation time(in seconds).
214
+ * @since 2.6.6
215
+ */
216
+ public function increment_batch_generation_time( $time_in_seconds ) {
217
+ $tracked_generation_time = floatval( get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME ) );
218
+ set_transient(
219
+ self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME,
220
+ floatval( $time_in_seconds ) + $tracked_generation_time,
221
+ self::TRANSIENT_WCTRACKER_LIFE_TIME
222
+ );
223
+ }
224
+
225
+ /**
226
+ * Save batch generation time.
227
+ *
228
+ * This is the last step in batch feed generation time tracking.
229
+ * The accumulated time value is copied to the main transient
230
+ * that is later used by the tracking code to track feed generation time.
231
+ *
232
+ * @since 2.6.6
233
+ */
234
+ public function save_batch_generation_time() {
235
+ $this->track_feed_file_generation_time(
236
+ get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_TIME )
237
+ );
238
+
239
+ $start = intval( get_transient( self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_START_TIME ) );
240
+ $end = time();
241
+ set_transient(
242
+ self::TRANSIENT_WCTRACKER_FEED_GENERATION_BATCH_WALL_TIME,
243
+ $end - $start,
244
+ self::TRANSIENT_WCTRACKER_LIFE_TIME
245
+ );
246
+ }
247
+
248
  /**
249
  * Store the fact that the feed has been requested by Facebook in a transient.
250
  * This will later be added to next tracker snapshot.
includes/fbproduct.php CHANGED
@@ -56,15 +56,21 @@ if ( ! class_exists( 'WC_Facebook_Product' ) ) :
56
 
57
  public function __construct( $wpid, $parent_product = null ) {
58
 
59
- $this->id = $wpid;
 
 
 
 
 
 
 
60
  $this->fb_description = '';
61
- $this->woo_product = wc_get_product( $wpid );
62
  $this->gallery_urls = null;
63
  $this->fb_use_parent_image = null;
64
  $this->main_description = '';
65
  $this->sync_short_description = \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT === facebook_for_woocommerce()->get_integration()->get_product_description_mode();
66
 
67
- if ( $meta = get_post_meta( $wpid, self::FB_VISIBILITY, true ) ) {
68
  $this->fb_visibility = wc_string_to_bool( $meta );
69
  } else {
70
  $this->fb_visibility = '';
56
 
57
  public function __construct( $wpid, $parent_product = null ) {
58
 
59
+ if ( $wpid instanceof WC_Product ) {
60
+ $this->id = $wpid->get_id();
61
+ $this->woo_product = $wpid;
62
+ } else {
63
+ $this->id = $wpid;
64
+ $this->woo_product = wc_get_product( $wpid );
65
+ }
66
+
67
  $this->fb_description = '';
 
68
  $this->gallery_urls = null;
69
  $this->fb_use_parent_image = null;
70
  $this->main_description = '';
71
  $this->sync_short_description = \WC_Facebookcommerce_Integration::PRODUCT_DESCRIPTION_MODE_SHORT === facebook_for_woocommerce()->get_integration()->get_product_description_mode();
72
 
73
+ if ( $meta = get_post_meta( $this->id, self::FB_VISIBILITY, true ) ) {
74
  $this->fb_visibility = wc_string_to_bool( $meta );
75
  } else {
76
  $this->fb_visibility = '';
includes/fbproductfeed.php CHANGED
@@ -25,16 +25,9 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
25
  class WC_Facebook_Product_Feed {
26
 
27
 
28
- /** @var string transient name for storing the average feed generation time */
29
- const TRANSIENT_AVERAGE_FEED_GENERATION_TIME = 'wc_facebook_average_feed_generation_time';
30
-
31
  /** @var string product catalog feed file directory inside the uploads folder */
32
- const UPLOADS_DIRECTORY = 'facebook_for_woocommerce';
33
-
34
- /** @var string product catalog feed file name - %s will be replaced with a hash */
35
- const FILE_NAME = 'product_catalog_%s.csv';
36
-
37
-
38
  const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
39
  const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
40
  const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
@@ -58,30 +51,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
58
  $this->feed_id = $feed_id;
59
  }
60
 
61
-
62
- /**
63
- * Schedules a new feed generation.
64
- *
65
- * @since 1.11.0
66
- */
67
- public function schedule_feed_generation() {
68
-
69
- // don't schedule another if one's already scheduled or in progress
70
- if ( false !== as_next_scheduled_action( 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' ) ) {
71
- return;
72
- }
73
-
74
- \WC_Facebookcommerce_Utils::log( 'Scheduling product catalog feed file generation' );
75
-
76
- // if async priority actions are supported (AS 3.0+)
77
- if ( function_exists( 'as_enqueue_async_action' ) ) {
78
- as_enqueue_async_action( 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' );
79
- } else {
80
- as_schedule_single_action( time(), 'wc_facebook_generate_product_catalog_feed', array(), 'facebook-for-woocommerce' );
81
- }
82
- }
83
-
84
-
85
  /**
86
  * Generates the product catalog feed.
87
  *
@@ -104,8 +73,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
104
  $generation_time = microtime( true ) - $start_time;
105
  facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( $generation_time );
106
 
107
- $this->set_feed_generation_time_with_decay( $generation_time );
108
-
109
  \WC_Facebookcommerce_Utils::log( 'Product feed file generated' );
110
 
111
  } catch ( \Exception $exception ) {
@@ -119,165 +86,6 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
119
  $profiling_logger->stop( 'generate_feed' );
120
  }
121
 
122
-
123
- /**
124
- * Sets the average feed generation time with a 25% decay.
125
- *
126
- * @since 1.11.0
127
- *
128
- * @param float $generation_time last generation time
129
- */
130
- private function set_feed_generation_time_with_decay( $generation_time ) {
131
-
132
- // update feed generation time estimate w/ 25% decay.
133
- $existing_generation_time = $this->get_average_feed_generation_time();
134
-
135
- if ( $generation_time < $existing_generation_time ) {
136
- $generation_time = $generation_time * 0.25 + $existing_generation_time * 0.75;
137
- }
138
-
139
- $this->set_average_feed_generation_time( $generation_time );
140
- }
141
-
142
-
143
- /**
144
- * Sets the average feed generation time.
145
- *
146
- * @since 1.11.0
147
- *
148
- * @param float $time generation time
149
- */
150
- private function set_average_feed_generation_time( $time ) {
151
-
152
- set_transient( self::TRANSIENT_AVERAGE_FEED_GENERATION_TIME, $time );
153
- }
154
-
155
-
156
- /**
157
- * Gets the estimated feed generation time.
158
- *
159
- * Performs a dry run and returns either the dry run time or last average estimated time, whichever is higher.
160
- *
161
- * @since 1.11.0
162
- *
163
- * @return int
164
- */
165
- public function get_estimated_feed_generation_time() {
166
-
167
- $estimate = $this->estimate_generation_time();
168
- $average = $this->get_average_feed_generation_time();
169
-
170
- return (int) max( $estimate, $average );
171
- }
172
-
173
-
174
- /**
175
- * Estimates the feed generation time.
176
- *
177
- * Runs a dry-run generation of a subset of products, then extrapolates that out to the full catalog size. Also
178
- * adds a bit of buffer time.
179
- *
180
- * @since 1.11.0
181
- *
182
- * @return float
183
- */
184
- private function estimate_generation_time() {
185
-
186
- $product_ids = $this->get_product_ids();
187
- $total_products = count( $product_ids );
188
- $sample_size = $this->get_feed_generation_estimate_sample_size();
189
- $buffer_time = $this->get_feed_generation_buffer_time();
190
-
191
- if ( $total_products > 0 ) {
192
-
193
- if ( $total_products < $sample_size ) {
194
-
195
- $sample_size = $total_products;
196
-
197
- } else {
198
-
199
- $product_ids = array_slice( $product_ids, 0, $sample_size );
200
- }
201
-
202
- $start_time = microtime( true );
203
-
204
- $this->write_product_feed_file( $product_ids, true );
205
-
206
- $end_time = microtime( true );
207
-
208
- $time_spent = $end_time - $start_time;
209
-
210
- // estimated Time = 150% of Linear extrapolation of the time to generate n products + buffer time.
211
- $time_estimate = $time_spent * $total_products / $sample_size * 1.5 + $buffer_time;
212
-
213
- } else {
214
-
215
- $time_estimate = $buffer_time;
216
- }
217
-
218
- WC_Facebookcommerce_Utils::log( 'Feed Generation Time Estimate: ' . $time_estimate );
219
-
220
- return $time_estimate;
221
- }
222
-
223
-
224
- /**
225
- * Gets the average feed generation time.
226
- *
227
- * @since 1.11.0
228
- *
229
- * @return float
230
- */
231
- private function get_average_feed_generation_time() {
232
-
233
- return get_transient( self::TRANSIENT_AVERAGE_FEED_GENERATION_TIME );
234
- }
235
-
236
-
237
- /**
238
- * Gets the number of products to use when estimating the feed file generation time.
239
- *
240
- * @since 1.11.0
241
- *
242
- * @return int
243
- */
244
- private function get_feed_generation_estimate_sample_size() {
245
-
246
- /**
247
- * Filters the number of products to use when estimating the feed file generation time.
248
- *
249
- * @since 1.11.0
250
- *
251
- * @param int $sample_size number of products to use when estimating the feed file generation time
252
- */
253
- $sample_size = (int) apply_filters( 'wc_facebook_product_catalog_feed_generation_estimate_sample_size', 200 );
254
-
255
- return max( $sample_size, 100 );
256
- }
257
-
258
-
259
- /**
260
- * Gets the number of seconds to add as a buffer when estimating the feed file generation time.
261
- *
262
- * @since 1.11.0
263
- *
264
- * @return int
265
- */
266
- private function get_feed_generation_buffer_time() {
267
-
268
- /**
269
- * Filters the number of seconds to add as a buffer when estimating the feed file generation time.
270
- *
271
- * @since 1.11.0
272
- *
273
- * @param int $time number of seconds to add as a buffer when estimating the feed file generation time
274
- */
275
- $buffer_time = (int) apply_filters( 'wc_facebook_product_catalog_feed_generation_buffer_time', 30 );
276
-
277
- return max( $buffer_time, 5 );
278
- }
279
-
280
-
281
  /**
282
  * Gets the product catalog feed file path.
283
  *
@@ -481,7 +289,7 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
481
  *
482
  * @since 1.11.0
483
  */
484
- private function create_files_to_protect_product_feed_directory() {
485
 
486
  $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
487
 
@@ -518,96 +326,133 @@ if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) :
518
  * @since 1.11.0
519
  *
520
  * @param int[] $wp_ids product IDs
521
- * @param bool $is_dry_run whether this is a dry run or the file should be written
522
  * @return bool
523
  */
524
- public function write_product_feed_file( $wp_ids, $is_dry_run = false ) {
525
 
526
  try {
527
 
528
- if ( ! $is_dry_run ) {
 
529
 
530
- $temp_file_path = $this->get_temp_file_path();
531
- $temp_feed_file = @fopen( $temp_file_path, 'w' );
532
 
533
- // check if we can open the temporary feed file
534
- if ( false === $temp_feed_file || ! is_writable( $temp_file_path ) ) {
535
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog temporary feed file for writing', 'facebook-for-woocommerce' ), 500 );
536
- }
537
 
538
- $file_path = $this->get_file_path();
539
 
540
- // check if we will be able to write to the final feed file
541
- if ( file_exists( $file_path ) && ! is_writable( $file_path ) ) {
542
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog feed file for writing', 'facebook-for-woocommerce' ), 500 );
543
- }
544
 
545
- fwrite( $temp_feed_file, $this->get_product_feed_header_row() );
 
 
 
 
 
546
  }
547
 
548
- $product_group_attribute_variants = array();
 
549
 
550
- foreach ( $wp_ids as $wp_id ) {
 
 
551
 
552
- $woo_product = new WC_Facebook_Product( $wp_id );
 
553
 
554
- // skip if we don't have a valid product object
555
- if ( ! $woo_product->woo_product instanceof \WC_Product ) {
556
- continue;
557
- }
 
 
 
 
 
 
 
558
 
559
- // skip if not enabled for sync
560
- if ( ! Products::product_should_be_synced( $woo_product->woo_product ) ) {
561
- continue;
562
- }
563
 
564
- $product_data_as_feed_row = $this->prepare_product_for_feed(
565
- $woo_product,
566
- $product_group_attribute_variants
567
- );
568
 
569
- if ( ! empty( $temp_feed_file ) ) {
570
- fwrite( $temp_feed_file, $product_data_as_feed_row );
571
- }
572
- }
573
 
574
- wp_reset_postdata();
 
 
575
 
576
- if ( ! empty( $temp_feed_file ) ) {
577
- fclose( $temp_feed_file );
578
- }
 
 
 
 
 
 
579
 
580
- if ( ! empty( $temp_file_path ) && ! empty( $file_path ) && ! empty( $temp_feed_file ) ) {
581
 
582
- $renamed = rename( $temp_file_path, $file_path );
583
 
584
- if ( empty( $renamed ) ) {
585
- throw new Framework\SV_WC_Plugin_Exception( __( 'Could not rename the product catalog feed file', 'facebook-for-woocommerce' ), 500 );
586
- }
587
  }
588
 
589
- $written = true;
 
 
 
590
 
591
- } catch ( Exception $e ) {
 
 
 
592
 
593
- WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
 
 
 
594
 
595
- $written = false;
596
 
597
- // close the temporary file
598
- if ( ! empty( $temp_feed_file ) && is_resource( $temp_feed_file ) ) {
 
 
599
 
600
- fclose( $temp_feed_file );
601
- }
 
 
 
 
 
 
 
 
 
 
602
 
603
- // delete the temporary file
604
- if ( ! empty( $temp_file_path ) && file_exists( $temp_file_path ) ) {
605
 
606
- unlink( $temp_file_path );
 
607
  }
608
  }
609
-
610
- return $written;
611
  }
612
 
613
  public function get_product_feed_header_row() {
25
  class WC_Facebook_Product_Feed {
26
 
27
 
 
 
 
28
  /** @var string product catalog feed file directory inside the uploads folder */
29
+ const UPLOADS_DIRECTORY = 'facebook_for_woocommerce';
30
+ const FILE_NAME = 'product_catalog_%s.csv';
 
 
 
 
31
  const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
32
  const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
33
  const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
51
  $this->feed_id = $feed_id;
52
  }
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * Generates the product catalog feed.
56
  *
73
  $generation_time = microtime( true ) - $start_time;
74
  facebook_for_woocommerce()->get_tracker()->track_feed_file_generation_time( $generation_time );
75
 
 
 
76
  \WC_Facebookcommerce_Utils::log( 'Product feed file generated' );
77
 
78
  } catch ( \Exception $exception ) {
86
  $profiling_logger->stop( 'generate_feed' );
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  /**
90
  * Gets the product catalog feed file path.
91
  *
289
  *
290
  * @since 1.11.0
291
  */
292
+ public function create_files_to_protect_product_feed_directory() {
293
 
294
  $catalog_feed_directory = trailingslashit( $this->get_file_directory() );
295
 
326
  * @since 1.11.0
327
  *
328
  * @param int[] $wp_ids product IDs
 
329
  * @return bool
330
  */
331
+ public function write_product_feed_file( $wp_ids ) {
332
 
333
  try {
334
 
335
+ // Step 1: Prepare the temporary empty feed file with header row.
336
+ $temp_feed_file = $this->prepare_temporary_feed_file();
337
 
338
+ // Step 2: Write products feed into the temporary feed file.
339
+ $this->write_products_feed_to_temp_file( $wp_ids, $temp_feed_file );
340
 
341
+ // Step 3: Rename temporary feed file to final feed file.
342
+ $this->rename_temporary_feed_file_to_final_feed_file();
 
 
343
 
344
+ $written = true;
345
 
346
+ } catch ( Exception $e ) {
347
+
348
+ WC_Facebookcommerce_Utils::log( json_encode( $e->getMessage() ) );
 
349
 
350
+ $written = false;
351
+
352
+ // close the temporary file
353
+ if ( ! empty( $temp_feed_file ) && is_resource( $temp_feed_file ) ) {
354
+
355
+ fclose( $temp_feed_file );
356
  }
357
 
358
+ // delete the temporary file
359
+ if ( ! empty( $temp_file_path ) && file_exists( $temp_file_path ) ) {
360
 
361
+ unlink( $temp_file_path );
362
+ }
363
+ }
364
 
365
+ return $written;
366
+ }
367
 
368
+ /**
369
+ * Prepare a fresh empty temporary feed file with the header row.
370
+ *
371
+ * @since 2.6.6
372
+ *
373
+ * @throws Framework\SV_WC_Plugin_Exception We can't open the file or the file is not writable.
374
+ * @return resource A file pointer resource.
375
+ */
376
+ public function prepare_temporary_feed_file() {
377
+ $temp_file_path = $this->get_temp_file_path();
378
+ $temp_feed_file = @fopen( $temp_file_path, 'w' );
379
 
380
+ // check if we can open the temporary feed file
381
+ if ( false === $temp_feed_file || ! is_writable( $temp_file_path ) ) {
382
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog temporary feed file for writing', 'facebook-for-woocommerce' ), 500 );
383
+ }
384
 
385
+ $file_path = $this->get_file_path();
 
 
 
386
 
387
+ // check if we will be able to write to the final feed file
388
+ if ( file_exists( $file_path ) && ! is_writable( $file_path ) ) {
389
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Could not open the product catalog feed file for writing', 'facebook-for-woocommerce' ), 500 );
390
+ }
391
 
392
+ fwrite( $temp_feed_file, $this->get_product_feed_header_row() );
393
+ return $temp_feed_file;
394
+ }
395
 
396
+ /**
397
+ * Write products feed into a file.
398
+ *
399
+ * @since 2.6.6
400
+ *
401
+ * @return void
402
+ */
403
+ public function write_products_feed_to_temp_file( $wp_ids, $temp_feed_file ) {
404
+ $product_group_attribute_variants = array();
405
 
406
+ foreach ( $wp_ids as $wp_id ) {
407
 
408
+ $woo_product = new WC_Facebook_Product( $wp_id );
409
 
410
+ // Skip if we don't have a valid product object.
411
+ if ( ! $woo_product->woo_product instanceof \WC_Product ) {
412
+ continue;
413
  }
414
 
415
+ // Skip if not enabled for sync.
416
+ if ( ! facebook_for_woocommerce()->get_product_sync_validator( $woo_product->woo_product )->passes_all_checks() ) {
417
+ continue;
418
+ }
419
 
420
+ $product_data_as_feed_row = $this->prepare_product_for_feed(
421
+ $woo_product,
422
+ $product_group_attribute_variants
423
+ );
424
 
425
+ if ( ! empty( $temp_feed_file ) ) {
426
+ fwrite( $temp_feed_file, $product_data_as_feed_row );
427
+ }
428
+ }
429
 
430
+ wp_reset_postdata();
431
 
432
+ if ( ! empty( $temp_feed_file ) ) {
433
+ fclose( $temp_feed_file );
434
+ }
435
+ }
436
 
437
+ /**
438
+ * Rename temporary feed file into the final feed file.
439
+ * This is the last step fo the feed generation procedure.
440
+ *
441
+ * @since 2.6.6
442
+ *
443
+ * @return void
444
+ */
445
+ public function rename_temporary_feed_file_to_final_feed_file() {
446
+ $file_path = $this->get_file_path();
447
+ $temp_file_path = $this->get_temp_file_path();
448
+ if ( ! empty( $temp_file_path ) && ! empty( $file_path ) ) {
449
 
450
+ $renamed = rename( $temp_file_path, $file_path );
 
451
 
452
+ if ( empty( $renamed ) ) {
453
+ throw new Framework\SV_WC_Plugin_Exception( __( 'Could not rename the product catalog feed file', 'facebook-for-woocommerce' ), 500 );
454
  }
455
  }
 
 
456
  }
457
 
458
  public function get_product_feed_header_row() {
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: facebook, automattic, woothemes
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
- Tested up to: 5.8
6
- Stable tag: 2.6.5
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
@@ -39,7 +39,17 @@ When opening a bug on GitHub, please give us as many details as possible.
39
 
40
  == Changelog ==
41
 
42
- = 2.6.5 - 2021-09-16 =
 
 
 
 
 
 
 
 
 
 
43
  * Fix - Incorrect `is_readable()` usage when loading Integration classes.
44
  * Tweak - WC 5.7 compatibility.
45
  * Tweak - WP 5.8 compatibility.
2
  Contributors: facebook, automattic, woothemes
3
  Tags: facebook, shop, catalog, advertise, pixel, product
4
  Requires at least: 4.4
5
+ Tested up to: 5.8
6
+ Stable tag: 2.6.6
7
  Requires PHP: 5.6 or greater
8
  MySQL: 5.6 or greater
9
  License: GPLv2 or later
39
 
40
  == Changelog ==
41
 
42
+ = 2.6.6 - 2021-11-03 =
43
+ * New - Memory improved feed generation process. #2099
44
+ * New - Add compatibility with the WooCommerce checkout block. #2095
45
+ * New - Track batched feed generation time in the tracker snapshots. #2104
46
+ * New - Track usage of the new style feed generator in the tracker snapshots. #2103
47
+ * New - Hide headers in logs for better visibility. #2093
48
+ * Dev - Update composer dependencies. #2090
49
+ * New - Add no synchronization reason to the product edit screen in the Facebook meta box. #1937
50
+ * Fix - Use published variations only for the default variation. #2091
51
+
52
+ = 2.6.5 - 2021-09-16 =
53
  * Fix - Incorrect `is_readable()` usage when loading Integration classes.
54
  * Tweak - WC 5.7 compatibility.
55
  * Tweak - WP 5.8 compatibility.
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit0a5c9cf66a2c48e36a98d940342789aa::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitde7d5bed3e120a58eb0e6fe4bc2b3a15::getLoader();
vendor/composer/ClassLoader.php CHANGED
@@ -42,21 +42,75 @@ namespace Composer\Autoload;
42
  */
43
  class ClassLoader
44
  {
 
 
 
45
  // PSR-4
 
 
 
 
46
  private $prefixLengthsPsr4 = array();
 
 
 
 
47
  private $prefixDirsPsr4 = array();
 
 
 
 
48
  private $fallbackDirsPsr4 = array();
49
 
50
  // PSR-0
 
 
 
 
51
  private $prefixesPsr0 = array();
 
 
 
 
52
  private $fallbackDirsPsr0 = array();
53
 
 
54
  private $useIncludePath = false;
 
 
 
 
 
55
  private $classMap = array();
 
 
56
  private $classMapAuthoritative = false;
 
 
 
 
 
57
  private $missingClasses = array();
 
 
58
  private $apcuPrefix;
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  public function getPrefixes()
61
  {
62
  if (!empty($this->prefixesPsr0)) {
@@ -66,28 +120,47 @@ class ClassLoader
66
  return array();
67
  }
68
 
 
 
 
 
69
  public function getPrefixesPsr4()
70
  {
71
  return $this->prefixDirsPsr4;
72
  }
73
 
 
 
 
 
74
  public function getFallbackDirs()
75
  {
76
  return $this->fallbackDirsPsr0;
77
  }
78
 
 
 
 
 
79
  public function getFallbackDirsPsr4()
80
  {
81
  return $this->fallbackDirsPsr4;
82
  }
83
 
 
 
 
 
84
  public function getClassMap()
85
  {
86
  return $this->classMap;
87
  }
88
 
89
  /**
90
- * @param array $classMap Class to filename map
 
 
 
91
  */
92
  public function addClassMap(array $classMap)
93
  {
@@ -102,9 +175,11 @@ class ClassLoader
102
  * Registers a set of PSR-0 directories for a given prefix, either
103
  * appending or prepending to the ones previously set for this prefix.
104
  *
105
- * @param string $prefix The prefix
106
- * @param array|string $paths The PSR-0 root directories
107
- * @param bool $prepend Whether to prepend the directories
 
 
108
  */
109
  public function add($prefix, $paths, $prepend = false)
110
  {
@@ -147,11 +222,13 @@ class ClassLoader
147
  * Registers a set of PSR-4 directories for a given namespace, either
148
  * appending or prepending to the ones previously set for this namespace.
149
  *
150
- * @param string $prefix The prefix/namespace, with trailing '\\'
151
- * @param array|string $paths The PSR-4 base directories
152
- * @param bool $prepend Whether to prepend the directories
153
  *
154
  * @throws \InvalidArgumentException
 
 
155
  */
156
  public function addPsr4($prefix, $paths, $prepend = false)
157
  {
@@ -195,8 +272,10 @@ class ClassLoader
195
  * Registers a set of PSR-0 directories for a given prefix,
196
  * replacing any others previously set for this prefix.
197
  *
198
- * @param string $prefix The prefix
199
- * @param array|string $paths The PSR-0 base directories
 
 
200
  */
201
  public function set($prefix, $paths)
202
  {
@@ -211,10 +290,12 @@ class ClassLoader
211
  * Registers a set of PSR-4 directories for a given namespace,
212
  * replacing any others previously set for this namespace.
213
  *
214
- * @param string $prefix The prefix/namespace, with trailing '\\'
215
- * @param array|string $paths The PSR-4 base directories
216
  *
217
  * @throws \InvalidArgumentException
 
 
218
  */
219
  public function setPsr4($prefix, $paths)
220
  {
@@ -234,6 +315,8 @@ class ClassLoader
234
  * Turns on searching the include path for class files.
235
  *
236
  * @param bool $useIncludePath
 
 
237
  */
238
  public function setUseIncludePath($useIncludePath)
239
  {
@@ -256,6 +339,8 @@ class ClassLoader
256
  * that have not been registered with the class map.
257
  *
258
  * @param bool $classMapAuthoritative
 
 
259
  */
260
  public function setClassMapAuthoritative($classMapAuthoritative)
261
  {
@@ -276,6 +361,8 @@ class ClassLoader
276
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
  *
278
  * @param string|null $apcuPrefix
 
 
279
  */
280
  public function setApcuPrefix($apcuPrefix)
281
  {
@@ -296,25 +383,44 @@ class ClassLoader
296
  * Registers this instance as an autoloader.
297
  *
298
  * @param bool $prepend Whether to prepend the autoloader or not
 
 
299
  */
300
  public function register($prepend = false)
301
  {
302
  spl_autoload_register(array($this, 'loadClass'), true, $prepend);
 
 
 
 
 
 
 
 
 
 
 
303
  }
304
 
305
  /**
306
  * Unregisters this instance as an autoloader.
 
 
307
  */
308
  public function unregister()
309
  {
310
  spl_autoload_unregister(array($this, 'loadClass'));
 
 
 
 
311
  }
312
 
313
  /**
314
  * Loads the given class or interface.
315
  *
316
  * @param string $class The name of the class
317
- * @return bool|null True if loaded, null otherwise
318
  */
319
  public function loadClass($class)
320
  {
@@ -323,6 +429,8 @@ class ClassLoader
323
 
324
  return true;
325
  }
 
 
326
  }
327
 
328
  /**
@@ -367,6 +475,21 @@ class ClassLoader
367
  return $file;
368
  }
369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  private function findFileWithExtension($class, $ext)
371
  {
372
  // PSR-4 lookup
@@ -438,6 +561,10 @@ class ClassLoader
438
  * Scope isolated include.
439
  *
440
  * Prevents access to $this/self from included files.
 
 
 
 
441
  */
442
  function includeFile($file)
443
  {
42
  */
43
  class ClassLoader
44
  {
45
+ /** @var ?string */
46
+ private $vendorDir;
47
+
48
  // PSR-4
49
+ /**
50
+ * @var array[]
51
+ * @psalm-var array<string, array<string, int>>
52
+ */
53
  private $prefixLengthsPsr4 = array();
54
+ /**
55
+ * @var array[]
56
+ * @psalm-var array<string, array<int, string>>
57
+ */
58
  private $prefixDirsPsr4 = array();
59
+ /**
60
+ * @var array[]
61
+ * @psalm-var array<string, string>
62
+ */
63
  private $fallbackDirsPsr4 = array();
64
 
65
  // PSR-0
66
+ /**
67
+ * @var array[]
68
+ * @psalm-var array<string, array<string, string[]>>
69
+ */
70
  private $prefixesPsr0 = array();
71
+ /**
72
+ * @var array[]
73
+ * @psalm-var array<string, string>
74
+ */
75
  private $fallbackDirsPsr0 = array();
76
 
77
+ /** @var bool */
78
  private $useIncludePath = false;
79
+
80
+ /**
81
+ * @var string[]
82
+ * @psalm-var array<string, string>
83
+ */
84
  private $classMap = array();
85
+
86
+ /** @var bool */
87
  private $classMapAuthoritative = false;
88
+
89
+ /**
90
+ * @var bool[]
91
+ * @psalm-var array<string, bool>
92
+ */
93
  private $missingClasses = array();
94
+
95
+ /** @var ?string */
96
  private $apcuPrefix;
97
 
98
+ /**
99
+ * @var self[]
100
+ */
101
+ private static $registeredLoaders = array();
102
+
103
+ /**
104
+ * @param ?string $vendorDir
105
+ */
106
+ public function __construct($vendorDir = null)
107
+ {
108
+ $this->vendorDir = $vendorDir;
109
+ }
110
+
111
+ /**
112
+ * @return string[]
113
+ */
114
  public function getPrefixes()
115
  {
116
  if (!empty($this->prefixesPsr0)) {
120
  return array();
121
  }
122
 
123
+ /**
124
+ * @return array[]
125
+ * @psalm-return array<string, array<int, string>>
126
+ */
127
  public function getPrefixesPsr4()
128
  {
129
  return $this->prefixDirsPsr4;
130
  }
131
 
132
+ /**
133
+ * @return array[]
134
+ * @psalm-return array<string, string>
135
+ */
136
  public function getFallbackDirs()
137
  {
138
  return $this->fallbackDirsPsr0;
139
  }
140
 
141
+ /**
142
+ * @return array[]
143
+ * @psalm-return array<string, string>
144
+ */
145
  public function getFallbackDirsPsr4()
146
  {
147
  return $this->fallbackDirsPsr4;
148
  }
149
 
150
+ /**
151
+ * @return string[] Array of classname => path
152
+ * @psalm-var array<string, string>
153
+ */
154
  public function getClassMap()
155
  {
156
  return $this->classMap;
157
  }
158
 
159
  /**
160
+ * @param string[] $classMap Class to filename map
161
+ * @psalm-param array<string, string> $classMap
162
+ *
163
+ * @return void
164
  */
165
  public function addClassMap(array $classMap)
166
  {
175
  * Registers a set of PSR-0 directories for a given prefix, either
176
  * appending or prepending to the ones previously set for this prefix.
177
  *
178
+ * @param string $prefix The prefix
179
+ * @param string[]|string $paths The PSR-0 root directories
180
+ * @param bool $prepend Whether to prepend the directories
181
+ *
182
+ * @return void
183
  */
184
  public function add($prefix, $paths, $prepend = false)
185
  {
222
  * Registers a set of PSR-4 directories for a given namespace, either
223
  * appending or prepending to the ones previously set for this namespace.
224
  *
225
+ * @param string $prefix The prefix/namespace, with trailing '\\'
226
+ * @param string[]|string $paths The PSR-4 base directories
227
+ * @param bool $prepend Whether to prepend the directories
228
  *
229
  * @throws \InvalidArgumentException
230
+ *
231
+ * @return void
232
  */
233
  public function addPsr4($prefix, $paths, $prepend = false)
234
  {
272
  * Registers a set of PSR-0 directories for a given prefix,
273
  * replacing any others previously set for this prefix.
274
  *
275
+ * @param string $prefix The prefix
276
+ * @param string[]|string $paths The PSR-0 base directories
277
+ *
278
+ * @return void
279
  */
280
  public function set($prefix, $paths)
281
  {
290
  * Registers a set of PSR-4 directories for a given namespace,
291
  * replacing any others previously set for this namespace.
292
  *
293
+ * @param string $prefix The prefix/namespace, with trailing '\\'
294
+ * @param string[]|string $paths The PSR-4 base directories
295
  *
296
  * @throws \InvalidArgumentException
297
+ *
298
+ * @return void
299
  */
300
  public function setPsr4($prefix, $paths)
301
  {
315
  * Turns on searching the include path for class files.
316
  *
317
  * @param bool $useIncludePath
318
+ *
319
+ * @return void
320
  */
321
  public function setUseIncludePath($useIncludePath)
322
  {
339
  * that have not been registered with the class map.
340
  *
341
  * @param bool $classMapAuthoritative
342
+ *
343
+ * @return void
344
  */
345
  public function setClassMapAuthoritative($classMapAuthoritative)
346
  {
361
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362
  *
363
  * @param string|null $apcuPrefix
364
+ *
365
+ * @return void
366
  */
367
  public function setApcuPrefix($apcuPrefix)
368
  {
383
  * Registers this instance as an autoloader.
384
  *
385
  * @param bool $prepend Whether to prepend the autoloader or not
386
+ *
387
+ * @return void
388
  */
389
  public function register($prepend = false)
390
  {
391
  spl_autoload_register(array($this, 'loadClass'), true, $prepend);
392
+
393
+ if (null === $this->vendorDir) {
394
+ return;
395
+ }
396
+
397
+ if ($prepend) {
398
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
399
+ } else {
400
+ unset(self::$registeredLoaders[$this->vendorDir]);
401
+ self::$registeredLoaders[$this->vendorDir] = $this;
402
+ }
403
  }
404
 
405
  /**
406
  * Unregisters this instance as an autoloader.
407
+ *
408
+ * @return void
409
  */
410
  public function unregister()
411
  {
412
  spl_autoload_unregister(array($this, 'loadClass'));
413
+
414
+ if (null !== $this->vendorDir) {
415
+ unset(self::$registeredLoaders[$this->vendorDir]);
416
+ }
417
  }
418
 
419
  /**
420
  * Loads the given class or interface.
421
  *
422
  * @param string $class The name of the class
423
+ * @return true|null True if loaded, null otherwise
424
  */
425
  public function loadClass($class)
426
  {
429
 
430
  return true;
431
  }
432
+
433
+ return null;
434
  }
435
 
436
  /**
475
  return $file;
476
  }
477
 
478
+ /**
479
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
480
+ *
481
+ * @return self[]
482
+ */
483
+ public static function getRegisteredLoaders()
484
+ {
485
+ return self::$registeredLoaders;
486
+ }
487
+
488
+ /**
489
+ * @param string $class
490
+ * @param string $ext
491
+ * @return string|false
492
+ */
493
  private function findFileWithExtension($class, $ext)
494
  {
495
  // PSR-4 lookup
561
  * Scope isolated include.
562
  *
563
  * Prevents access to $this/self from included files.
564
+ *
565
+ * @param string $file
566
+ * @return void
567
+ * @private
568
  */
569
  function includeFile($file)
570
  {
vendor/composer/InstalledVersions.php CHANGED
@@ -1,260 +1,337 @@
1
  <?php
2
 
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
 
13
  namespace Composer;
14
 
 
15
  use Composer\Semver\VersionParser;
16
 
17
-
18
-
19
-
20
-
21
-
 
 
22
  class InstalledVersions
23
  {
24
- private static $installed = array (
25
- 'root' =>
26
- array (
27
- 'pretty_version' => 'dev-release/2.6.5',
28
- 'version' => 'dev-release/2.6.5',
29
- 'aliases' =>
30
- array (
31
- ),
32
- 'reference' => 'f4e8c6097d6622059705df6d7cbab9179609c055',
33
- 'name' => 'facebookincubator/facebook-for-woocommerce',
34
- ),
35
- 'versions' =>
36
- array (
37
- 'composer/installers' =>
38
- array (
39
- 'pretty_version' => 'v1.11.0',
40
- 'version' => '1.11.0.0',
41
- 'aliases' =>
42
- array (
43
- ),
44
- 'reference' => 'ae03311f45dfe194412081526be2e003960df74b',
45
- ),
46
- 'facebookincubator/facebook-for-woocommerce' =>
47
- array (
48
- 'pretty_version' => 'dev-release/2.6.5',
49
- 'version' => 'dev-release/2.6.5',
50
- 'aliases' =>
51
- array (
52
- ),
53
- 'reference' => 'f4e8c6097d6622059705df6d7cbab9179609c055',
54
- ),
55
- 'roundcube/plugin-installer' =>
56
- array (
57
- 'replaced' =>
58
- array (
59
- 0 => '*',
60
- ),
61
- ),
62
- 'shama/baton' =>
63
- array (
64
- 'replaced' =>
65
- array (
66
- 0 => '*',
67
- ),
68
- ),
69
- 'skyverge/wc-plugin-framework' =>
70
- array (
71
- 'pretty_version' => '5.10.0',
72
- 'version' => '5.10.0.0',
73
- 'aliases' =>
74
- array (
75
- ),
76
- 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
77
- ),
78
- 'woocommerce/action-scheduler-job-framework' =>
79
- array (
80
- 'pretty_version' => '1.0.0',
81
- 'version' => '1.0.0.0',
82
- 'aliases' =>
83
- array (
84
- ),
85
- 'reference' => '718594e15d7ba2d56f8a37743bda6d7a8296e1c8',
86
- ),
87
- ),
88
- );
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
- public static function getInstalledPackages()
97
- {
98
- return array_keys(self::$installed['versions']);
99
- }
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
- public static function isInstalled($packageName)
110
- {
111
- return isset(self::$installed['versions'][$packageName]);
112
- }
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
- public static function satisfies(VersionParser $parser, $packageName, $constraint)
128
- {
129
- $constraint = $parser->parseConstraints($constraint);
130
- $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
131
-
132
- return $provided->matches($constraint);
133
- }
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
- public static function getVersionRanges($packageName)
145
- {
146
- if (!isset(self::$installed['versions'][$packageName])) {
147
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
148
- }
149
-
150
- $ranges = array();
151
- if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
152
- $ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
153
- }
154
- if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
155
- $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
156
- }
157
- if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
158
- $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
159
- }
160
- if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
161
- $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
162
- }
163
-
164
- return implode(' || ', $ranges);
165
- }
166
-
167
-
168
-
169
-
170
-
171
- public static function getVersion($packageName)
172
- {
173
- if (!isset(self::$installed['versions'][$packageName])) {
174
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
175
- }
176
-
177
- if (!isset(self::$installed['versions'][$packageName]['version'])) {
178
- return null;
179
- }
180
-
181
- return self::$installed['versions'][$packageName]['version'];
182
- }
183
-
184
-
185
-
186
-
187
-
188
- public static function getPrettyVersion($packageName)
189
- {
190
- if (!isset(self::$installed['versions'][$packageName])) {
191
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
192
- }
193
-
194
- if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
195
- return null;
196
- }
197
-
198
- return self::$installed['versions'][$packageName]['pretty_version'];
199
- }
200
-
201
-
202
-
203
-
204
-
205
- public static function getReference($packageName)
206
- {
207
- if (!isset(self::$installed['versions'][$packageName])) {
208
- throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
209
- }
210
-
211
- if (!isset(self::$installed['versions'][$packageName]['reference'])) {
212
- return null;
213
- }
214
-
215
- return self::$installed['versions'][$packageName]['reference'];
216
- }
217
-
218
-
219
-
220
-
221
-
222
- public static function getRootPackage()
223
- {
224
- return self::$installed['root'];
225
- }
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
- public static function getRawData()
234
- {
235
- return self::$installed;
236
- }
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
- public static function reload($data)
257
- {
258
- self::$installed = $data;
259
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  }
1
  <?php
2
 
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
 
13
  namespace Composer;
14
 
15
+ use Composer\Autoload\ClassLoader;
16
  use Composer\Semver\VersionParser;
17
 
18
+ /**
19
+ * This class is copied in every Composer installed project and available to all
20
+ *
21
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
+ *
23
+ * To require its presence, you can require `composer-runtime-api ^2.0`
24
+ */
25
  class InstalledVersions
26
  {
27
+ private static $installed;
28
+ private static $canGetVendors;
29
+ private static $installedByVendor = array();
30
+
31
+ /**
32
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
33
+ *
34
+ * @return string[]
35
+ * @psalm-return list<string>
36
+ */
37
+ public static function getInstalledPackages()
38
+ {
39
+ $packages = array();
40
+ foreach (self::getInstalled() as $installed) {
41
+ $packages[] = array_keys($installed['versions']);
42
+ }
43
+
44
+ if (1 === \count($packages)) {
45
+ return $packages[0];
46
+ }
47
+
48
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
49
+ }
50
+
51
+ /**
52
+ * Returns a list of all package names with a specific type e.g. 'library'
53
+ *
54
+ * @param string $type
55
+ * @return string[]
56
+ * @psalm-return list<string>
57
+ */
58
+ public static function getInstalledPackagesByType($type)
59
+ {
60
+ $packagesByType = array();
61
+
62
+ foreach (self::getInstalled() as $installed) {
63
+ foreach ($installed['versions'] as $name => $package) {
64
+ if (isset($package['type']) && $package['type'] === $type) {
65
+ $packagesByType[] = $name;
66
+ }
67
+ }
68
+ }
69
+
70
+ return $packagesByType;
71
+ }
72
+
73
+ /**
74
+ * Checks whether the given package is installed
75
+ *
76
+ * This also returns true if the package name is provided or replaced by another package
77
+ *
78
+ * @param string $packageName
79
+ * @param bool $includeDevRequirements
80
+ * @return bool
81
+ */
82
+ public static function isInstalled($packageName, $includeDevRequirements = true)
83
+ {
84
+ foreach (self::getInstalled() as $installed) {
85
+ if (isset($installed['versions'][$packageName])) {
86
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
87
+ }
88
+ }
89
+
90
+ return false;
91
+ }
92
+
93
+ /**
94
+ * Checks whether the given package satisfies a version constraint
95
+ *
96
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
97
+ *
98
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
99
+ *
100
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
101
+ * @param string $packageName
102
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
103
+ * @return bool
104
+ */
105
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
106
+ {
107
+ $constraint = $parser->parseConstraints($constraint);
108
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
109
+
110
+ return $provided->matches($constraint);
111
+ }
112
+
113
+ /**
114
+ * Returns a version constraint representing all the range(s) which are installed for a given package
115
+ *
116
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
117
+ * whether a given version of a package is installed, and not just whether it exists
118
+ *
119
+ * @param string $packageName
120
+ * @return string Version constraint usable with composer/semver
121
+ */
122
+ public static function getVersionRanges($packageName)
123
+ {
124
+ foreach (self::getInstalled() as $installed) {
125
+ if (!isset($installed['versions'][$packageName])) {
126
+ continue;
127
+ }
128
+
129
+ $ranges = array();
130
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
131
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
132
+ }
133
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
134
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
135
+ }
136
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
137
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
138
+ }
139
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
140
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
141
+ }
142
+
143
+ return implode(' || ', $ranges);
144
+ }
145
+
146
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
147
+ }
148
+
149
+ /**
150
+ * @param string $packageName
151
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
152
+ */
153
+ public static function getVersion($packageName)
154
+ {
155
+ foreach (self::getInstalled() as $installed) {
156
+ if (!isset($installed['versions'][$packageName])) {
157
+ continue;
158
+ }
159
+
160
+ if (!isset($installed['versions'][$packageName]['version'])) {
161
+ return null;
162
+ }
163
+
164
+ return $installed['versions'][$packageName]['version'];
165
+ }
166
+
167
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
168
+ }
169
+
170
+ /**
171
+ * @param string $packageName
172
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
173
+ */
174
+ public static function getPrettyVersion($packageName)
175
+ {
176
+ foreach (self::getInstalled() as $installed) {
177
+ if (!isset($installed['versions'][$packageName])) {
178
+ continue;
179
+ }
180
+
181
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
182
+ return null;
183
+ }
184
+
185
+ return $installed['versions'][$packageName]['pretty_version'];
186
+ }
187
+
188
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
189
+ }
190
+
191
+ /**
192
+ * @param string $packageName
193
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
194
+ */
195
+ public static function getReference($packageName)
196
+ {
197
+ foreach (self::getInstalled() as $installed) {
198
+ if (!isset($installed['versions'][$packageName])) {
199
+ continue;
200
+ }
201
+
202
+ if (!isset($installed['versions'][$packageName]['reference'])) {
203
+ return null;
204
+ }
205
+
206
+ return $installed['versions'][$packageName]['reference'];
207
+ }
208
+
209
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
210
+ }
211
+
212
+ /**
213
+ * @param string $packageName
214
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
215
+ */
216
+ public static function getInstallPath($packageName)
217
+ {
218
+ foreach (self::getInstalled() as $installed) {
219
+ if (!isset($installed['versions'][$packageName])) {
220
+ continue;
221
+ }
222
+
223
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
224
+ }
225
+
226
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
227
+ }
228
+
229
+ /**
230
+ * @return array
231
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
232
+ */
233
+ public static function getRootPackage()
234
+ {
235
+ $installed = self::getInstalled();
236
+
237
+ return $installed[0]['root'];
238
+ }
239
+
240
+ /**
241
+ * Returns the raw installed.php data for custom implementations
242
+ *
243
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
244
+ * @return array[]
245
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
246
+ */
247
+ public static function getRawData()
248
+ {
249
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
250
+
251
+ if (null === self::$installed) {
252
+ // only require the installed.php file if this file is loaded from its dumped location,
253
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
254
+ if (substr(__DIR__, -8, 1) !== 'C') {
255
+ self::$installed = include __DIR__ . '/installed.php';
256
+ } else {
257
+ self::$installed = array();
258
+ }
259
+ }
260
+
261
+ return self::$installed;
262
+ }
263
+
264
+ /**
265
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
266
+ *
267
+ * @return array[]
268
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
269
+ */
270
+ public static function getAllRawData()
271
+ {
272
+ return self::getInstalled();
273
+ }
274
+
275
+ /**
276
+ * Lets you reload the static array from another file
277
+ *
278
+ * This is only useful for complex integrations in which a project needs to use
279
+ * this class but then also needs to execute another project's autoloader in process,
280
+ * and wants to ensure both projects have access to their version of installed.php.
281
+ *
282
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
283
+ * the data it needs from this class, then call reload() with
284
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
285
+ * the project in which it runs can then also use this class safely, without
286
+ * interference between PHPUnit's dependencies and the project's dependencies.
287
+ *
288
+ * @param array[] $data A vendor/composer/installed.php data set
289
+ * @return void
290
+ *
291
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
292
+ */
293
+ public static function reload($data)
294
+ {
295
+ self::$installed = $data;
296
+ self::$installedByVendor = array();
297
+ }
298
+
299
+ /**
300
+ * @return array[]
301
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
302
+ */
303
+ private static function getInstalled()
304
+ {
305
+ if (null === self::$canGetVendors) {
306
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
307
+ }
308
+
309
+ $installed = array();
310
+
311
+ if (self::$canGetVendors) {
312
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
313
+ if (isset(self::$installedByVendor[$vendorDir])) {
314
+ $installed[] = self::$installedByVendor[$vendorDir];
315
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
316
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
317
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
318
+ self::$installed = $installed[count($installed) - 1];
319
+ }
320
+ }
321
+ }
322
+ }
323
+
324
+ if (null === self::$installed) {
325
+ // only require the installed.php file if this file is loaded from its dumped location,
326
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
327
+ if (substr(__DIR__, -8, 1) !== 'C') {
328
+ self::$installed = require __DIR__ . '/installed.php';
329
+ } else {
330
+ self::$installed = array();
331
+ }
332
+ }
333
+ $installed[] = self::$installed;
334
+
335
+ return $installed;
336
+ }
337
  }
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit0a5c9cf66a2c48e36a98d940342789aa
6
  {
7
  private static $loader;
8
 
@@ -24,15 +24,15 @@ class ComposerAutoloaderInit0a5c9cf66a2c48e36a98d940342789aa
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
- spl_autoload_register(array('ComposerAutoloaderInit0a5c9cf66a2c48e36a98d940342789aa', 'loadClassLoader'), true, true);
28
- self::$loader = $loader = new \Composer\Autoload\ClassLoader();
29
- spl_autoload_unregister(array('ComposerAutoloaderInit0a5c9cf66a2c48e36a98d940342789aa', 'loadClassLoader'));
30
 
31
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
  if ($useStaticLoader) {
33
  require __DIR__ . '/autoload_static.php';
34
 
35
- call_user_func(\Composer\Autoload\ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa::getInitializer($loader));
36
  } else {
37
  $map = require __DIR__ . '/autoload_namespaces.php';
38
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitde7d5bed3e120a58eb0e6fe4bc2b3a15
6
  {
7
  private static $loader;
8
 
24
 
25
  require __DIR__ . '/platform_check.php';
26
 
27
+ spl_autoload_register(array('ComposerAutoloaderInitde7d5bed3e120a58eb0e6fe4bc2b3a15', 'loadClassLoader'), true, true);
28
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInitde7d5bed3e120a58eb0e6fe4bc2b3a15', 'loadClassLoader'));
30
 
31
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
32
  if ($useStaticLoader) {
33
  require __DIR__ . '/autoload_static.php';
34
 
35
+ call_user_func(\Composer\Autoload\ComposerStaticInitde7d5bed3e120a58eb0e6fe4bc2b3a15::getInitializer($loader));
36
  } else {
37
  $map = require __DIR__ . '/autoload_namespaces.php';
38
  foreach ($map as $namespace => $path) {
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'S' =>
@@ -43,9 +43,9 @@ class ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa
43
  public static function getInitializer(ClassLoader $loader)
44
  {
45
  return \Closure::bind(function () use ($loader) {
46
- $loader->prefixLengthsPsr4 = ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa::$prefixLengthsPsr4;
47
- $loader->prefixDirsPsr4 = ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa::$prefixDirsPsr4;
48
- $loader->classMap = ComposerStaticInit0a5c9cf66a2c48e36a98d940342789aa::$classMap;
49
 
50
  }, null, ClassLoader::class);
51
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInitde7d5bed3e120a58eb0e6fe4bc2b3a15
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'S' =>
43
  public static function getInitializer(ClassLoader $loader)
44
  {
45
  return \Closure::bind(function () use ($loader) {
46
+ $loader->prefixLengthsPsr4 = ComposerStaticInitde7d5bed3e120a58eb0e6fe4bc2b3a15::$prefixLengthsPsr4;
47
+ $loader->prefixDirsPsr4 = ComposerStaticInitde7d5bed3e120a58eb0e6fe4bc2b3a15::$prefixDirsPsr4;
48
+ $loader->classMap = ComposerStaticInitde7d5bed3e120a58eb0e6fe4bc2b3a15::$classMap;
49
 
50
  }, null, ClassLoader::class);
51
  }
vendor/composer/installed.json CHANGED
@@ -2,17 +2,17 @@
2
  "packages": [
3
  {
4
  "name": "composer/installers",
5
- "version": "v1.11.0",
6
- "version_normalized": "1.11.0.0",
7
  "source": {
8
  "type": "git",
9
  "url": "https://github.com/composer/installers.git",
10
- "reference": "ae03311f45dfe194412081526be2e003960df74b"
11
  },
12
  "dist": {
13
  "type": "zip",
14
- "url": "https://api.github.com/repos/composer/installers/zipball/ae03311f45dfe194412081526be2e003960df74b",
15
- "reference": "ae03311f45dfe194412081526be2e003960df74b",
16
  "shasum": ""
17
  },
18
  "require": {
@@ -30,7 +30,7 @@
30
  "symfony/phpunit-bridge": "^4.2 || ^5",
31
  "symfony/process": "^2.3"
32
  },
33
- "time": "2021-04-28T06:42:17+00:00",
34
  "type": "composer-plugin",
35
  "extra": {
36
  "class": "Composer\\Installers\\Plugin",
@@ -113,6 +113,7 @@
113
  "modx",
114
  "moodle",
115
  "osclass",
 
116
  "phpbb",
117
  "piwik",
118
  "ppi",
@@ -135,7 +136,7 @@
135
  ],
136
  "support": {
137
  "issues": "https://github.com/composer/installers/issues",
138
- "source": "https://github.com/composer/installers/tree/v1.11.0"
139
  },
140
  "funding": [
141
  {
@@ -183,17 +184,17 @@
183
  },
184
  {
185
  "name": "woocommerce/action-scheduler-job-framework",
186
- "version": "1.0.0",
187
- "version_normalized": "1.0.0.0",
188
  "source": {
189
  "type": "git",
190
  "url": "https://github.com/woocommerce/action-scheduler-job-framework.git",
191
- "reference": "718594e15d7ba2d56f8a37743bda6d7a8296e1c8"
192
  },
193
  "dist": {
194
  "type": "zip",
195
- "url": "https://api.github.com/repos/woocommerce/action-scheduler-job-framework/zipball/718594e15d7ba2d56f8a37743bda6d7a8296e1c8",
196
- "reference": "718594e15d7ba2d56f8a37743bda6d7a8296e1c8",
197
  "shasum": ""
198
  },
199
  "require": {
@@ -202,7 +203,7 @@
202
  "require-dev": {
203
  "woocommerce/woocommerce-sniffs": "0.1.0"
204
  },
205
- "time": "2021-05-05T02:11:38+00:00",
206
  "type": "library",
207
  "installation-source": "dist",
208
  "autoload": {
@@ -220,7 +221,7 @@
220
  ],
221
  "description": "A job framework for Action Scheduler (actionscheduler.org)",
222
  "support": {
223
- "source": "https://github.com/woocommerce/action-scheduler-job-framework/tree/1.0.0",
224
  "issues": "https://github.com/woocommerce/action-scheduler-job-framework/issues"
225
  },
226
  "install-path": "../woocommerce/action-scheduler-job-framework"
2
  "packages": [
3
  {
4
  "name": "composer/installers",
5
+ "version": "v1.12.0",
6
+ "version_normalized": "1.12.0.0",
7
  "source": {
8
  "type": "git",
9
  "url": "https://github.com/composer/installers.git",
10
+ "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19"
11
  },
12
  "dist": {
13
  "type": "zip",
14
+ "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19",
15
+ "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19",
16
  "shasum": ""
17
  },
18
  "require": {
30
  "symfony/phpunit-bridge": "^4.2 || ^5",
31
  "symfony/process": "^2.3"
32
  },
33
+ "time": "2021-09-13T08:19:44+00:00",
34
  "type": "composer-plugin",
35
  "extra": {
36
  "class": "Composer\\Installers\\Plugin",
113
  "modx",
114
  "moodle",
115
  "osclass",
116
+ "pantheon",
117
  "phpbb",
118
  "piwik",
119
  "ppi",
136
  ],
137
  "support": {
138
  "issues": "https://github.com/composer/installers/issues",
139
+ "source": "https://github.com/composer/installers/tree/v1.12.0"
140
  },
141
  "funding": [
142
  {
184
  },
185
  {
186
  "name": "woocommerce/action-scheduler-job-framework",
187
+ "version": "2.0.0",
188
+ "version_normalized": "2.0.0.0",
189
  "source": {
190
  "type": "git",
191
  "url": "https://github.com/woocommerce/action-scheduler-job-framework.git",
192
+ "reference": "b0b21b9cc87e476ba7f8817050b39274ea7d6732"
193
  },
194
  "dist": {
195
  "type": "zip",
196
+ "url": "https://api.github.com/repos/woocommerce/action-scheduler-job-framework/zipball/b0b21b9cc87e476ba7f8817050b39274ea7d6732",
197
+ "reference": "b0b21b9cc87e476ba7f8817050b39274ea7d6732",
198
  "shasum": ""
199
  },
200
  "require": {
203
  "require-dev": {
204
  "woocommerce/woocommerce-sniffs": "0.1.0"
205
  },
206
+ "time": "2021-05-20T02:32:48+00:00",
207
  "type": "library",
208
  "installation-source": "dist",
209
  "autoload": {
221
  ],
222
  "description": "A job framework for Action Scheduler (actionscheduler.org)",
223
  "support": {
224
+ "source": "https://github.com/woocommerce/action-scheduler-job-framework/tree/2.0.0",
225
  "issues": "https://github.com/woocommerce/action-scheduler-job-framework/issues"
226
  },
227
  "install-path": "../woocommerce/action-scheduler-job-framework"
vendor/composer/installed.php CHANGED
@@ -1,65 +1,62 @@
1
- <?php return array (
2
- 'root' =>
3
- array (
4
- 'pretty_version' => 'dev-release/2.6.5',
5
- 'version' => 'dev-release/2.6.5',
6
- 'aliases' =>
7
- array (
 
 
 
8
  ),
9
- 'reference' => 'f4e8c6097d6622059705df6d7cbab9179609c055',
10
- 'name' => 'facebookincubator/facebook-for-woocommerce',
11
- ),
12
- 'versions' =>
13
- array (
14
- 'composer/installers' =>
15
- array (
16
- 'pretty_version' => 'v1.11.0',
17
- 'version' => '1.11.0.0',
18
- 'aliases' =>
19
- array (
20
- ),
21
- 'reference' => 'ae03311f45dfe194412081526be2e003960df74b',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ),
23
- 'facebookincubator/facebook-for-woocommerce' =>
24
- array (
25
- 'pretty_version' => 'dev-release/2.6.5',
26
- 'version' => 'dev-release/2.6.5',
27
- 'aliases' =>
28
- array (
29
- ),
30
- 'reference' => 'f4e8c6097d6622059705df6d7cbab9179609c055',
31
- ),
32
- 'roundcube/plugin-installer' =>
33
- array (
34
- 'replaced' =>
35
- array (
36
- 0 => '*',
37
- ),
38
- ),
39
- 'shama/baton' =>
40
- array (
41
- 'replaced' =>
42
- array (
43
- 0 => '*',
44
- ),
45
- ),
46
- 'skyverge/wc-plugin-framework' =>
47
- array (
48
- 'pretty_version' => '5.10.0',
49
- 'version' => '5.10.0.0',
50
- 'aliases' =>
51
- array (
52
- ),
53
- 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
54
- ),
55
- 'woocommerce/action-scheduler-job-framework' =>
56
- array (
57
- 'pretty_version' => '1.0.0',
58
- 'version' => '1.0.0.0',
59
- 'aliases' =>
60
- array (
61
- ),
62
- 'reference' => '718594e15d7ba2d56f8a37743bda6d7a8296e1c8',
63
- ),
64
- ),
65
  );
1
+ <?php return array(
2
+ 'root' => array(
3
+ 'pretty_version' => 'dev-release/2.6.6',
4
+ 'version' => 'dev-release/2.6.6',
5
+ 'type' => 'wordpress-plugin',
6
+ 'install_path' => __DIR__ . '/../../',
7
+ 'aliases' => array(),
8
+ 'reference' => '225d96525f5d3ac9330f597ba17af8041b24082a',
9
+ 'name' => 'facebookincubator/facebook-for-woocommerce',
10
+ 'dev' => false,
11
  ),
12
+ 'versions' => array(
13
+ 'composer/installers' => array(
14
+ 'pretty_version' => 'v1.12.0',
15
+ 'version' => '1.12.0.0',
16
+ 'type' => 'composer-plugin',
17
+ 'install_path' => __DIR__ . '/./installers',
18
+ 'aliases' => array(),
19
+ 'reference' => 'd20a64ed3c94748397ff5973488761b22f6d3f19',
20
+ 'dev_requirement' => false,
21
+ ),
22
+ 'facebookincubator/facebook-for-woocommerce' => array(
23
+ 'pretty_version' => 'dev-release/2.6.6',
24
+ 'version' => 'dev-release/2.6.6',
25
+ 'type' => 'wordpress-plugin',
26
+ 'install_path' => __DIR__ . '/../../',
27
+ 'aliases' => array(),
28
+ 'reference' => '225d96525f5d3ac9330f597ba17af8041b24082a',
29
+ 'dev_requirement' => false,
30
+ ),
31
+ 'roundcube/plugin-installer' => array(
32
+ 'dev_requirement' => false,
33
+ 'replaced' => array(
34
+ 0 => '*',
35
+ ),
36
+ ),
37
+ 'shama/baton' => array(
38
+ 'dev_requirement' => false,
39
+ 'replaced' => array(
40
+ 0 => '*',
41
+ ),
42
+ ),
43
+ 'skyverge/wc-plugin-framework' => array(
44
+ 'pretty_version' => '5.10.0',
45
+ 'version' => '5.10.0.0',
46
+ 'type' => 'library',
47
+ 'install_path' => __DIR__ . '/../skyverge/wc-plugin-framework',
48
+ 'aliases' => array(),
49
+ 'reference' => 'e230d7c40286854e49c0cafeec3398cbf2427a64',
50
+ 'dev_requirement' => false,
51
+ ),
52
+ 'woocommerce/action-scheduler-job-framework' => array(
53
+ 'pretty_version' => '2.0.0',
54
+ 'version' => '2.0.0.0',
55
+ 'type' => 'library',
56
+ 'install_path' => __DIR__ . '/../woocommerce/action-scheduler-job-framework',
57
+ 'aliases' => array(),
58
+ 'reference' => 'b0b21b9cc87e476ba7f8817050b39274ea7d6732',
59
+ 'dev_requirement' => false,
60
+ ),
61
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  );
vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php CHANGED
@@ -47,6 +47,7 @@ class CakePHPInstaller extends BaseInstaller
47
  * @param string $matcher
48
  * @param string $version
49
  * @return bool
 
50
  */
51
  protected function matchesCakeVersion($matcher, $version)
52
  {
47
  * @param string $matcher
48
  * @param string $version
49
  * @return bool
50
+ * @phpstan-param Constraint::STR_OP_* $matcher
51
  */
52
  protected function matchesCakeVersion($matcher, $version)
53
  {
vendor/composer/installers/src/Composer/Installers/Installer.php CHANGED
@@ -91,6 +91,7 @@ class Installer extends LibraryInstaller
91
  'phifty' => 'PhiftyInstaller',
92
  'porto' => 'PortoInstaller',
93
  'processwire' => 'ProcessWireInstaller',
 
94
  'redaxo' => 'RedaxoInstaller',
95
  'redaxo5' => 'Redaxo5Installer',
96
  'reindex' => 'ReIndexInstaller',
91
  'phifty' => 'PhiftyInstaller',
92
  'porto' => 'PortoInstaller',
93
  'processwire' => 'ProcessWireInstaller',
94
+ 'quicksilver' => 'PantheonInstaller',
95
  'redaxo' => 'RedaxoInstaller',
96
  'redaxo5' => 'Redaxo5Installer',
97
  'reindex' => 'ReIndexInstaller',
vendor/composer/installers/src/Composer/Installers/PantheonInstaller.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Composer\Installers;
4
+
5
+ class PantheonInstaller extends BaseInstaller
6
+ {
7
+ /** @var array<string, string> */
8
+ protected $locations = array(
9
+ 'script' => 'web/private/scripts/quicksilver/{$name}',
10
+ 'module' => 'web/private/scripts/quicksilver/{$name}',
11
+ );
12
+ }
vendor/woocommerce/action-scheduler-job-framework/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <!-- For now we follow Gutenberg's package changelog format https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. -->
2
+
3
+ ## 2.0.0 (2021-05-19)
4
+
5
+ ### Breaking Changes
6
+
7
+ - Removed `...AbstractChainedJob::filter_items_before_processing` method in favor of the more flexible `process_items` method ([#2](https://github.com/woocommerce/action-scheduler-job-framework/pull/2))
8
+
9
+ ### New Feature
10
+ - Added `...AbstractChainedJob::process_items` method to give jobs more control over how the whole batch is processed ([#2](https://github.com/woocommerce/action-scheduler-job-framework/pull/2))
vendor/woocommerce/action-scheduler-job-framework/README.md CHANGED
@@ -3,6 +3,36 @@
3
  ## Requirements
4
 
5
  - PHP 7.0+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  ## Chained Jobs
8
 
3
  ## Requirements
4
 
5
  - PHP 7.0+
6
+ - [Jetpack Autoloader](https://github.com/Automattic/jetpack-autoloader) - Required to ensure the latest version of the framework is used in case multiple plugins have it as a dependency
7
+
8
+
9
+ ## Versioning & breaking changes
10
+
11
+ This package follows [Semver](https://semver.org/) for versioning.
12
+
13
+ This framework may receive breaking changes at the moment while it is only used in the [Facebook for WooCommerce](https://github.com/woocommerce/facebook-for-woocommerce) plugin.
14
+ However, once we use it in another plugin we will need to reject all breaking changes.
15
+
16
+ ## Installation
17
+
18
+ The framework should be installed via [Composer](https://getcomposer.org/).
19
+
20
+ 1. Add the following to your project's `composer.json` file:
21
+ ```json
22
+ {
23
+ "repositories": [
24
+ {
25
+ "type": "vcs",
26
+ "url": "https://github.com/woocommerce/action-scheduler-job-framework"
27
+ }
28
+ ],
29
+ "require": {
30
+ "woocommerce/action-scheduler-job-framework": "1.0.0"
31
+ }
32
+ }
33
+ ```
34
+
35
+ 2. Then run `composer update`
36
 
37
  ## Chained Jobs
38
 
vendor/woocommerce/action-scheduler-job-framework/src/AbstractChainedJob.php CHANGED
@@ -138,11 +138,7 @@ abstract class AbstractChainedJob extends AbstractJob implements ChainedJobInter
138
  // No more items to process so end the job chain
139
  $this->queue_end( $args );
140
  } else {
141
- $items = $this->filter_items_before_processing( $items );
142
-
143
- foreach ( $items as $item ) {
144
- $this->process_item( $item, $args );
145
- }
146
 
147
  // If there were items, queue another batch.
148
  $this->queue_batch( $batch_number + 1, $args );
@@ -150,16 +146,19 @@ abstract class AbstractChainedJob extends AbstractJob implements ChainedJobInter
150
  }
151
 
152
  /**
153
- * Filter-like function that runs before items in a batch are processed.
154
  *
155
- * This could be useful if the results from the initial batch query need to be filtered in an additional query.
156
  *
157
- * @param array $items
 
158
  *
159
- * @return array
160
  */
161
- protected function filter_items_before_processing( array $items ): array {
162
- return $items;
 
 
163
  }
164
 
165
  /**
138
  // No more items to process so end the job chain
139
  $this->queue_end( $args );
140
  } else {
141
+ $this->process_items( $items, $args );
 
 
 
 
142
 
143
  // If there were items, queue another batch.
144
  $this->queue_batch( $batch_number + 1, $args );
146
  }
147
 
148
  /**
149
+ * Processes a batch of items.
150
  *
151
+ * @since 1.1.0
152
  *
153
+ * @param array $items The items of the current batch.
154
+ * @param array $args The args for the job.
155
  *
156
+ * @throws Exception On error. The failure will be logged by Action Scheduler and the job chain will stop.
157
  */
158
+ protected function process_items( array $items, array $args ) {
159
+ foreach ( $items as $item ) {
160
+ $this->process_item( $item, $args );
161
+ }
162
  }
163
 
164
  /**