diff --git a/assets/js/editor-components/feedback-prompt/index.tsx b/assets/js/editor-components/feedback-prompt/index.tsx index 939941eb874..c9400e25a36 100644 --- a/assets/js/editor-components/feedback-prompt/index.tsx +++ b/assets/js/editor-components/feedback-prompt/index.tsx @@ -12,7 +12,7 @@ import './style.scss'; interface FeedbackPromptProps { text: string; title?: string; - url?: string; + url: string; } /** * Component to render a Feedback prompt in the sidebar. @@ -25,7 +25,7 @@ interface FeedbackPromptProps { const FeedbackPrompt = ( { text, title = __( 'Feedback?', 'woo-gutenberg-products-block' ), - url = 'https://ideas.woocommerce.com/forums/133476-woocommerce?category_id=384565', + url, }: FeedbackPromptProps ) => { // By returning false we ensure that this component is not entered into the InspectorControls // (which is a slot fill), children array on first render, on the second render when the state diff --git a/assets/js/extensions/jetpack/woocommerce-analytics/constants.ts b/assets/js/extensions/jetpack/woocommerce-analytics/constants.ts deleted file mode 100644 index 88fa51b8a6a..00000000000 --- a/assets/js/extensions/jetpack/woocommerce-analytics/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const namespace = 'jetpack-woocommerce-analytics'; -export const actionPrefix = 'experimental__woocommerce_blocks'; diff --git a/assets/js/extensions/jetpack/woocommerce-analytics/index.ts b/assets/js/extensions/jetpack/woocommerce-analytics/index.ts deleted file mode 100644 index db84f07861c..00000000000 --- a/assets/js/extensions/jetpack/woocommerce-analytics/index.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * External dependencies - */ -import { Cart, isObject, objectHasProp } from '@woocommerce/types'; -import { select } from '@wordpress/data'; -import { getSetting } from '@woocommerce/settings'; - -/** - * Internal dependencies - */ -import { STORE_KEY as CART_STORE_KEY } from '../../../data/cart/constants'; - -declare global { - interface Window { - // eslint-disable-next-line @typescript-eslint/naming-convention - _wca: { - // eslint-disable-next-line @typescript-eslint/ban-types - push: ( properties: Record< string, unknown > ) => void; - }; - } -} - -interface StorePageDetails { - id: number; - title: string; - permalink: string; -} - -interface StorePages { - checkout: StorePageDetails; - cart: StorePageDetails; - myaccount: StorePageDetails; - privacy: StorePageDetails; - shop: StorePageDetails; - terms: StorePageDetails; -} - -/** - * Check if the _wca object is valid and has a push property that is a function. - * - * @param wca {unknown} Object that might be a Jetpack WooCommerce Analytics object. - */ -// eslint-disable-next-line @typescript-eslint/ban-types -const isValidWCA = ( - wca: unknown -): wca is { push: ( properties: Record< string, unknown > ) => void } => { - if ( ! isObject( wca ) || ! objectHasProp( wca, 'push' ) ) { - return false; - } - return typeof wca.push === 'function'; -}; - -const registerActions = (): void => { - if ( ! isValidWCA( window._wca ) ) { - // eslint-disable-next-line no-useless-return - return; - } - - // We will register actions here in a later PR. -}; - -document.addEventListener( 'DOMContentLoaded', () => { - registerActions(); -} ); - -export const cleanUrl = ( link: string ) => { - const url = link.split( '?' )[ 0 ]; - if ( url.charAt( url.length - 1 ) !== '/' ) { - return url + '/'; - } - return url; -}; - -const maybeTrackCheckoutPageView = ( cart: Cart ) => { - const storePages = getSetting< StorePages >( 'storePages', {} ); - if ( ! objectHasProp( storePages, 'checkout' ) ) { - return; - } - - if ( - cleanUrl( storePages?.checkout?.permalink ) !== - cleanUrl( window.location.href ) - ) { - return; - } - - if ( ! isValidWCA( window._wca ) ) { - return; - } - - const checkoutData = getSetting< Record< string, unknown > >( - 'wc-blocks-jetpack-woocommerce-analytics_cart_checkout_info', - {} - ); - - window._wca.push( { - _en: 'woocommerceanalytics_checkout_view', - products_count: cart.items.length, - order_value: cart.totals.total_price, - products: JSON.stringify( - cart.items.map( ( item ) => { - return { - pp: item.totals.line_total, - pq: item.quantity, - pi: item.id, - pn: item.name, - }; - } ) - ), - ...checkoutData, - } ); -}; - -const maybeTrackCartPageView = ( cart: Cart ) => { - const storePages = getSetting< StorePages >( 'storePages', {} ); - if ( ! objectHasProp( storePages, 'cart' ) ) { - return; - } - - if ( - cleanUrl( storePages?.cart?.permalink ) !== - cleanUrl( window.location.href ) - ) { - return; - } - - if ( ! isValidWCA( window._wca ) ) { - return; - } - - const checkoutData = getSetting< Record< string, unknown > >( - 'wc-blocks-jetpack-woocommerce-analytics_cart_checkout_info', - {} - ); - - window._wca.push( { - _en: 'woocommerceanalytics_cart_view', - products_count: cart.items.length, - order_value: cart.totals.total_price, - products: JSON.stringify( - cart.items.map( ( item ) => { - return { - pp: item.totals.line_total, - pq: item.quantity, - pi: item.id, - pn: item.name, - pt: item.type, - }; - } ) - ), - ...checkoutData, - } ); -}; - -const maybeTrackOrderReceivedPageView = () => { - const orderReceivedProps = getSetting( - 'wc-blocks-jetpack-woocommerce-analytics_order_received_properties', - false - ); - - if ( ! orderReceivedProps || ! isValidWCA( window._wca ) ) { - return; - } - - window._wca.push( { - _en: 'woocommerceanalytics_order_confirmation_view', - ...orderReceivedProps, - } ); -}; - -document.addEventListener( 'DOMContentLoaded', () => { - const store = select( CART_STORE_KEY ); - - // If the store doesn't load, we aren't on a cart/checkout block page, so maybe it's order received page. - if ( ! store ) { - maybeTrackOrderReceivedPageView(); - return; - } - - const hasCartLoaded = store.hasFinishedResolution( 'getCartTotals' ); - if ( hasCartLoaded ) { - maybeTrackCartPageView( store.getCartData() ); - maybeTrackCheckoutPageView( store.getCartData() ); - } -} ); diff --git a/assets/js/extensions/jetpack/woocommerce-analytics/test/index.ts b/assets/js/extensions/jetpack/woocommerce-analytics/test/index.ts deleted file mode 100644 index 4f4048c95b6..00000000000 --- a/assets/js/extensions/jetpack/woocommerce-analytics/test/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Internal dependencies - */ -import { cleanUrl } from '../index'; - -describe( 'WooCommerce Analytics', () => { - describe( 'cleanUrl', () => { - it( 'returns a clean URL with a trailing slash', () => { - expect( cleanUrl( 'https://test.com?test=1' ) ).toEqual( - 'https://test.com/' - ); - expect( cleanUrl( '' ) ).toEqual( '/' ); - expect( cleanUrl( 'https://test.com/' ) ).toEqual( - 'https://test.com/' - ); - expect( cleanUrl( 'https://test.com' ) ).toEqual( - 'https://test.com/' - ); - } ); - } ); -} ); diff --git a/bin/webpack-entries.js b/bin/webpack-entries.js index 75ca5defe87..8d520e94975 100644 --- a/bin/webpack-entries.js +++ b/bin/webpack-entries.js @@ -240,8 +240,6 @@ const entries = { './assets/js/extensions/google-analytics/index.ts', 'wc-shipping-method-pickup-location': './assets/js/extensions/shipping-methods/pickup-location/index.js', - 'wc-blocks-jetpack-woocommerce-analytics': - './assets/js/extensions/jetpack/woocommerce-analytics/index.ts', }, editor: { 'wc-blocks-classic-template-revert-button': diff --git a/docs/third-party-developers/extensibility/rest-api/extend-rest-api-add-custom-fields.md b/docs/third-party-developers/extensibility/rest-api/extend-rest-api-add-custom-fields.md new file mode 100644 index 00000000000..58c548fd280 --- /dev/null +++ b/docs/third-party-developers/extensibility/rest-api/extend-rest-api-add-custom-fields.md @@ -0,0 +1,285 @@ +# Add a new inner block containing a custom field to the WooCommerce Checkout Block + +This document describes how a developer can insert an input field into the Checkout block and have its value passed to the Store API so it's available when processing the checkout. + +## Overview + +Developers can extend the Checkout block to add new inner blocks and process additional data through the checkout POST request. This involves leveraging the extensibility interfaces provided by Gutenberg and WooCommerce Blocks. This is demonstrated in more detail in our tutorial: [Tutorial: Extending the WooCommerce Checkout Block +](https://developer.woocommerce.com/2023/08/07/extending-the-woocommerce-checkout-block-to-add-custom-shipping-options/). + +## Prerequisites + +- Basic understanding of React and the Gutenberg block editor. +- Familiarity with WooCommerce Blocks' extensibility interfaces and the Store API. + +## Step-by-Step Guide + +### 1. Set Up Your Development Environment + +Ensure you have the following files in your project: + +- `index.js`: Entry point for Webpack, imports, and registers the block type. +- `edit.js`: Handles the rendering of the block in the editor interface. +- `block.json`: Provides metadata and configurations for the block. +- `block.js`: Manages the block's state and user interactions. +- `frontend.js`: Registers the checkout block component for the frontend. + +Refer to [this tutorial](https://developer.woocommerce.com/2023/08/07/extending-the-woocommerce-checkout-block-to-add-custom-shipping-options/) for an example of adding a custom shipping option to the checkout block. + +### 2. Add a new field block to the Checkout Block + +To add a field block to the Checkout Block you will need to add the following entries to the `block.json` file of your block: + +```json +"parent": [ "woocommerce/checkout-shipping-methods-block" ], +"attributes": { + "lock": { + "type": "object", + "default": { + "remove": true, + "move": true + } + } +} +``` + +- The [lock attribute](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-templates/#individual-block-locking) is an object that controls whether the block can be removed or moved. By default, the lock attribute is set to allow the block to be removed and moved. However, by modifying the lock attribute, you can “force” the block to be non-removable. For example, you can set both remove and move properties to false in order to prevent the block from being removed or moved. +- The [parent attribute](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#parent) specifies the parent block that this block should be nested within it. It determines where the block will render. In our example, the block is a child of the `woocommerce/checkout-shipping-methods-block`. This means that your block will be rendered within the `woocommerce/checkout-shipping-methods-block`. If the shipping methods block is not required, your block will not be rendered. + +### 3. Setting custom checkout data + +We can set the added field data to send it to the `wc/store/checkout` endpoint when processing orders using the function `setExtensionData`: + +```JavaScript +setExtensionData( + 'namespace-of-your-block', + 'key-of-your-data', + value +); +``` + +#### Parameters + +- namespace `string` - The namespace of your block. +- key `string` - The key of your data. +- value `any` - The value of your data. + +#### How it works + + 1. `setExtensionData` is passed to inner blocks via props. + 2. It updates the `extensionData` key of the `wc/store/checkout` data store. + 3. This key is passed as part of the request body when POSTing to the checkout endpoint. + +#### Code Example + +```JavaScript +// block.js +export const Block = ( { checkoutExtensionData, extensions } ) => { +/** + * setExtensionData will update the wc/store/checkout data store with the values supplied. It + * can be used to pass data from the client to the server when submitting the checkout form. + */ + const { setExtensionData } = checkoutExtensionData; +} + +// ... Some code here + +useEffect( () => { +/** + * This code should use `setExtensionData` to update the `key-of-your-data` key + * in the `namespace-of-your-block` namespace of the checkout data store. +*/ +setExtensionData( + 'namespace-of-your-block', + 'key-of-your-data', + value +); +}, [ setExtensionData, value ] ); +``` + +#### Screenshots + +Screenshots of Redux Dev tool showing the data store before and after the setExtensionData call: + +| Before | After | +| ------ | ----- | +| image | image | + +### 4. Processing the Checkout POST Request + +To process the added field data, we'll need extend the Store API to tell it to expect additional data. See more details in the [Exposing your data in the Store API](https://github.com/woocommerce/woocommerce-blocks/blob/trunk/docs/third-party-developers/extensibility/rest-api/extend-rest-api-add-data.md) + +#### Code Example + +We will use the following PHP files in our example: + +- The `custom-inner-block-blocks-integration.php` file: Enqueue scripts, styles, and data on the frontend when the Checkout blocks is being used. See more details in the [IntegrationInterface](https://github.com/woocommerce/woocommerce-blocks/blob/trunk/docs/third-party-developers/extensibility/checkout-block/integration-interface.md) documentation. + +```php +use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface; + +/** + * Class for integrating with WooCommerce Blocks + */ +class Custom_Inner_Block_Blocks_Integration implements IntegrationInterface { + + /** + * The name of the integration. + * + * @return string + */ + public function get_name() { + return 'new-field-block'; + } + + /** + * When called invokes any initialization/setup for the integration. + */ + public function initialize() { + // ... Some code here: (e.g. init functions that registers scripts and styles, and other instructions) + } + + // ... Other functions here +} +``` + +- The `custom-inner-block-extend-store-endpoint.php` file: extends the [Store API](https://github.com/woocommerce/woocommerce-blocks/tree/trunk/src/StoreApi) and adds hooks to save and display your new field block instructions. This doesn't save the data from the custom block anywhere by default, but you can add your own logic to save the data to the database. + +```php +use Automattic\WooCommerce\Blocks\Package; +use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema; +use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CheckoutSchema; + +/** + * Your New Field Block Extend Store API. + */ +class Custom_Inner_Block_Extend_Store_Endpoint { + /** + * Stores Rest Extending instance. + * + * @var ExtendRestApi + */ + private static $extend; + + /** + * Plugin Identifier, unique to each plugin. + * + * @var string + */ + const IDENTIFIER = 'new-field-block'; + + /** + * Bootstraps the class and hooks required data. + * + */ + public static function init() { + self::$extend = Automattic\WooCommerce\StoreApi\StoreApi::container()->get( Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class ); + self::extend_store(); + } + + /** + * Registers the actual data into each endpoint. + */ + public static function extend_store() { + if ( is_callable( [ self::$extend, 'register_endpoint_data' ] ) ) { + self::$extend->register_endpoint_data( + [ + 'endpoint' => CheckoutSchema::IDENTIFIER, + 'namespace' => self::IDENTIFIER, + 'schema_callback' => [ 'Custom_Inner_Block_Extend_Store_Endpoint', 'extend_checkout_schema' ], + 'schema_type' => ARRAY_A, + ] + ); + } + } + + /** + * Register the new field block schema into the Checkout endpoint. + * + * @return array Registered schema. + * + */ + public static function extend_checkout_schema() { + return [ + 'Value_1' => [ + 'description' => 'A description of the field', + 'type' => 'string', // ... type of the field, this should be a string + 'context' => [ 'view', 'edit' ], // ... context of the field, this should be an array containing 'view' and 'edit' + 'readonly' => true, // ... whether the field is readonly or not, this should be a boolean + 'optional' => true, // ... whether the field is optional or not, this should be a boolean + ], + // ... other values + ]; + } +} +``` + +- The `new-field-block.php` file: the main plugin file that loads the `custom-inner-block-blocks-integration.php` and `custom-inner-block-extend-store-endpoint.php` files. + +```php +register( new Custom_Inner_Block_Blocks_Integration() ); + } + ); + } +); + +// ... Some code here +``` + +Here is an example from our [tutorial](https://developer.woocommerce.com/2023/08/07/extending-the-woocommerce-checkout-block-to-add-custom-shipping-options/) of how to get this custom field's data while processing the checkout. This example is from the `shipping-workshop-blocks-integration.php` file. The complete code can be found in this [GitHub repository](https://github.com/woocommerce/wceu23-shipping-workshop-final/blob/main/shipping-workshop-blocks-integration.php#L42-L83). + +```php +private function save_shipping_instructions() { + /** + * We write a hook, using the `woocommerce_store_api_checkout_update_order_from_request` action + * that will update the order metadata with the shipping-workshop alternate shipping instruction. + * + * The documentation for this hook is at: https://github.com/woocommerce/woocommerce-blocks/blob/b73fbcacb68cabfafd7c3e7557cf962483451dc1/docs/third-party-developers/extensibility/hooks/actions.md#woocommerce_store_api_checkout_update_order_from_request + */ + add_action( + 'woocommerce_store_api_checkout_update_order_from_request', + function( \WC_Order $order, \WP_REST_Request $request ) { + $shipping_workshop_request_data = $request['extensions'][$this->get_name()]; + $alternate_shipping_instruction = $shipping_workshop_request_data['alternateShippingInstruction']; + $other_shipping_value = $shipping_workshop_request_data['otherShippingValue']; + $order->update_meta_data( 'shipping_workshop_alternate_shipping_instruction', $alternate_shipping_instruction ); + $order->save(); + }, + 10, + 2 + ); +} +``` + +## Conclusion + +By following the steps above, you can add and process new field blocks in the WooCommerce checkout block. For complete implementation and additional examples, refer to the provided [tutorial](https://developer.woocommerce.com/2023/08/07/extending-the-woocommerce-checkout-block-to-add-custom-shipping-options/) and the corresponding [GitHub repository](https://github.com). diff --git a/src/Domain/Bootstrap.php b/src/Domain/Bootstrap.php index 58b1f13ac0e..4b40fd32ee6 100644 --- a/src/Domain/Bootstrap.php +++ b/src/Domain/Bootstrap.php @@ -8,7 +8,6 @@ use Automattic\WooCommerce\Blocks\BlockTemplatesController; use Automattic\WooCommerce\Blocks\BlockTypesController; use Automattic\WooCommerce\Blocks\Domain\Services\CreateAccount; -use Automattic\WooCommerce\Blocks\Domain\Services\JetpackWooCommerceAnalytics; use Automattic\WooCommerce\Blocks\Domain\Services\Notices; use Automattic\WooCommerce\Blocks\Domain\Services\DraftOrders; use Automattic\WooCommerce\Blocks\Domain\Services\FeatureGating; @@ -132,7 +131,6 @@ function() { $this->container->get( CreateAccount::class )->init(); $this->container->get( ShippingController::class )->init(); $this->container->get( TasksController::class )->init(); - $this->container->get( JetpackWooCommerceAnalytics::class )->init(); // Load assets in admin and on the frontend. if ( ! $is_rest ) { @@ -366,15 +364,6 @@ function( Container $container ) { return new GoogleAnalytics( $asset_api ); } ); - $this->container->register( - JetpackWooCommerceAnalytics::class, - function( Container $container ) { - $asset_api = $container->get( AssetApi::class ); - $asset_data_registry = $container->get( AssetDataRegistry::class ); - $block_templates_controller = $container->get( BlockTemplatesController::class ); - return new JetpackWooCommerceAnalytics( $asset_api, $asset_data_registry, $block_templates_controller ); - } - ); $this->container->register( Notices::class, function( Container $container ) { diff --git a/src/Domain/Services/JetpackWooCommerceAnalytics.php b/src/Domain/Services/JetpackWooCommerceAnalytics.php deleted file mode 100644 index 4b0e320a3db..00000000000 --- a/src/Domain/Services/JetpackWooCommerceAnalytics.php +++ /dev/null @@ -1,400 +0,0 @@ -asset_api = $asset_api; - $this->asset_data_registry = $asset_data_registry; - $this->block_templates_controller = $block_templates_controller; - } - - /** - * Hook into WP. - */ - public function init() { - add_action( 'init', array( $this, 'check_compatibility' ) ); - add_action( 'rest_pre_serve_request', array( $this, 'track_local_pickup' ), 10, 4 ); - - $is_rest = wc()->is_rest_api_request(); - if ( ! $is_rest ) { - add_action( 'init', array( $this, 'init_if_compatible' ), 20 ); - } - } - - /** - * Gets product categories or varation attributes as a formatted concatenated string - * - * @param object $product WC_Product. - * @return string - */ - public function get_product_categories_concatenated( $product ) { - - if ( ! $product instanceof WC_Product ) { - return ''; - } - - $variation_data = $product->is_type( 'variation' ) ? wc_get_product_variation_attributes( $product->get_id() ) : ''; - if ( is_array( $variation_data ) && ! empty( $variation_data ) ) { - $line = wc_get_formatted_variation( $variation_data, true ); - } else { - $out = array(); - $categories = get_the_terms( $product->get_id(), 'product_cat' ); - if ( $categories ) { - foreach ( $categories as $category ) { - $out[] = $category->name; - } - } - $line = implode( '/', $out ); - } - return $line; - } - - /** - * Gather relevant product information. Taken from Jetpack WooCommerce Analytics Module. - * - * @param \WC_Product $product product. - * @return array - */ - public function get_product_details( $product ) { - return array( - 'id' => $product->get_id(), - 'name' => $product->get_title(), - 'category' => $this->get_product_categories_concatenated( $product ), - 'price' => $product->get_price(), - 'type' => $product->get_type(), - ); - } - - /** - * Save the order received page view event properties to the asset data registry. The front end will consume these - * later. - * - * @param int $order_id The order ID. - * - * @return void - */ - public function output_order_received_page_view_properties( $order_id ) { - $order = wc_get_order( $order_id ); - $product_data = wp_json_encode( - array_map( - function( $item ) { - $product = wc_get_product( $item->get_product_id() ); - $product_details = $this->get_product_details( $product ); - return array( - 'pi' => $product_details['id'], - 'pq' => $item->get_quantity(), - 'pt' => $product_details['type'], - 'pn' => $product_details['name'], - 'pc' => $product_details['category'], - 'pp' => $product_details['price'], - ); - }, - $order->get_items() - ) - ); - - $properties = $this->get_cart_checkout_info(); - $properties['products'] = $product_data; - $this->asset_data_registry->add( 'wc-blocks-jetpack-woocommerce-analytics_order_received_properties', $properties ); - } - - /** - * Check compatibility with Jetpack WooCommerce Analytics. - * - * @return void - */ - public function check_compatibility() { - // Require Jetpack WooCommerce Analytics to be available. - $this->is_compatible = class_exists( 'Jetpack_WooCommerce_Analytics_Universal', false ) && - class_exists( 'Jetpack_WooCommerce_Analytics', false ) && - \Jetpack_WooCommerce_Analytics::should_track_store(); - } - - /** - * Initialize if compatible. - */ - public function init_if_compatible() { - if ( ! $this->is_compatible ) { - return; - } - $this->register_assets(); - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'register_script_data' ) ); - add_action( 'woocommerce_thankyou', array( $this, 'output_order_received_page_view_properties' ) ); - } - - /** - * Register scripts. - */ - public function register_assets() { - if ( ! $this->is_compatible ) { - return; - } - $asset_file = include Package::get_path() . 'build/wc-blocks-jetpack-woocommerce-analytics.asset.php'; - if ( is_array( $asset_file['dependencies'] ) ) { - $this->asset_api->register_script( 'wc-blocks-jetpack-woocommerce-analytics', 'build/wc-blocks-jetpack-woocommerce-analytics.js', array_merge( array( 'wc-blocks' ), $asset_file['dependencies'] ) ); - } - } - - /** - * Enqueue the Google Tag Manager script if prerequisites are met. - */ - public function enqueue_scripts() { - // Additional check here before finally enqueueing the scripts. Done late here because checking these earlier fails. - if ( ! is_cart() && ! is_checkout() ) { - return; - } - wp_enqueue_script( 'wc-blocks-jetpack-woocommerce-analytics' ); - } - - /** - * Enqueue the Google Tag Manager script if prerequisites are met. - */ - public function register_script_data() { - $this->asset_data_registry->add( 'wc-blocks-jetpack-woocommerce-analytics_cart_checkout_info', $this->get_cart_checkout_info() ); - } - - /** - * Get the current user id - * - * @return int - */ - private function get_user_id() { - if ( is_user_logged_in() ) { - $blogid = \Jetpack::get_option( 'id' ); - $userid = get_current_user_id(); - return $blogid . ':' . $userid; - } - return 'null'; - } - - /** - * Default event properties which should be included with all events. - * - * @return array Array of standard event props. - */ - public function get_common_properties() { - if ( ! class_exists( 'Jetpack' ) || ! is_callable( array( 'Jetpack', 'get_option' ) ) ) { - return array(); - } - return array( - 'blog_id' => \Jetpack::get_option( 'id' ), - 'ui' => $this->get_user_id(), - 'url' => home_url(), - 'woo_version' => WC()->version, - ); - } - - /** - * Get info about the cart & checkout pages, in particular whether the store is using shortcodes or Gutenberg blocks. - * This info is cached in a transient. - * - * @return array - */ - public function get_cart_checkout_info() { - $transient_name = 'woocommerce_blocks_jetpack_woocommerce_analytics_cart_checkout_info_cache'; - - $info = get_transient( $transient_name ); - - // Return cached data early to prevent additional processing, the transient lasts for 1 day. - if ( false !== $info ) { - return $info; - } - - $cart_template = null; - $checkout_template = null; - $cart_template_id = null; - $checkout_template_id = null; - $templates = $this->block_templates_controller->get_block_templates( array( 'cart', 'checkout', 'page-checkout', 'page-cart' ) ); - $guest_checkout = ucfirst( get_option( 'woocommerce_enable_guest_checkout', 'No' ) ); - $create_account = ucfirst( get_option( 'woocommerce_enable_signup_and_login_from_checkout', 'No' ) ); - - foreach ( $templates as $template ) { - if ( 'cart' === $template->slug || 'page-cart' === $template->slug ) { - $cart_template_id = ( $template->id ); - continue; - } - if ( 'checkout' === $template->slug || 'page-checkout' === $template->slug ) { - $checkout_template_id = ( $template->id ); - } - } - - // Get the template and its contents from the IDs we found above. - if ( function_exists( 'get_block_template' ) ) { - $cart_template = get_block_template( $cart_template_id ); - $checkout_template = get_block_template( $checkout_template_id ); - } - - if ( function_exists( 'gutenberg_get_block_template' ) ) { - $cart_template = get_block_template( $cart_template_id ); - $checkout_template = get_block_template( $checkout_template_id ); - } - - // Something failed with the template retrieval, return early with 0 values rather than let a warning appear. - if ( ! $cart_template || ! $checkout_template ) { - return array( - 'cart_page_contains_cart_block' => 0, - 'cart_page_contains_cart_shortcode' => 0, - 'checkout_page_contains_checkout_block' => 0, - 'checkout_page_contains_checkout_shortcode' => 0, - ); - } - - // Update the info transient with data we got from the templates, if the site isn't using WC Blocks we - // won't be doing this so no concern about overwriting. - // Sites that load this code will be loading it on a page using the relevant block, but we still need to check - // the other page to see if it's using the block or shortcode. - $info = array( - 'cart_page_contains_cart_block' => str_contains( $cart_template->content, '