@automattic/newspack-blocks 4.12.3 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/carousel/view.asset.php +1 -1
  3. package/dist/carousel/view.js +3 -3
  4. package/dist/editor.asset.php +1 -1
  5. package/dist/editor.js +3 -3
  6. package/dist/modal.asset.php +1 -1
  7. package/dist/modal.js +1 -1
  8. package/dist/modalCheckout-rtl.css +1 -1
  9. package/dist/modalCheckout.asset.php +1 -1
  10. package/dist/modalCheckout.css +1 -1
  11. package/dist/modalCheckout.js +1 -1
  12. package/includes/class-modal-checkout.php +32 -106
  13. package/includes/class-newspack-blocks.php +4 -0
  14. package/includes/modal-checkout/class-checkout-data.php +287 -0
  15. package/includes/tracking/class-data-events.php +5 -140
  16. package/newspack-blocks.php +3 -2
  17. package/package.json +5 -5
  18. package/src/blocks/checkout-button/view.php +8 -42
  19. package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-base.php +2 -2
  20. package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-frequency-based.php +4 -28
  21. package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-tiers-based.php +5 -17
  22. package/src/modal-checkout/analytics/ga4/checkout-attempt.js +3 -2
  23. package/src/modal-checkout/analytics/ga4/checkout-success.js +3 -3
  24. package/src/modal-checkout/analytics/ga4/dismissed.js +3 -3
  25. package/src/modal-checkout/analytics/ga4/loaded.js +3 -3
  26. package/src/modal-checkout/analytics/ga4/pagination.js +3 -2
  27. package/src/modal-checkout/analytics/ga4/utils/index.js +31 -17
  28. package/src/modal-checkout/analytics/index.js +1 -1
  29. package/src/modal-checkout/checkout.scss +2 -1
  30. package/src/modal-checkout/index.js +1 -1
  31. package/src/modal-checkout/modal.js +232 -253
  32. package/src/modal-checkout/templates/thankyou.php +3 -6
  33. package/src/modal-checkout/utils.js +126 -0
  34. package/vendor/autoload.php +1 -1
  35. package/vendor/composer/autoload_real.php +4 -4
  36. package/vendor/composer/autoload_static.php +2 -2
  37. package/vendor/composer/installed.php +2 -2
@@ -7,7 +7,7 @@
7
7
 
8
8
  namespace Newspack_Blocks;
9
9
 
10
- use Newspack_Blocks\Tracking\Data_Events;
10
+ use Newspack_Blocks\Modal_Checkout\Checkout_Data;
11
11
 
12
12
  defined( 'ABSPATH' ) || exit;
13
13
 
@@ -710,8 +710,6 @@ final class Modal_Checkout {
710
710
  $variation_name = wc_get_formatted_variation( $variation, true );
711
711
  $price = $variation->get_price();
712
712
  $price_html = $variation->get_price_html();
713
- $frequency = '';
714
- $product_type = Data_Events::get_product_type( $product_id );
715
713
 
716
714
  // Use suggested price if NYP is active and set for variation.
717
715
  if ( \Newspack_Blocks::can_use_name_your_price() && \WC_Name_Your_Price_Helpers::is_nyp( $variation_id ) ) {
@@ -722,29 +720,6 @@ final class Modal_Checkout {
722
720
  }
723
721
  }
724
722
 
725
- if ( class_exists( '\WC_Subscriptions_Product' ) && \WC_Subscriptions_Product::is_subscription( $variation ) ) {
726
- $frequency = \WC_Subscriptions_Product::get_period( $variation );
727
- }
728
-
729
- $name = sprintf(
730
- /* translators: 1: variable product name, 2: product variation name */
731
- __( '%1$s - %2$s', 'newspack-blocks' ),
732
- $product_name,
733
- $variation_name
734
- );
735
- $product_price_summary = self::get_summary_card_price_string( $name, $price, $frequency );
736
- $product_data = [
737
- 'amount' => $price,
738
- 'action_type' => 'checkout_button',
739
- 'currency' => function_exists( 'get_woocommerce_currency' ) ? \get_woocommerce_currency() : 'USD',
740
- 'product_price_summary' => $product_price_summary,
741
- 'product_id' => (string) $product_id,
742
- 'product_type' => $product_type,
743
- 'recurrence' => ! empty( $frequency ) ? $frequency : 'once',
744
- 'referrer' => substr( \get_permalink(), strlen( home_url() ) ), // TODO: Is this OK?
745
- 'variation_id' => (string) $variation_id,
746
- ];
747
-
748
723
  // Replace nyp price html for variations.
749
724
  if ( class_exists( '\WC_Name_Your_Price_Helpers' ) && \WC_Name_Your_Price_Helpers::is_nyp( $variation->get_id() ) ) {
750
725
  $price_html = str_replace( ':', '', $price_html );
@@ -757,7 +732,7 @@ final class Modal_Checkout {
757
732
  <span class="price"><?php echo $price_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
758
733
  </div>
759
734
  <div class="variation"><?php echo esc_html( $variation_name ); ?></div>
760
- <form data-product="<?php echo esc_attr( wp_json_encode( $product_data ) ); ?>">
735
+ <form data-checkout="<?php echo esc_attr( wp_json_encode( Checkout_Data::get_checkout_data( $variation ) ) ); ?>">
761
736
  <input type="hidden" name="newspack_checkout" value="1" />
762
737
  <button type="submit" class="<?php echo esc_attr( "{$class_prefix}__button {$class_prefix}__button--primary" ); ?> newspack-modal-checkout-variation-selection"><?php echo esc_html( self::get_modal_checkout_labels( 'checkout_confirm_variation' ) ); ?></button>
763
738
  </form>
@@ -1025,6 +1000,7 @@ final class Modal_Checkout {
1025
1000
  'newspack_class_prefix' => self::get_class_prefix(),
1026
1001
  'is_registration_required' => self::is_registration_required(),
1027
1002
  'has_unsupported_payment_gateway' => self::has_unsupported_payment_gateway(),
1003
+ 'checkout_url' => remove_query_arg( 'my_account_checkout', add_query_arg( 'modal_checkout', '1', wc_get_checkout_url() ) ),
1028
1004
  'labels' => [
1029
1005
  'auth_modal_title' => self::get_modal_checkout_labels( 'auth_modal_title' ),
1030
1006
  'checkout_modal_title' => self::get_modal_checkout_labels( 'checkout_modal_title' ),
@@ -1111,29 +1087,23 @@ final class Modal_Checkout {
1111
1087
  * Get after success button params.
1112
1088
  */
1113
1089
  private static function get_after_success_params() {
1114
- // Express checkout payment requests are separate requests, so they won't have the after_success attributes. We'll have to check the HTTP_REFERER instead.
1090
+ $request_params = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1115
1091
  if ( self::is_express_checkout() ) {
1092
+ $request_params = [];
1116
1093
  $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1117
1094
  if ( $referrer ) {
1118
1095
  $referrer_query = \wp_parse_url( $referrer, PHP_URL_QUERY );
1119
- \wp_parse_str( $referrer_query, $referrer_query_params );
1120
- return array_filter(
1121
- [
1122
- 'after_success_behavior' => isset( $referrer_query_params['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $referrer_query_params['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1123
- 'after_success_url' => isset( $referrer_query_params['after_success_url'] ) ? sanitize_url( wp_unslash( $referrer_query_params['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1124
- 'after_success_button_label' => isset( $referrer_query_params['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $referrer_query_params['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1125
- ]
1126
- );
1096
+ \wp_parse_str( $referrer_query, $request_params );
1127
1097
  }
1128
- } else {
1129
- return array_filter(
1130
- [
1131
- 'after_success_behavior' => isset( $_REQUEST['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1132
- 'after_success_url' => isset( $_REQUEST['after_success_url'] ) ? sanitize_url( wp_unslash( $_REQUEST['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1133
- 'after_success_button_label' => isset( $_REQUEST['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1134
- ]
1135
- );
1136
1098
  }
1099
+ return array_filter(
1100
+ [
1101
+ 'after_success_behavior' => isset( $request_params['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $request_params['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1102
+ 'after_success_url' => isset( $request_params['after_success_url'] ) ? sanitize_url( wp_unslash( $request_params['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1103
+ 'after_success_button_label' => isset( $request_params['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $request_params['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1104
+ 'action_type' => isset( $request_params['action_type'] ) ? sanitize_text_field( wp_unslash( $request_params['action_type'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1105
+ ]
1106
+ );
1137
1107
  }
1138
1108
 
1139
1109
  /**
@@ -1538,16 +1508,7 @@ final class Modal_Checkout {
1538
1508
  */
1539
1509
  public static function pass_url_param_on_redirect( $location ) {
1540
1510
  if ( self::is_modal_checkout() ) {
1541
- $params = [ 'modal_checkout' => 1 ];
1542
- $newspack_popup_id = filter_input( INPUT_GET, 'newspack_popup_id', FILTER_SANITIZE_NUMBER_INT );
1543
- $gate_post_id = filter_input( INPUT_GET, 'memberships_content_gate', FILTER_SANITIZE_NUMBER_INT );
1544
- if ( $newspack_popup_id ) {
1545
- $params['newspack_popup_id'] = $newspack_popup_id;
1546
- }
1547
- if ( $gate_post_id ) {
1548
- $params['memberships_content_gate'] = $gate_post_id;
1549
- }
1550
- $location = \add_query_arg( $params, $location );
1511
+ $location = \add_query_arg( [ 'modal_checkout' => 1 ], $location );
1551
1512
  }
1552
1513
  return $location;
1553
1514
  }
@@ -1707,29 +1668,28 @@ final class Modal_Checkout {
1707
1668
  if ( 1 !== $cart->get_cart_contents_count() ) {
1708
1669
  return;
1709
1670
  }
1671
+ $cart_item_key = array_key_first( $cart->get_cart() );
1672
+ $cart_item = $cart->get_cart_item( $cart_item_key );
1673
+ $product_id = $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
1710
1674
  $class_prefix = self::get_class_prefix();
1711
1675
  ?>
1712
1676
  <div class="<?php echo esc_attr( "order-details-summary {$class_prefix}__box {$class_prefix}__box--text-center" ); ?>">
1713
1677
  <?php
1714
1678
  // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce hooks.
1715
- foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) :
1716
- $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
1717
- if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_checkout_cart_item_visible', true, $cart_item, $cart_item_key ) ) :
1718
- // Create an array of order information to pass to GA4 via JavaScript.
1719
- $data_order_details = Data_Events::build_js_data_events( $_product->get_id(), $cart_item );
1720
- ?>
1721
- <p id="modal-checkout-product-details" data-order-details='<?php echo wp_json_encode( $data_order_details ); ?>'>
1722
- <strong>
1723
- <?php
1724
- echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ': '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1725
- echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1726
- ?>
1727
- <?php echo apply_filters( 'woocommerce_cart_item_subtotal', $cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1728
- </strong>
1729
- </p>
1730
- <?php
1731
- endif;
1732
- endforeach;
1679
+ $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
1680
+ if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_checkout_cart_item_visible', true, $cart_item, $cart_item_key ) ) :
1681
+ ?>
1682
+ <p id="modal-checkout-product-details" data-checkout='<?php echo wp_json_encode( Checkout_Data::get_checkout_data( $cart ) ); ?>'>
1683
+ <strong>
1684
+ <?php
1685
+ echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ': '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1686
+ echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1687
+ ?>
1688
+ <?php echo apply_filters( 'woocommerce_cart_item_subtotal', $cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
1689
+ </strong>
1690
+ </p>
1691
+ <?php
1692
+ endif;
1733
1693
  // phpcs:enable
1734
1694
  ?>
1735
1695
  </div>
@@ -2080,40 +2040,6 @@ final class Modal_Checkout {
2080
2040
  return self::$modal_checkout_labels[ $key ] ?? '';
2081
2041
  }
2082
2042
 
2083
- /**
2084
- * Get price string for the price summary card to render in auth flow.
2085
- *
2086
- * @param string $name The name.
2087
- * @param string $price The price. Optional. If not provided, the price string will contain 0.
2088
- * @param string $frequency The frequency. Optional. If not provided, the price will be treated as a one-time payment.
2089
- *
2090
- * @return string The price string.
2091
- */
2092
- public static function get_summary_card_price_string( $name, $price = '', $frequency = '' ) {
2093
- if ( ! $price ) {
2094
- $price = '0';
2095
- }
2096
-
2097
- if ( function_exists( 'wcs_price_string' ) && function_exists( 'wc_price' ) ) {
2098
- if ( $frequency && $frequency !== 'once' ) {
2099
- $price = wp_strip_all_tags(
2100
- wcs_price_string(
2101
- [
2102
- 'recurring_amount' => $price,
2103
- 'subscription_period' => $frequency,
2104
- 'use_per_slash' => true,
2105
- ]
2106
- )
2107
- );
2108
- } else {
2109
- $price = wp_strip_all_tags( wc_price( $price ) );
2110
- }
2111
- }
2112
-
2113
- // translators: 1 is the name of the item. 2 is the price of the item.
2114
- return sprintf( __( '%1$s: %2$s', 'newspack-blocks' ), $name, $price );
2115
- }
2116
-
2117
2043
  /**
2118
2044
  * Set the checkout registration flag to WC session.
2119
2045
  */
@@ -1504,6 +1504,10 @@ class Newspack_Blocks {
1504
1504
  $currency_symbol = function_exists( 'get_woocommerce_currency_symbol' ) ? \get_woocommerce_currency_symbol() : '&#36;';
1505
1505
  $wc_formatted_amount = '<span class="woocommerce-Price-amount amount"><bdi><span class="woocommerce-Price-currencySymbol">' . $currency_symbol . '</span>AMOUNT_PLACEHOLDER</bdi></span> FREQUENCY_PLACEHOLDER';
1506
1506
  } else {
1507
+ // If it's a float but with no decimal value, treat it as an int.
1508
+ if ( is_float( $amount ) && floor( $amount ) == $amount ) {
1509
+ $amount = (int) $amount;
1510
+ }
1507
1511
  // Format the amount with currency symbol and separators.
1508
1512
  $amount_string = \wc_price(
1509
1513
  $amount,
@@ -0,0 +1,287 @@
1
+ <?php
2
+ /**
3
+ * Newspack Blocks Modal Checkout Data.
4
+ *
5
+ * @package Newspack
6
+ */
7
+
8
+ namespace Newspack_Blocks\Modal_Checkout;
9
+
10
+ /**
11
+ * Checkout Data Class.
12
+ */
13
+ final class Checkout_Data {
14
+ /**
15
+ * Get price string for the price summary card to render in auth flow.
16
+ *
17
+ * @param string $name The name.
18
+ * @param string $price The price. Optional. If not provided, the price string will contain 0.
19
+ * @param string $frequency The frequency. Optional. If not provided, the price will be treated as a one-time payment.
20
+ *
21
+ * @return string The price string.
22
+ */
23
+ public static function get_price_summary( $name, $price = '', $frequency = '' ) {
24
+ if ( ! $price ) {
25
+ $price = '0';
26
+ }
27
+
28
+ if ( function_exists( 'wcs_price_string' ) && function_exists( 'wc_price' ) ) {
29
+ if ( $frequency && $frequency !== 'once' ) {
30
+ $price = wp_strip_all_tags(
31
+ wcs_price_string(
32
+ [
33
+ 'recurring_amount' => $price,
34
+ 'subscription_period' => $frequency,
35
+ 'use_per_slash' => true,
36
+ ]
37
+ )
38
+ );
39
+ } else {
40
+ $price = wp_strip_all_tags( wc_price( $price ) );
41
+ }
42
+ }
43
+
44
+ // translators: 1 is the name of the item. 2 is the price of the item.
45
+ return sprintf( __( '%1$s: %2$s', 'newspack-blocks' ), $name, $price );
46
+ }
47
+
48
+ /**
49
+ * Returns whether a product is a one time purchase, or recurring and when.
50
+ *
51
+ * @param string $product_id Product's ID.
52
+ */
53
+ public static function get_purchase_recurrence( $product_id ) {
54
+ $recurrence = get_post_meta( $product_id, '_subscription_period', true );
55
+ if ( empty( $recurrence ) ) {
56
+ $recurrence = 'once';
57
+ }
58
+ return $recurrence;
59
+ }
60
+
61
+ /**
62
+ * Returns whether a product ID is associated with a membership.
63
+ *
64
+ * @param string $product_id Product's ID.
65
+ */
66
+ public static function is_membership_product( $product_id ) {
67
+ if ( ! function_exists( 'wc_memberships_get_membership_plans' ) ) {
68
+ return false;
69
+ }
70
+ $membership_plans = wc_memberships_get_membership_plans();
71
+ $plans = [];
72
+
73
+ foreach ( $membership_plans as $plan ) {
74
+ $subscription_plan = new \WC_Memberships_Integration_Subscriptions_Membership_Plan( $plan->get_id() );
75
+ $required_products = $subscription_plan->get_subscription_product_ids();
76
+ if ( in_array( $product_id, $required_products ) ) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+
83
+
84
+ /**
85
+ * Returns the product type: product, subscription, donation, or membership.
86
+ *
87
+ * @param string $product_id Product's ID.
88
+ */
89
+ public static function get_product_type( $product_id ) {
90
+ $product_type = 'product';
91
+ $recurrence = self::get_purchase_recurrence( $product_id );
92
+
93
+ // Check if it's a subscription product.
94
+ if ( 'once' !== $recurrence ) {
95
+ $product_type = 'subscription';
96
+ }
97
+
98
+ // Check if it's a membership product.
99
+ if ( self::is_membership_product( $product_id ) ) {
100
+ $product_type = 'membership';
101
+ }
102
+
103
+ // Check if it's a donation product.
104
+ if ( method_exists( 'Newspack\Donations', 'is_donation_product' ) ) {
105
+ if ( \Newspack\Donations::is_donation_product( $product_id ) ) {
106
+ $product_type = 'donation';
107
+ }
108
+ }
109
+
110
+ return $product_type;
111
+ }
112
+
113
+ /**
114
+ * Returns the action type: checkout_button or donation.
115
+ *
116
+ * @param string $product_id Product's ID.
117
+ */
118
+ public static function get_action_type( $product_id ) {
119
+ $action_type = 'checkout_button';
120
+ // Check if it's a donation product, and update action_type, product_type.
121
+ if ( method_exists( 'Newspack\Donations', 'is_donation_product' ) ) {
122
+ if ( \Newspack\Donations::is_donation_product( $product_id ) ) {
123
+ $action_type = 'donation';
124
+ }
125
+ }
126
+ return $action_type;
127
+ }
128
+
129
+ /**
130
+ * Get donate product checkout data.
131
+ *
132
+ * @param string $frequency The frequency.
133
+ * @param int $amount The amount.
134
+ *
135
+ * @return array
136
+ */
137
+ public static function get_donation_checkout_data( $frequency, $amount = null ) {
138
+ if ( ! method_exists( 'Newspack\Donations', 'get_donation_product' ) ) {
139
+ return [];
140
+ }
141
+ $product_id = \Newspack\Donations::get_donation_product( $frequency );
142
+ if ( ! $product_id ) {
143
+ return [];
144
+ }
145
+ $product = wc_get_product( $product_id );
146
+ if ( ! $product ) {
147
+ return [];
148
+ }
149
+ $data = self::get_checkout_data( $product );
150
+ if ( $amount ) {
151
+ $data['amount'] = $amount;
152
+ }
153
+ return $data;
154
+ }
155
+
156
+ /**
157
+ * Returns checkout data given a product, product variation, cart or order object.
158
+ *
159
+ * @param \WC_Product|\WC_Product_Variation|\WC_Cart|\WC_Order $source Product, product variation, cart or order object.
160
+ *
161
+ * @return array
162
+ */
163
+ public static function get_checkout_data( $source ) {
164
+ $data = [];
165
+ if ( empty( $source ) ) {
166
+ return $data;
167
+ }
168
+
169
+ $cart_item = null;
170
+ $order = null;
171
+ $referrer = '';
172
+ $variation_id = null;
173
+
174
+ if ( $source instanceof \WC_Product_Variation ) {
175
+ $product_id = $source->get_parent_id();
176
+ $variation_id = $source->get_id();
177
+ $amount = $source->get_price();
178
+ } elseif ( $source instanceof \WC_Product ) {
179
+ $product_id = $source->get_id();
180
+ if ( $source->get_parent_id() ) {
181
+ $product_id = $source->get_parent_id();
182
+ $variation_id = $source->get_id();
183
+ }
184
+ $amount = $source->get_price();
185
+ } elseif ( $source instanceof \WC_Cart ) {
186
+ $cart_items = $source->get_cart();
187
+ $cart_item = reset( $cart_items ); // Use only the first item in the cart.
188
+ $product_id = $cart_item['product_id'];
189
+ $variation_id = $cart_item['variation_id'];
190
+ $amount = $cart_item['data']->get_price();
191
+ $referrer = $cart_item['referer'] ?? '';
192
+ } elseif ( $source instanceof \WC_Order ) {
193
+ $order = $source;
194
+ $order_items = $order->get_items();
195
+ $order_item = reset( $order_items ); // Use only the first item in the order.
196
+ $product_id = $order_item->get_product_id();
197
+ $variation_id = $order_item->get_variation_id();
198
+ $amount = $order_item->get_subtotal();
199
+ $referrer = $order->get_meta( '_newspack_referer' );
200
+ }
201
+
202
+ // If we have no referrer, set it to the current path.
203
+ if ( ! $referrer ) {
204
+ global $wp;
205
+ $referrer = $wp->request;
206
+ }
207
+
208
+ $product_type = self::get_product_type( $product_id );
209
+ $recurrence = self::get_purchase_recurrence( $product_id );
210
+
211
+ /**
212
+ * Price summary name.
213
+ */
214
+ if ( 'donation' === $product_type ) {
215
+ $name = __( 'Donate', 'newspack-blocks' );
216
+ } elseif ( $variation_id ) {
217
+ $variation = wc_get_product( $variation_id );
218
+ $name = $variation->get_name();
219
+ } else {
220
+ $product = wc_get_product( $product_id );
221
+ $name = $product->get_name();
222
+ }
223
+
224
+ $data = [
225
+ 'amount' => $amount,
226
+ 'action_type' => self::get_action_type( $product_id ),
227
+ 'currency' => function_exists( 'get_woocommerce_currency' ) ? \get_woocommerce_currency() : 'USD',
228
+ 'product_id' => strval( $product_id ? $product_id : '' ),
229
+ 'product_type' => $product_type,
230
+ 'price_summary' => self::get_price_summary( $name, $amount, $recurrence ),
231
+ 'summary_template' => self::get_price_summary( $name, '{{PRICE}}', $recurrence ),
232
+ 'referrer' => $referrer ? str_replace( home_url(), '', $referrer ) : '', // Keeps format consistent for Homepage with Donate and Checkout Button blocks.
233
+ 'recurrence' => $recurrence,
234
+ 'variation_id' => strval( $variation_id ? $variation_id : '' ),
235
+ ];
236
+
237
+ /**
238
+ * Order specific data.
239
+ */
240
+ if ( $order ) {
241
+ $data['order_id'] = $order->get_id();
242
+ if ( in_array( $product_type, [ 'subscription', 'membership' ], true ) ) {
243
+ $subscription_renewal = $order->get_meta( '_subscription_renewal' );
244
+ if ( $subscription_renewal ) {
245
+ $data['subscription_renewal'] = $subscription_renewal;
246
+ }
247
+ if ( function_exists( 'wcs_get_subscriptions_for_order' ) ) {
248
+ $subscriptions = wcs_get_subscriptions_for_order( $order );
249
+ if ( ! empty( $subscriptions ) ) {
250
+ $data['subscription_ids'] = array_values(
251
+ array_map(
252
+ function( $subscription ) {
253
+ return $subscription->get_id();
254
+ },
255
+ $subscriptions
256
+ )
257
+ );
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Gate and popup data.
265
+ */
266
+ $gate_post_id = null;
267
+ $newspack_popup_id = null;
268
+ if ( $order ) {
269
+ $gate_post_id = $order->get_meta( '_memberships_content_gate' );
270
+ $newspack_popup_id = $order->get_meta( '_newspack_popup_id' );
271
+ } elseif ( $cart_item ) {
272
+ $gate_post_id = $cart_item['memberships_content_gate'] ?? null;
273
+ $newspack_popup_id = $cart_item['newspack_popup_id'] ?? null;
274
+ } else {
275
+ $gate_post_id = filter_input( INPUT_GET, 'memberships_content_gate', FILTER_SANITIZE_NUMBER_INT );
276
+ $newspack_popup_id = filter_input( INPUT_GET, 'newspack_popup_id', FILTER_SANITIZE_NUMBER_INT );
277
+ }
278
+ if ( $gate_post_id ) {
279
+ $data['gate_post_id'] = $gate_post_id;
280
+ }
281
+ if ( $newspack_popup_id ) {
282
+ $data['newspack_popup_id'] = $newspack_popup_id;
283
+ }
284
+
285
+ return $data;
286
+ }
287
+ }
@@ -7,6 +7,8 @@
7
7
 
8
8
  namespace Newspack_Blocks\Tracking;
9
9
 
10
+ use Newspack_Blocks\Modal_Checkout\Checkout_Data;
11
+
10
12
  /**
11
13
  * Tracking Data Events Class.
12
14
  */
@@ -47,143 +49,6 @@ final class Data_Events {
47
49
  );
48
50
  }
49
51
 
50
- /**
51
- * Returns whether a product is a one time purchase, or recurring and when.
52
- *
53
- * @param string $product_id Product's ID.
54
- */
55
- public static function get_purchase_recurrence( $product_id ) {
56
- $recurrence = get_post_meta( $product_id, '_subscription_period', true );
57
- if ( empty( $recurrence ) ) {
58
- $recurrence = 'once';
59
- }
60
- return $recurrence;
61
- }
62
-
63
- /**
64
- * Returns whether a product ID is associated with a membership.
65
- *
66
- * @param string $product_id Product's ID.
67
- */
68
- public static function is_membership_product( $product_id ) {
69
- if ( ! function_exists( 'wc_memberships_get_membership_plans' ) ) {
70
- return false;
71
- }
72
- $membership_plans = wc_memberships_get_membership_plans();
73
- $plans = [];
74
-
75
- foreach ( $membership_plans as $plan ) {
76
- $subscription_plan = new \WC_Memberships_Integration_Subscriptions_Membership_Plan( $plan->get_id() );
77
- $required_products = $subscription_plan->get_subscription_product_ids();
78
- if ( in_array( $product_id, $required_products ) ) {
79
- return true;
80
- }
81
- }
82
- return false;
83
- }
84
-
85
-
86
- /**
87
- * Returns the product type: product, subscription, donation, or membership.
88
- * TODOGA4: move this check & related functions into a more central location, and update based on final decision for product types.
89
- *
90
- * @param string $product_id Product's ID.
91
- */
92
- public static function get_product_type( $product_id ) {
93
- $product_type = 'product';
94
- $recurrence = self::get_purchase_recurrence( $product_id );
95
-
96
- // Check if it's a subscription product.
97
- if ( 'once' !== $recurrence ) {
98
- $product_type = 'subscription';
99
- }
100
-
101
- // Check if it's a membership product.
102
- if ( self::is_membership_product( $product_id ) ) {
103
- $product_type = 'membership';
104
- }
105
-
106
- // Check if it's a donation product.
107
- if ( method_exists( 'Newspack\Donations', 'is_donation_product' ) ) {
108
- if ( \Newspack\Donations::is_donation_product( $product_id ) ) {
109
- $product_type = 'donation';
110
- }
111
- }
112
-
113
- return $product_type;
114
- }
115
-
116
- /**
117
- * Returns the action type: checkout_button or donation.
118
- *
119
- * @param string $product_id Product's ID.
120
- */
121
- public static function get_action_type( $product_id ) {
122
- $action_type = 'checkout_button';
123
- // Check if it's a donation product, and update action_type, product_type.
124
- if ( method_exists( 'Newspack\Donations', 'is_donation_product' ) ) {
125
- if ( \Newspack\Donations::is_donation_product( $product_id ) ) {
126
- $action_type = 'donation';
127
- }
128
- }
129
- return $action_type;
130
- }
131
-
132
- /**
133
- * Returns an array of product information.
134
- *
135
- * @param int $product_id Product's ID.
136
- * @param array $cart_item Cart item, if during checkout.
137
- * @param WC_Order $order Completed order, if checkout is completed.
138
- *
139
- * @return array
140
- */
141
- public static function build_js_data_events( $product_id, $cart_item = null, $order = null ) {
142
- $data_order_details = [];
143
- if ( empty( $product_id ) || ( empty( $cart_item ) && empty( $order ) ) ) {
144
- return $data_order_details;
145
- }
146
-
147
- // Reassign the IDs to make sure the product is the product and the variation is the variation.
148
- $product = \wc_get_product( $product_id );
149
- $product_parent_id = $product->get_parent_id();
150
- $variation_id = '';
151
- if ( '' !== $product_parent_id && 0 !== $product_parent_id ) {
152
- $variation_id = $product_id;
153
- $product_id = $product_parent_id;
154
- }
155
-
156
- $amount = 0;
157
- $referrer = '';
158
- if ( ! empty( $order ) ) {
159
- $amount = $order->get_total();
160
- $referrer = $order->get_meta( '_newspack_referer' );
161
- } elseif ( ! empty( $cart_item ) ) {
162
- $amount = $cart_item['data']->get_price();
163
- $referrer = $cart_item['referer'];
164
- }
165
-
166
- $data_order_details = [
167
- 'amount' => $amount,
168
- 'action_type' => self::get_action_type( $product_id ),
169
- 'currency' => function_exists( 'get_woocommerce_currency' ) ? \get_woocommerce_currency() : 'USD',
170
- 'product_id' => strval( $product_id ),
171
- 'product_type' => self::get_product_type( $product_id ),
172
- 'referrer' => str_replace( home_url(), '', $referrer ), // Keeps format consistent for Homepage with Donate and Checkout Button blocks.
173
- 'recurrence' => self::get_purchase_recurrence( $product_id ),
174
- 'variation_id' => strval( $variation_id ),
175
- ];
176
- $gate_post_id = ! empty( $order ) ? $order->get_meta( '_memberships_content_gate' ) : filter_input( INPUT_GET, 'memberships_content_gate', FILTER_SANITIZE_NUMBER_INT );
177
- if ( $gate_post_id ) {
178
- $data_order_details['gate_post_id'] = $gate_post_id;
179
- }
180
- $newspack_popup_id = ! empty( $order ) ? $order->get_meta( '_newspack_popup_id' ) : filter_input( INPUT_GET, 'newspack_popup_id', FILTER_SANITIZE_NUMBER_INT );
181
- if ( $newspack_popup_id ) {
182
- $data_order_details['newspack_popup_id'] = $newspack_popup_id;
183
- }
184
- return $data_order_details;
185
- }
186
-
187
52
  /**
188
53
  * Send data to GA4.
189
54
  *
@@ -207,10 +72,10 @@ final class Data_Events {
207
72
  $product_id = is_array( $data['platform_data']['product_id'] ) ? $data['platform_data']['product_id'][0] : $data['platform_data']['product_id'];
208
73
 
209
74
  $data['action'] = self::FORM_SUBMISSION_SUCCESS;
210
- $data['action_type'] = self::get_action_type( $product_id );
75
+ $data['action_type'] = Checkout_Data::get_action_type( $product_id );
211
76
  $data['product_id'] = $product_id;
212
- $data['product_type'] = self::get_product_type( $product_id );
213
- $data['recurrence'] = self::get_purchase_recurrence( $product_id );
77
+ $data['product_type'] = Checkout_Data::get_product_type( $product_id );
78
+ $data['recurrence'] = Checkout_Data::get_purchase_recurrence( $product_id );
214
79
 
215
80
  return $data;
216
81
  }