update plugins

This commit is contained in:
Tony Volpe
2024-06-17 14:42:23 -04:00
parent a00f379f7f
commit 38e314323c
9467 changed files with 2032414 additions and 0 deletions

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array('wp-hooks', 'wp-i18n'), 'version' => 'd0d5ee1d86289188dd81');

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,363 @@
*** Changelog ***
= 2.0.7 - 2024-05-14 =
* Dev - Add a manual workflow run for PHP unit tests.
* Fix - Apply discounts to the ecommerce event if available.
* Fix - Incorrect quantity value when adding product to the cart that exists in the cart.
* Tweak - WC 8.9 compatibility.
= 2.0.6 - 2024-04-09 =
* Tweak - WC 8.8 compatibility.
= 2.0.5 - 2024-04-04 =
* Tweak - Register `woocommerce-google-analytics-integration` script earlier, so other extensions can plug in.
* Update - Separate the site tag from the event tracking file and delay execution until DOMContentLoaded.
= 2.0.4 - 2024-03-26 =
* Dev - Add QIT workflow.
* Dev - E2E tests for the All Products block.
* Dev - Remove unused functions from code.
* Fix - Warning after plugin install using wp-cli.
* Tweak - Add WP 6.5 Require plugins header.
= 2.0.3 - 2024-03-13 =
* Tweak - WC 8.7 compatibility.
* Tweak - WP 6.5 compatibility.
= 2.0.2 - 2024-03-12 =
* Dev - Add E2E testing to track events across all page types.
* Fix - Add to cart & select content events for All Products Block.
* Fix - Get correct variation data when formatting product.
* Fix - Handle AJAX add to cart for simple products.
* Fix - Purchase price format.
* Fix - Track purchase transaction ID.
* Fix - Typo with undefined variable.
= 2.0.1 - 2024-03-07 =
* Fix - Adding variable product attributes.
* Fix - Resolve Google Analytics not loading if gtag already exists on the window object.
* Tweak - Update Contributors list.
* Tweak - Update assets and readme for 2.0.
= 2.0.0 - 2024-03-05 =
* Add - Basic consent mode support.
* Add - Setting to specify the structure of the product identifier.
* Add - Update notice for merchants using a Universal Analytics Property ID.
* Dev - Remove options remapping, use settings directly.
* Fix - Prevent tracking orders multiple times and additional fixes.
* Remove - Universal Analytics code.
* Remove - Universal Analytics integration.
* Simplify tracker event handler API.
* Update - Add shared tracking functionality for WooCommerce Blocks and classic pages.
* Update - Extension branding to Google Analytics for WooCommerce.
* Update - Tracking for GA4 when using Blocks.
= 1.8.14 - 2024-02-07 =
* Tweak - Declare feature compatibility for the new product editor (also called product block editor).
* Tweak - WC 8.6 compatibility.
= 1.8.13 - 2024-01-09 =
* Dev - Upgrade all NPM dev dependencies.
* Dev - Upgrade to Node.js 18.
* Tweak - WC 8.5 compatibility.
= 1.8.12 - 2023-12-28 =
* Fix - Avoid JavaScript exceptions when sending checkout event tracking due to incompatible data structure.
* Fix - Correct misplaced each product index value as its quantity when sending checkout event tracking.
= 1.8.11 - 2023-12-18 =
* Dev - Prevent DB warnings in unit tests.
* Tweak - WC 8.4 compatibility.
= 1.8.10 - 2023-11-28 =
* Dev - Update phpunit polyfills to 1.1 for WP 6.4.
* Fix - Add semicolon after `gtag` calls.
* Fix - Prevent firing up the add_to_cart event when clicking in product image.
* Tweak - Test environment setup to resolve notice.
= 1.8.9 - 2023-11-07 =
* Tweak - WC 8.3 compatibility.
* Tweak - WP 6.4 compatibility.
* Update - Use new Woo.com domain.
* Update - WordPressCS to version 3.0.
= 1.8.8 - 2023-10-24 =
* Tweak - Declare cart_checkout_blocks feature compatibility.
= 1.8.7 - 2023-10-10 =
* Fix - JS syntax error on pages with cart and mini-cart rendered, which was causing purchases and cart removals not to be tracked.
= 1.8.6 - 2023-10-03 =
* Add - Privacy policy guide section.
* Dev - Enable since tag replacement.
* Fix - Track select_content instead of add_to_cart for variations.
* Tweak - Add documentation link with UTM parameters.
* Tweak - Tracking for Products ( Add To Cart and Impression) when using Products (Beta) Block.
* Tweak - WC 8.2 compatibility.
= 1.8.5 - 2023-09-14 =
* Dev - Add Workflow for generation Hooks documentation.
* Dev - Fetch WooCommerce and WordPress versions for our tests.
* Fix - Add To Cart and Impression events when using Blocks.
* Fix - Compat - Add PHP 8.2 support.
* Tweak - WC 8.1.0 compatibility.
= 1.8.4 - 2023-08-08 =
* Dev - Add release preparation GH workflow.
* Fix - Add async attribute in `google-tag-manager` script.
* Tweak - WC 8.0 compatibility.
* Tweak - WP 6.3 compatibility.
= 1.8.3 - 2023-07-11 =
* Dev - Set engines for the repository.
* Fix - Record consecutive cart removals.
* Tweak - WC 7.9 compatibility.
= 1.8.2 - 2023-06-13 =
* Tweak - WC 7.8 compatibility.
= 1.8.1 - 2023-05-09 =
* Fix - Fatal error when running with Elementor.
* Tweak - WC 7.7 compatibility.
= 1.8.0 - 2023-05-02 =
* Add - Create WordPress Hook Actions for Google Analytics.
* Add - Implement tracking with Actions Hooks.
* Dev - Implement JS Build (ES6) and JS Lint.
* Dev - Implement Javascript Building.
= 1.7.1 - 2023-04-12 =
* Fix - Bug with tracking enhanced ecommerce.
= 1.7.0 - 2023-03-28 =
* Dev - Load scripts via `wp_register_scripts` and `wp_eneuque_js`.
* Fix - Avoid duplication of Google Tag Manager script.
* Tweak - WC 7.6 compatibility.
* Tweak - WP 6.2 compatibility.
= 1.6.2 - 2023-03-07 =
* Tweak - WC 7.5 compatibility.
* Tweak - WP 6.2 compatibility.
= 1.6.1 - 2023-02-15 =
* Tweak - WC 7.4 compatibility.
= 1.6.0 - 2023-01-31 =
* Add - Common function for event code.
* Fix - Add PHP unit tests.
* Fix - Feature/consistency across gtag implementation.
* Fix - Fix inconsistencies across item data in events.
* Fix - Fix usage of tracker_var() in load_analytics().
= 1.5.19 - 2023-01-11 =
* Fix - undefined WC constant.
* Tweak - WC 7.3 compatibility.
= 1.5.18 - 2022-12-14 =
* Add - .nvmrc file.
* Tweak - WC 7.2 compatibility.
= 1.5.17 - 2022-11-09 =
* Add - New Google Analytics task in WC.
= 1.5.16 - 2022-11-03 =
* Add - Declare compatibility for High Performance Order Storage.
* Tweak - WC 7.1 compatibility.
* Tweak - WP 6.1 compatibility.
= 1.5.15 - 2022-10-04 =
* Add - Support for a Google Tag ID.
* Tweak - WC 7.0 compatibility.
= 1.5.14 - 2022-09-02 =
* Dev - Add branch-labels GH workflow.
* Dev - GH release config and the new PR template.
* Tweak - WC 6.9 compatibility.
= 1.5.13 - 2022-08-03 =
* Fix - Custom Order table compatibility.
= 1.5.12 - 2022-07-27 =
* Tweak - WC 6.8 compatibility.
= 1.5.11 - 2022-07-06 =
* Tweak - WC 6.7 compatibility.
= 1.5.10 - 2022-06-07 =
* Tweak - WC 6.6 compatibility.
= 1.5.9 - 2022-05-10 =
* Tweak - WC 6.5 compatibility.
* Tweak - WordPress 6.0 compatibility.
= 1.5.8 - 2022-02-02 =
* Add - Support for Google Analytics cross domain tracking features.
= 1.5.7 - 2022-01-13 =
* Fix - Activation error when WC was disabled.
* Tweak - WC 6.1 compatibility.
= 1.5.6 - 2021-12-29 =
* Fix - Confirm order key before displaying transaction tracking code.
= 1.5.5 - 2021-12-09 =
* Tweak - WC 6.0 compatibility.
* Tweak - WP 5.9 compatibility.
= 1.5.4 - 2021-11-10 =
* Fix - Remove the slow order counting query from admin init.
* Tweak - WC 5.9 compatibility.
= 1.5.3 - 2021-09-15 =
* Tweak - Avoid unnecessary completed orders queries.
* Tweak - WC 5.7 compatibility.
* Tweak - WP 5.8 compatibility.
= 1.5.2 - 2021-07-30 =
* Fix - Change utm_source and utm_medium in upsell notice link.
* Fix - add product links to readme.
= 1.5.1 - 2021-02-03 =
* Tweak - WC 5.0 compatibility.
= 1.5.0 - 2020-12-17 =
* Add - Option to use Global Site Tag and the gtag.js library (for Universal Analytics or Google Analytics 4).
* Add - Several new values added to the Tracker data.
* Add - Developer ID for gtag.js and analytics.js.
* Tweak - Bump minimum-supported WooCommerce version to 3.2.
* Tweak - Remove deprecated jQuery .click().
* Fix - Settings link in plugins table row points directly to plugin settings.
* Fix - Issue with multiple consecutive "Remove from Cart" events sent from the mini cart.
= 1.4.25 - 2020-11-25 =
* Tweak - WC 4.7 compatibility.
* Tweak - WordPress 5.6 compatibility.
= 1.4.24 - 2020-10-12 =
* Tweak - WC 4.5 compatibility.
= 1.4.23 - 2020-08-19 =
* Fix - Prevent transaction from being tracked a second time when page is reloaded locally or from cache.
* Tweak - WordPress 5.5 compatibility.
= 1.4.22 - 2020-06-05 =
* Tweak - WC 4.2 compatibility.
= 1.4.21 - 2020-05-04 =
* Tweak - WC 4.1 compatibility.
= 1.4.20 - 2020-03-29 =
* Fix - Change wc_goole_analytics_send_pageview fiter name to wc_google_analytics_send_pageview.
= 1.4.19 - 2020-03-09 =
* Tweak - WordPress 5.4 compatibility.
= 1.4.18 - 2020-03-04 =
* Tweak - Use code sniff version.
* Tweak - WC 4.0 compatibility.
= 1.4.17 - 2020-01-13 =
* Tweak - Update constant VERSION in plugin file
= 1.4.16 - 2020-01-13 =
* Tweak - WC 3.9 compatibility.
= 1.4.15 - 2019-11-04 =
* Tweak - WC 3.8 compatibility.
= 1.4.14 - 2019-09-04 =
* Fix - Google Analytics JS URL missing quotes.
= 1.4.13 - 2019-09-03 =
* Tweak - Make Google Analytics JS script URL filterable.
= 1.4.12 - 2019-08-13 =
* Tweak - WC 3.7 compatibility.
= 1.4.11 - 2019-08-02 =
* Add - Filter to bypass "send pageview" for users whom want to use separate standard GA. `wc_goole_analytics_send_pageview`.
* Fix - Revert last release due to it causing ecommerce tracking to be disabled when standard tracking is disabled.
= 1.4.10 - 2019-07-10 =
* Fix - Ensure universal analytics pageview doesnt occur if standard tracking is disabled.
= 1.4.9 - 2019-04-16 =
* Tweak - WC 3.6 compatibility.
= 1.4.8 - 2019-03-04 =
* Fix - Event for deleting from cart not sent after a cart update.
= 1.4.7 - 11/19/2018 =
* Tweak - WP 5.0 compatibility.
= 1.4.6 - 11/06/2018 =
* Fix - Check for active WooCommerce plugin.
= 1.4.5 - 10/16/2018 =
* Tweak - Mention Google Analytics Pro in certain cases.
* Tweak - WC 3.5 compatibility.
= 1.4.4 - 03/20/2018 =
* Fix - WC30 compatibility error when using deprecated get_product_from_item method.
* Fix - Check object before using methods to prevent errors.
* Fix - Variations not reporting category in cart tracking.
* Add - Filter woocommerce_ga_disable_tracking added to disable tracking.
* Tweak - Rebuilt languages pot file.
= 1.4.3 - 06/15/2018 =
* Fix - WC 3.x notice by using proper variation data.
* Add - Option to track 404 (Not found) errors.
= 1.4.2 - 09/05/2017 =
* Fix - Missing Google Analytics ID.
= 1.4.1 - 01/05/2017 =
* Add - Filters for GA snippet (woocommerce_ga_snippet_head, woocommerce_ga_snippet_create, woocommerce_ga_snippet_require, woocommerce_ga_snippet_output)
* Add - Option to toggle on/off Enhanced Link Attribution
* Fix - JavaScript break by wrapping it in quotes
* Fix - Use ID and SKU data in a consistent way so that all products are correctly tracked.
* Fix - Updates for WooCommerce 3.0 compatibility.
* Add - Settings link to the plugin in the Plugins screen
* Fix - Fatal error on shortcode usage for empty product
= 1.4.0 - 20/11/2015 =
* Feature - Support for enhanced eCommerce (tracking full store process from view to order)
* Tweak - Setting up the plugin is now clearer with some helpful links and clearer language
* Tweak - New filter on the ga global variable
* Refactor - JavaScript generation functions have been moved to their own class
= 1.3.0 - 12/11/2014 =
* Feature - Added the transaction currency in the tracking code
* Feature - Add data privacy option that are mandatory in some countries
* Tweak - Moved the tracking code to the head of the page
* Tweak - Remove the "SKU" prefix to the sku for addItem
* Refactor - Integration class reformulated
= 1.2.2 - 15/10/2014 =
* Feature - Adding option to anonymize IP addresses
* Feature - Adding gaOptOut function to be called from any page for OptOut
= 1.2.1 - 17/09/2014 =
* Tweak - Adding utmnooverride to return url for Google Adwords
= 1.2.0 - 28/07/2014 =
* Feature - Adding display advertising parameter to Universal Analytics
* Fix - Using get_total_shipping() instead of get_shipping
* Fix - Using wc_enqueue_js() instead of $woocommerce->add_inline_js(
* Tweak - Updating plugin FAQ
* Tweak - Adding parenthesis for clarity
= 1.1 - 29/05/2014 =
* Added option to enable Display Advertising
* Added compatibility support for WooCommerce 2.1 beta releases
= 1.0 - 22/11/2013 =
* Initial release

View File

@@ -0,0 +1,388 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use Automattic\WooCommerce\StoreApi\Schemas\V1\ProductSchema;
use Automattic\WooCommerce\StoreApi\Schemas\V1\CartItemSchema;
/**
* WC_Abstract_Google_Analytics_JS class
*
* Abstract JS for recording Google Analytics/Gtag info
*/
abstract class WC_Abstract_Google_Analytics_JS {
/** @var WC_Abstract_Google_Analytics_JS $instance Class Instance */
protected static $instance;
/** @var array $settings Inherited Analytics settings */
protected static $settings;
/** @var string Developer ID */
public const DEVELOPER_ID = 'dOGY3NW';
/**
* Constructor
* To be called from child classes to setup event data
*
* @return void
*/
public function __construct() {
$this->attach_event_data();
if ( did_action( 'woocommerce_blocks_loaded' ) ) {
woocommerce_store_api_register_endpoint_data(
array(
'endpoint' => ProductSchema::IDENTIFIER,
'namespace' => 'woocommerce_google_analytics_integration',
'data_callback' => array( $this, 'data_callback' ),
'schema_callback' => array( $this, 'schema_callback' ),
'schema_type' => ARRAY_A,
)
);
woocommerce_store_api_register_endpoint_data(
array(
'endpoint' => CartItemSchema::IDENTIFIER,
'namespace' => 'woocommerce_google_analytics_integration',
'data_callback' => array( $this, 'data_callback' ),
'schema_callback' => array( $this, 'schema_callback' ),
'schema_type' => ARRAY_A,
)
);
}
}
/**
* Hook into various parts of WooCommerce and set the relevant
* script data that the frontend tracking script will use.
*
* @return void
*/
public function attach_event_data(): void {
add_action(
'wp_head',
function () {
$this->set_script_data( 'cart', $this->get_formatted_cart() );
}
);
add_action(
'woocommerce_before_single_product',
function () {
global $product;
$this->set_script_data( 'product', $this->get_formatted_product( $product ) );
}
);
add_action(
'woocommerce_add_to_cart',
function ( $cart_item_key, $product_id, $quantity, $variation_id, $variation ) {
$this->set_script_data( 'added_to_cart', $this->get_formatted_product( wc_get_product( $product_id ), $variation_id, $variation, $quantity ) );
},
10,
5
);
add_filter(
'woocommerce_loop_add_to_cart_link',
function ( $button, $product ) {
$this->append_script_data( 'products', $this->get_formatted_product( $product ) );
return $button;
},
10,
2
);
add_action(
'woocommerce_thankyou',
function ( $order_id ) {
if ( 'yes' === self::get( 'ga_ecommerce_tracking_enabled' ) ) {
$order = wc_get_order( $order_id );
if ( $order && $order->get_meta( '_ga_tracked' ) !== '1' ) {
// Check order key.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$order_key = empty( $_GET['key'] ) ? '' : wc_clean( wp_unslash( $_GET['key'] ) );
if ( $order->key_is_valid( $order_key ) ) {
// Mark the order as tracked.
$order->update_meta_data( '_ga_tracked', 1 );
$order->save();
$this->set_script_data( 'order', $this->get_formatted_order( $order ) );
}
}
}
}
);
}
/**
* Return one of our settings
*
* @param string $setting Key/name for the setting.
*
* @return string|null Value of the setting or null if not found
*/
protected static function get( $setting ): ?string {
return self::$settings[ $setting ] ?? null;
}
/**
* Generic GA snippet for opt out
*/
public static function load_opt_out(): void {
$code = "
var gaProperty = '" . esc_js( self::get( 'ga_id' ) ) . "';
var disableStr = 'ga-disable-' + gaProperty;
if ( document.cookie.indexOf( disableStr + '=true' ) > -1 ) {
window[disableStr] = true;
}
function gaOptout() {
document.cookie = disableStr + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
window[disableStr] = true;
}";
wp_register_script( 'google-analytics-opt-out', '', array(), null, false );
wp_add_inline_script( 'google-analytics-opt-out', $code );
wp_enqueue_script( 'google-analytics-opt-out' );
}
/**
* Get item identifier from product data
*
* @param WC_Product $product WC_Product Object.
*
* @return string
*/
public static function get_product_identifier( WC_Product $product ): string {
$identifier = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id();
if ( 'product_sku' === self::get( 'ga_product_identifier' ) ) {
if ( ! empty( $product->get_sku() ) ) {
$identifier = $product->get_sku();
} else {
$identifier = '#' . ( $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id() );
}
}
return apply_filters( 'woocommerce_ga_product_identifier', $identifier, $product );
}
/**
* Returns an array of cart data in the required format
*
* @return array
*/
public function get_formatted_cart(): array {
return array(
'items' => array_map(
function ( $item ) {
return array_merge(
$this->get_formatted_product( $item['data'] ),
array(
'quantity' => $item['quantity'],
'prices' => array(
'price' => $this->get_formatted_price( $item['line_total'] ),
'currency_minor_unit' => wc_get_price_decimals(),
),
)
);
},
array_values( WC()->cart->get_cart() )
),
'coupons' => WC()->cart->get_coupons(),
'totals' => array(
'currency_code' => get_woocommerce_currency(),
'total_price' => $this->get_formatted_price( WC()->cart->get_total( 'edit' ) ),
'currency_minor_unit' => wc_get_price_decimals(),
),
);
}
/**
* Returns an array of product data in the required format
*
* @param WC_Product $product The product to format.
* @param int $variation_id Variation product ID.
* @param array|bool $variation An array containing product variation attributes to include in the product data.
* For the "variation" type products, we'll use product->get_attributes.
* @param bool|int $quantity Quantity to include in the formatted product object
*
* @return array
*/
public function get_formatted_product( WC_Product $product, $variation_id = 0, $variation = false, $quantity = false ): array {
$product_id = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id();
$price = $product->get_price();
// Get product price from chosen variation if set.
if ( $variation_id ) {
$variation_product = wc_get_product( $variation_id );
if ( $variation_product ) {
$price = $variation_product->get_price();
}
}
$formatted = array(
'id' => $product_id,
'name' => $product->get_title(),
'categories' => array_map(
fn( $category ) => array( 'name' => $category->name ),
wc_get_product_terms( $product_id, 'product_cat', array( 'number' => 5 ) )
),
'prices' => array(
'price' => $this->get_formatted_price( $price ),
'currency_minor_unit' => wc_get_price_decimals(),
),
'extensions' => array(
'woocommerce_google_analytics_integration' => array(
'identifier' => $this->get_product_identifier( $product ),
),
),
);
if ( $quantity ) {
$formatted['quantity'] = (int) $quantity;
}
if ( $product->is_type( 'variation' ) ) {
$variation = $product->get_attributes();
}
if ( is_array( $variation ) ) {
$formatted['variation'] = implode(
', ',
array_map(
function ( $attribute, $value ) {
return sprintf(
'%s: %s',
str_replace( 'attribute_', '', $attribute ),
$value
);
},
array_keys( $variation ),
array_values( $variation )
)
);
}
return $formatted;
}
/**
* Returns an array of order data in the required format
*
* @param WC_Abstract_Order $order An instance of the WooCommerce Order object.
*
* @return array
*/
public function get_formatted_order( $order ): array {
return array(
'id' => $order->get_id(),
'affiliation' => get_bloginfo( 'name' ),
'totals' => array(
'currency_code' => $order->get_currency(),
'currency_minor_unit' => wc_get_price_decimals(),
'tax_total' => $this->get_formatted_price( $order->get_total_tax() ),
'shipping_total' => $this->get_formatted_price( $order->get_total_shipping() ),
'total_price' => $this->get_formatted_price( $order->get_total() ),
),
'items' => array_map(
function ( $item ) {
return array_merge(
$this->get_formatted_product( $item->get_product() ),
array(
'quantity' => $item->get_quantity(),
// The method get_total() will return the price after coupon discounts.
// https://github.com/woocommerce/woocommerce/blob/54eba223b8dec015c91a13423f9eced09e96f399/plugins/woocommerce/includes/class-wc-order-item-product.php#L308-L310
'price_after_coupon_discount' => $this->get_formatted_price( $item->get_total() ),
)
);
},
array_values( $order->get_items() ),
),
);
}
/**
* Formats a price the same way WooCommerce Blocks does
*
* @param mixed $value The price value for format
*
* @return int
*/
public function get_formatted_price( $value ): int {
return intval(
round(
( (float) wc_format_decimal( $value ) ) * ( 10 ** absint( wc_get_price_decimals() ) ),
0
)
);
}
/**
* Add product identifier to StoreAPI
*
* @param WC_Product|array $product Either an instance of WC_Product or a cart item array depending on the endpoint
*
* @return array
*/
public function data_callback( $product ): array {
$product = is_a( $product, 'WC_Product' ) ? $product : $product['data'];
return array(
'identifier' => (string) $this->get_product_identifier( $product ),
);
}
/**
* Schema for the extended StoreAPI data
*
* @return array
*/
public function schema_callback(): array {
return array(
'identifier' => array(
'description' => __( 'The formatted product identifier to use in Google Analytics events.', 'woocommerce-google-analytics-integration' ),
'type' => 'string',
'readonly' => true,
),
);
}
/**
* Returns the tracker variable this integration should use
*
* @return string
*/
abstract public static function tracker_function_name(): string;
/**
* Add an event to the script data
*
* @param string $type The type of event this data is related to.
* @param string|array $data The event data to add.
*
* @return void
*/
abstract public function set_script_data( string $type, $data ): void;
/**
* Append data to an existing script data array
*
* @param string $type The type of event this data is related to.
* @param string|array $data The event data to add.
*
* @return void
*/
abstract public function append_script_data( string $type, $data ): void;
/**
* Get the class instance
*
* @param array $settings Settings
* @return WC_Abstract_Google_Analytics_JS
*/
abstract public static function get_instance( $settings = array() ): WC_Abstract_Google_Analytics_JS;
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Set up Google Analytics for WooCommerce task.
*
* Adds a set up Google Analytics for WooCommerce task to the task list.
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task;
/**
* Setup Task class.
*/
class WC_Google_Analytics_Task extends Task {
/**
* Get the ID of the task.
*
* @return string
*/
public function get_id() {
return 'setup-google-analytics-integration';
}
/**
* Get the title for the task.
*
* @return string
*/
public function get_title() {
return esc_html__( 'Set up Google Analytics', 'woocommerce-google-analytics-integration' );
}
/**
* Get the content for the task.
*
* @return string
*/
public function get_content() {
return esc_html__( 'Provides the integration between WooCommerce and Google Analytics.', 'woocommerce-google-analytics-integration' );
}
/**
* Get the time required to perform the task.
*
* @return string
*/
public function get_time() {
return esc_html__( '5 minutes', 'woocommerce-google-analytics-integration' );
}
/**
* Get the action URL for the task.
*
* @return string
*/
public function get_action_url() {
return WC_Google_Analytics_Integration::get_instance()->get_settings_url();
}
/**
* Check if the task is complete.
*
* @return bool
*/
public function is_complete() {
return WC_Google_Analytics_Integration::get_integration()->is_setup_complete();
}
}

View File

@@ -0,0 +1,315 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists;
/**
* Google Analytics for WooCommerce
*
* Allows tracking code to be inserted into store pages.
*
* @class WC_Google_Analytics
* @extends WC_Integration
*/
class WC_Google_Analytics extends WC_Integration {
/**
* Returns the proper class based on Gtag settings.
*
* @return WC_Abstract_Google_Analytics_JS
*/
protected function get_tracking_instance() {
return WC_Google_Gtag_JS::get_instance( $this->settings );
}
/**
* Constructor
* Init and hook in the integration.
*/
public function __construct() {
$this->id = 'google_analytics';
$this->method_title = __( 'Google Analytics', 'woocommerce-google-analytics-integration' );
$this->method_description = __( 'Google Analytics is a free service offered by Google that generates detailed statistics about the visitors to a website.', 'woocommerce-google-analytics-integration' );
// Load the settings
$this->init_form_fields();
$this->init_settings();
add_action( 'admin_notices', array( $this, 'universal_analytics_upgrade_notice' ) );
include_once 'class-wc-abstract-google-analytics-js.php';
include_once 'class-wc-google-gtag-js.php';
if ( ! $this->disable_tracking( 'all' ) ) {
$this->get_tracking_instance();
}
// Display a task on "Things to do next section"
add_action( 'init', array( $this, 'add_wc_setup_task' ), 20 );
// Admin Options
add_filter( 'woocommerce_tracker_data', array( $this, 'track_settings' ) );
add_action( 'woocommerce_update_options_integration_google_analytics', array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_update_options_integration_google_analytics', array( $this, 'show_options_info' ) );
add_action( 'admin_init', array( $this, 'privacy_policy' ) );
// utm_nooverride parameter for Google AdWords
add_filter( 'woocommerce_get_return_url', array( $this, 'utm_nooverride' ) );
// Dequeue the WooCommerce Blocks Google Analytics integration,
// not to let it register its `gtag` function so that we could provide a more detailed configuration.
add_action(
'wp_enqueue_scripts',
function () {
wp_dequeue_script( 'wc-blocks-google-analytics' );
}
);
}
/**
* Conditionally display an error notice to the merchant if the stored property ID starts with "UA"
*
* @return void
*/
public function universal_analytics_upgrade_notice() {
if ( 'ua' === substr( strtolower( $this->get_option( 'ga_id' ) ), 0, 2 ) ) {
printf(
'<div class="%1$s"><p>%2$s</p></div>',
'notice notice-error',
sprintf(
/* translators: 1) URL for Google documentation on upgrading from UA to GA4 2) URL to WooCommerce Google Analytics settings page */
esc_html__( 'Your website is configured to use Universal Analytics which Google retired in July of 2023. Update your account using the %1$ssetup assistant%2$s and then update your %3$sWooCommerce settings%4$s.', 'woocommerce-google-analytics-integration' ),
'<a href="https://support.google.com/analytics/answer/9744165" target="_blank">',
'</a>',
'<a href="/wp-admin/admin.php?page=wc-settings&tab=integration&section=google_analytics">',
'</a>'
)
);
}
}
/**
* Tells WooCommerce which settings to display under the "integration" tab
*/
public function init_form_fields() {
$this->form_fields = array(
'ga_product_identifier' => array(
'title' => __( 'Product Identification', 'woocommerce-google-analytics-integration' ),
'description' => __( 'Specify how your products will be identified to Google Analytics. Changing this setting may cause issues with historical data if a product was previously identified using a different structure.', 'woocommerce-google-analytics-integration' ),
'type' => 'select',
'options' => array(
'product_id' => __( 'Product ID', 'woocommerce-google-analytics-integration' ),
'product_sku' => __( 'Product SKU with prefixed (#) ID as fallback', 'woocommerce-google-analytics-integration' ),
),
// If the option is not set then the product SKU is used as default for existing installations
'default' => 'product_sku',
),
'ga_id' => array(
'title' => __( 'Google Analytics Tracking ID', 'woocommerce-google-analytics-integration' ),
'description' => __( 'Log into your Google Analytics account to find your ID. e.g. <code>GT-XXXXX</code> or <code>G-XXXXX</code>', 'woocommerce-google-analytics-integration' ),
'type' => 'text',
'placeholder' => 'GT-XXXXX',
'default' => get_option( 'woocommerce_ga_id' ), // Backwards compat
),
'ga_support_display_advertising' => array(
'label' => __( '"Display Advertising" Support', 'woocommerce-google-analytics-integration' ),
/* translators: Read more link */
'description' => sprintf( __( 'Set the Google Analytics code to support Display Advertising. %1$sRead more about Display Advertising%2$s.', 'woocommerce-google-analytics-integration' ), '<a href="https://support.google.com/analytics/answer/2700409" target="_blank">', '</a>' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => get_option( 'woocommerce_ga_support_display_advertising' ) ? get_option( 'woocommerce_ga_support_display_advertising' ) : 'yes', // Backwards compat
),
'ga_404_tracking_enabled' => array(
'label' => __( 'Track 404 (Not found) Errors', 'woocommerce-google-analytics-integration' ),
/* translators: Read more link */
'description' => sprintf( __( 'Enable this to find broken or dead links. An "Event" with category "Error" and action "404 Not Found" will be created in Google Analytics for each incoming pageview to a non-existing page. By setting up a "Custom Goal" for these events within Google Analytics you can find out where broken links originated from (the referrer). %1$sRead how to set up a goal%2$s.', 'woocommerce-google-analytics-integration' ), '<a href="https://support.google.com/analytics/answer/1032415" target="_blank">', '</a>' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_linker_allow_incoming_enabled' => array(
'label' => __( 'Accept Incoming Linker Parameters', 'woocommerce-google-analytics-integration' ),
'description' => __( 'Enabling this option will allow incoming linker parameters from other websites.', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'no',
),
'ga_ecommerce_tracking_enabled' => array(
'title' => __( 'Event Tracking', 'woocommerce-google-analytics-integration' ),
'label' => __( 'Purchase Transactions', 'woocommerce-google-analytics-integration' ),
'description' => __( 'This requires a payment gateway that redirects to the thank you/order received page after payment. Orders paid with gateways which do not do this will not be tracked.', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => 'start',
'default' => get_option( 'woocommerce_ga_ecommerce_tracking_enabled' ) ? get_option( 'woocommerce_ga_ecommerce_tracking_enabled' ) : 'yes', // Backwards compat
),
'ga_event_tracking_enabled' => array(
'label' => __( 'Add to Cart Events', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_enhanced_remove_from_cart_enabled' => array(
'label' => __( 'Remove from Cart Events', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_enhanced_product_impression_enabled' => array(
'label' => __( 'Product Impressions from Listing Pages', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_enhanced_product_click_enabled' => array(
'label' => __( 'Product Clicks from Listing Pages', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_enhanced_product_detail_view_enabled' => array(
'label' => __( 'Product Detail Views', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_enhanced_checkout_process_enabled' => array(
'label' => __( 'Checkout Process Initiated', 'woocommerce-google-analytics-integration' ),
'type' => 'checkbox',
'checkboxgroup' => '',
'default' => 'yes',
),
'ga_linker_cross_domains' => array(
'title' => __( 'Cross Domain Tracking', 'woocommerce-google-analytics-integration' ),
/* translators: Read more link */
'description' => sprintf( __( 'Add a comma separated list of domains for automatic linking. %1$sRead more about Cross Domain Measurement%2$s', 'woocommerce-google-analytics-integration' ), '<a href="https://support.google.com/analytics/answer/7476333" target="_blank">', '</a>' ),
'type' => 'text',
'placeholder' => 'example.com, example.net',
'default' => '',
),
);
}
/**
* Shows some additional help text after saving the Google Analytics settings
*/
public function show_options_info() {
$this->method_description .= "<div class='notice notice-info'><p>" . __( 'Please allow Google Analytics 24 hours to start displaying results.', 'woocommerce-google-analytics-integration' ) . '</p></div>';
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['woocommerce_google_analytics_ga_ecommerce_tracking_enabled'] ) && true === (bool) $_REQUEST['woocommerce_google_analytics_ga_ecommerce_tracking_enabled'] ) {
$this->method_description .= "<div class='notice notice-info'><p>" . __( 'Please note, for transaction tracking to work properly, you will need to use a payment gateway that redirects the customer back to a WooCommerce order received/thank you page.', 'woocommerce-google-analytics-integration' ) . '</div>';
}
}
/**
* Hooks into woocommerce_tracker_data and tracks some of the analytic settings (just enabled|disabled status)
* only if you have opted into WooCommerce tracking
* https://woo.com/usage-tracking/
*
* @param array $data Current WC tracker data.
* @return array Updated WC Tracker data.
*/
public function track_settings( $data ) {
$settings = $this->settings;
$data['wc-google-analytics'] = array(
'support_display_advertising' => $settings['ga_support_display_advertising'],
'ga_404_tracking_enabled' => $settings['ga_404_tracking_enabled'],
'ecommerce_tracking_enabled' => $settings['ga_ecommerce_tracking_enabled'],
'event_tracking_enabled' => $settings['ga_event_tracking_enabled'],
'plugin_version' => WC_GOOGLE_ANALYTICS_INTEGRATION_VERSION,
'linker_allow_incoming_enabled' => empty( $settings['ga_linker_allow_incoming_enabled'] ) ? 'no' : 'yes',
'linker_cross_domains' => $settings['ga_linker_cross_domains'],
);
// ID prefix, blank, or X for unknown
$prefix = strstr( strtoupper( $settings['ga_id'] ), '-', true );
if ( in_array( $prefix, array( 'UA', 'G', 'GT' ), true ) || empty( $prefix ) ) {
$data['wc-google-analytics']['ga_id'] = $prefix;
} else {
$data['wc-google-analytics']['ga_id'] = 'X';
}
return $data;
}
/**
* Add suggested privacy policy content
*
* @return void
*/
public function privacy_policy() {
$policy_text = sprintf(
/* translators: 1) HTML anchor open tag 2) HTML anchor closing tag */
esc_html__( 'By using this extension, you may be storing personal data or sharing data with an external service. %1$sLearn more about what data is collected by Google and what you may want to include in your privacy policy%2$s.', 'woocommerce-google-analytics-integration' ),
'<a href="https://support.google.com/analytics/answer/7318509" target="_blank">',
'</a>'
);
// As the extension doesn't offer suggested privacy policy text, the button to copy it is hidden.
$content = '
<p class="privacy-policy-tutorial">' . $policy_text . '</p>
<style>#privacy-settings-accordion-block-woocommerce-google-analytics-integration .privacy-settings-accordion-actions { display: none }</style>';
wp_add_privacy_policy_content( 'Google Analytics for WooCommerce', wpautop( $content, false ) );
}
/**
* Check if tracking is disabled
*
* @param string $type The setting to check
* @return bool True if tracking for a certain setting is disabled
*/
private function disable_tracking( $type ) {
return is_admin() || current_user_can( 'manage_options' ) || empty( $this->settings['ga_id'] ) || 'no' === $type || apply_filters( 'woocommerce_ga_disable_tracking', false, $type );
}
/**
* Add the utm_nooverride parameter to any return urls. This makes sure Google Adwords doesn't mistake the offsite gateway as the referrer.
*
* @param string $return_url WooCommerce Return URL
* @return string URL
*/
public function utm_nooverride( $return_url ) {
// We don't know if the URL already has the parameter so we should remove it just in case
$return_url = remove_query_arg( 'utm_nooverride', $return_url );
// Now add the utm_nooverride query arg to the URL
$return_url = add_query_arg( 'utm_nooverride', '1', $return_url );
return esc_url( $return_url, null, 'db' );
}
/**
* Check if the Google Analytics Tracking ID has been set up.
*
* @since 1.5.17
*
* @return bool Whether the Google Analytics setup is completed.
*/
public function is_setup_complete() {
return (bool) $this->get_option( 'ga_id' );
}
/**
* Adds the setup task to the Tasklists.
*
* @since 1.5.17
*/
public function add_wc_setup_task() {
require_once 'class-wc-google-analytics-task.php';
TaskLists::add_task(
'extended',
new WC_Google_Analytics_Task(
TaskLists::get_list( 'extended' )
)
);
}
}

View File

@@ -0,0 +1,378 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use WC_Google_Analytics_Integration as Plugin;
/**
* WC_Google_Gtag_JS class
*
* JS for recording Google Gtag info
*/
class WC_Google_Gtag_JS extends WC_Abstract_Google_Analytics_JS {
/** @var string $script_handle Handle for the front end JavaScript file */
public $script_handle = 'woocommerce-google-analytics-integration';
/** @var string $gtag_script_handle Handle for the gtag setup script */
public $gtag_script_handle = 'woocommerce-google-analytics-integration-gtag';
/** @var string $data_script_handle Handle for the event data inline script */
public $data_script_handle = 'woocommerce-google-analytics-integration-data';
/** @var string $script_data Data required for frontend event tracking */
private $script_data = array();
/** @var array $mappings A map of the GA4 events and the classic WooCommerce hooks that trigger them */
private $mappings = array(
'actions' => array(
'begin_checkout' => 'woocommerce_before_checkout_form',
'purchase' => 'woocommerce_thankyou',
'add_to_cart' => 'woocommerce_add_to_cart',
'remove_from_cart' => 'woocommerce_cart_item_removed',
'view_item' => 'woocommerce_after_single_product',
),
'filters' => array(
'view_item_list' => 'woocommerce_loop_add_to_cart_link',
),
);
/**
* Constructor
* Takes our settings from the parent class so we can later use them in the JS snippets
*
* @param array $settings Settings
*/
public function __construct( $settings = array() ) {
parent::__construct();
self::$settings = $settings;
$this->map_hooks();
$this->register_scripts();
// Setup frontend scripts
add_action( 'wp_enqueue_scripts', array( $this, 'enquque_tracker' ), 5 );
add_action( 'wp_footer', array( $this, 'inline_script_data' ) );
}
/**
* Register manager and tracker scripts.
* Call early so other extensions could add inline date to it.
*
* @return void
*/
private function register_scripts(): void {
wp_register_script(
'google-tag-manager',
'https://www.googletagmanager.com/gtag/js?id=' . self::get( 'ga_id' ),
array(),
null,
array(
'strategy' => 'async',
)
);
wp_register_script(
$this->gtag_script_handle,
'',
array(),
null,
array(
'in_footer' => false,
)
);
wp_add_inline_script(
$this->gtag_script_handle,
apply_filters(
'woocommerce_gtag_snippet',
sprintf(
'/* Google Analytics for WooCommerce (gtag.js) */
window.dataLayer = window.dataLayer || [];
function %2$s(){dataLayer.push(arguments);}
// Set up default consent state.
for ( const mode of %4$s || [] ) {
%2$s( "consent", "default", mode );
}
%2$s("js", new Date());
%2$s("set", "developer_id.%3$s", true);
%2$s("config", "%1$s", %5$s);',
esc_js( $this->get( 'ga_id' ) ),
esc_js( $this->tracker_function_name() ),
esc_js( static::DEVELOPER_ID ),
json_encode( $this->get_consent_modes() ),
json_encode( $this->get_site_tag_config() )
)
)
);
wp_enqueue_script( $this->gtag_script_handle );
wp_register_script(
$this->script_handle,
Plugin::get_instance()->get_js_asset_url( 'main.js' ),
array(
...Plugin::get_instance()->get_js_asset_dependencies( 'main' ),
'google-tag-manager',
),
Plugin::get_instance()->get_js_asset_version( 'main' ),
true
);
}
/**
* Enqueue tracker scripts and its inline config.
* We need to execute tracker.js w/ `gtag` configuration before any trackable action may happen.
*
* @return void
*/
public function enquque_tracker(): void {
wp_enqueue_script( 'google-tag-manager' );
// tracker.js needs to be executed ASAP, the remaining bits for main.js could be deffered,
// but to reduce the traffic, we ship it all together.
wp_enqueue_script( $this->script_handle );
}
/**
* Add all event data via an inline script in the footer to ensure all the data is collected in time.
*
* @return void
*/
public function inline_script_data(): void {
wp_register_script(
$this->data_script_handle,
'',
array( $this->script_handle ),
null,
array(
'in_footer' => true,
)
);
wp_add_inline_script(
$this->data_script_handle,
sprintf(
'window.ga4w = { data: %1$s, settings: %2$s }; document.dispatchEvent(new Event("ga4w:ready"));',
$this->get_script_data(),
wp_json_encode(
array(
'tracker_function_name' => $this->tracker_function_name(),
'events' => $this->get_enabled_events(),
'identifier' => $this->get( 'ga_product_identifier' ),
),
),
)
);
wp_enqueue_script( $this->data_script_handle );
}
/**
* Hook into WooCommerce and add corresponding Blocks Actions to our event data
*
* @return void
*/
public function map_hooks(): void {
array_walk(
$this->mappings['actions'],
function ( $hook, $gtag_event ) {
add_action(
$hook,
function () use ( $gtag_event ) {
$this->append_event( $gtag_event );
}
);
}
);
array_walk(
$this->mappings['filters'],
function ( $hook, $gtag_event ) {
add_action(
$hook,
function ( $filtered_value ) use ( $gtag_event ) {
$this->append_event( $gtag_event );
return $filtered_value;
}
);
}
);
}
/**
* Appends a specific event, if it's not included yet.
*
* @param string $gtag_event
* @return void
*/
private function append_event( string $gtag_event ) {
if ( ! in_array( $gtag_event, $this->script_data['events'] ?? [], true ) ) {
$this->append_script_data( 'events', $gtag_event );
}
}
/**
* Set script data for a specific event
*
* @param string $type The type of event this data is related to.
* @param string|array $data The event data to add.
*
* @return void
*/
public function set_script_data( string $type, $data ): void {
$this->script_data[ $type ] = $data;
}
/**
* Append data to an existing script data array
*
* @param string $type The type of event this data is related to.
* @param string|array $data The event data to add.
*
* @return void
*/
public function append_script_data( string $type, $data ): void {
if ( ! isset( $this->script_data[ $type ] ) ) {
$this->script_data[ $type ] = array();
}
$this->script_data[ $type ][] = $data;
}
/**
* Return a JSON encoded string of all script data for the current page load
*
* @return string
*/
public function get_script_data(): string {
return wp_json_encode( $this->script_data );
}
/**
* Returns the tracker variable this integration should use
*
* @return string
*/
public static function tracker_function_name(): string {
return apply_filters( 'woocommerce_gtag_tracker_variable', 'gtag' );
}
/**
* Return Google Analytics configuration, for JS to read.
*
* @return array
*/
public function get_site_tag_config(): array {
return apply_filters(
'woocommerce_ga_gtag_config',
array(
'track_404' => 'yes' === $this->get( 'ga_404_tracking_enabled' ),
'allow_google_signals' => 'yes' === $this->get( 'ga_support_display_advertising' ),
'logged_in' => is_user_logged_in(),
'linker' => array(
'domains' => ! empty( $this->get( 'ga_linker_cross_domains' ) ) ? array_map( 'esc_js', explode( ',', $this->get( 'ga_linker_cross_domains' ) ) ) : array(),
'allow_incoming' => 'yes' === $this->get( 'ga_linker_allow_incoming_enabled' ),
),
'custom_map' => array(
'dimension1' => 'logged_in',
),
),
);
}
/**
* Get an array containing the names of all enabled events
*
* @return array
*/
public static function get_enabled_events(): array {
$events = array();
$settings = array(
'purchase' => 'ga_ecommerce_tracking_enabled',
'add_to_cart' => 'ga_event_tracking_enabled',
'remove_from_cart' => 'ga_enhanced_remove_from_cart_enabled',
'view_item_list' => 'ga_enhanced_product_impression_enabled',
'select_content' => 'ga_enhanced_product_click_enabled',
'view_item' => 'ga_enhanced_product_detail_view_enabled',
'begin_checkout' => 'ga_enhanced_checkout_process_enabled',
);
foreach ( $settings as $event => $setting_name ) {
if ( 'yes' === self::get( $setting_name ) ) {
$events[] = $event;
}
}
return $events;
}
/**
* Get the default state configuration of consent mode.
*/
protected static function get_consent_modes(): array {
$consent_modes = array(
array(
'analytics_storage' => 'denied',
'ad_storage' => 'denied',
'ad_user_data' => 'denied',
'ad_personalization' => 'denied',
'region' => array(
'AT',
'BE',
'BG',
'HR',
'CY',
'CZ',
'DK',
'EE',
'FI',
'FR',
'DE',
'GR',
'HU',
'IS',
'IE',
'IT',
'LV',
'LI',
'LT',
'LU',
'MT',
'NL',
'NO',
'PL',
'PT',
'RO',
'SK',
'SI',
'ES',
'SE',
'GB',
'CH',
),
),
);
/**
* Filters the default gtag consent mode configuration.
*
* @param array $consent_modes Array of default state configuration of consent mode.
*/
return apply_filters( 'woocommerce_ga_gtag_consent_modes', $consent_modes );
}
/**
* Get the class instance
*
* @param array $settings Settings
* @return WC_Abstract_Google_Analytics_JS
*/
public static function get_instance( $settings = array() ): WC_Abstract_Google_Analytics_JS {
if ( null === self::$instance ) {
self::$instance = new self( $settings );
}
return self::$instance;
}
}

View File

@@ -0,0 +1,64 @@
=== Google Analytics for WooCommerce ===
Contributors: woocommerce, automattic, claudiosanches, bor0, royho, laurendavissmith001, cshultz88, mmjones, tomalec
Tags: woocommerce, google analytics
Requires at least: 6.2
Tested up to: 6.5
Stable tag: 2.0.7
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Provides integration between Google Analytics and WooCommerce.
== Description ==
This plugin provides the integration between Google Analytics and the WooCommerce plugin. You can link a referral to a purchase and add transaction information to your Google Analytics data. It supports Global Site Tag (GA4) and eCommerce event tracking.
Please visit the [documentation page for additional information](https://woo.com/document/google-analytics-integration/).
Contributions are welcome via the [GitHub repository](https://github.com/woocommerce/woocommerce-google-analytics-integration).
== Installation ==
1. Download the plugin file to your computer and unzip it
2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installations wp-content/plugins/ directory.
3. Activate the plugin from the Plugins menu within the WordPress admin.
4. Don't forget to enable e-commerce tracking in your Google Analytics account: [https://support.google.com/analytics/answer/1009612?hl=en](https://support.google.com/analytics/answer/1009612?hl=en)
Or use the automatic installation wizard through your admin panel, just search for this plugin's name.
== Frequently Asked Questions ==
= Where can I find the setting for this plugin? =
This plugin will add the settings to the Integration tab, found in the WooCommerce → Settings menu.
= I don't see the code on my site. Where is it? =
We purposefully don't track admin visits to the site. Log out of the site (or open a Google Chrome Incognito window) and check if the code is there for non-admins.
Also please make sure to enter your Google Analytics ID under WooCommerce → Settings → Integrations.
= My code is there. Why is it still not tracking sales? =
Duplicate Google Analytics code causes a conflict in tracking. Remove any other Google Analytics plugins or code from your site to avoid duplication and conflicts in tracking.
== Screenshots ==
1. Google Analytics Integration Settings.
== Changelog ==
= 2.0.7 - 2024-05-14 =
* Dev - Add a manual workflow run for PHP unit tests.
* Fix - Apply discounts to the ecommerce event if available.
* Fix - Incorrect quantity value when adding product to the cart that exists in the cart.
* Tweak - WC 8.9 compatibility.
= 2.0.6 - 2024-04-09 =
* Tweak - WC 8.8 compatibility.
= 2.0.5 - 2024-04-04 =
* Tweak - Register `woocommerce-google-analytics-integration` script earlier, so other extensions can plug in.
* Update - Separate the site tag from the event tracking file and delay execution until DOMContentLoaded.
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-google-analytics-integration/trunk/changelog.txt).

View File

@@ -0,0 +1,321 @@
<?php
/**
* Plugin Name: Google Analytics for WooCommerce
* Plugin URI: https://wordpress.org/plugins/woocommerce-google-analytics-integration/
* Description: Allows Google Analytics tracking code to be inserted into WooCommerce store pages.
* Author: WooCommerce
* Author URI: https://woo.com
* Version: 2.0.7
* WC requires at least: 8.4
* WC tested up to: 8.9
* Requires at least: 6.2
* Requires Plugins: woocommerce
* Tested up to: 6.5
* License: GPLv2 or later
* Text Domain: woocommerce-google-analytics-integration
* Domain Path: languages/
*/
use Automattic\WooCommerce\Utilities\FeaturesUtil;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WC_Google_Analytics_Integration' ) ) {
define( 'WC_GOOGLE_ANALYTICS_INTEGRATION_VERSION', '2.0.7' ); // WRCS: DEFINED_VERSION.
define( 'WC_GOOGLE_ANALYTICS_INTEGRATION_MIN_WC_VER', '6.8' );
// Maybe show the GA Pro notice on plugin activation.
register_activation_hook(
__FILE__,
function () {
WC_Google_Analytics_Integration::get_instance()->maybe_show_ga_pro_notices();
WC_Google_Analytics_Integration::get_instance()->maybe_set_defaults();
}
);
// HPOS compatibility declaration.
add_action(
'before_woocommerce_init',
function () {
if ( class_exists( FeaturesUtil::class ) ) {
FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__ );
FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__ );
FeaturesUtil::declare_compatibility( 'product_block_editor', __FILE__ );
}
}
);
/**
* Google Analytics for WooCommerce main class.
*/
class WC_Google_Analytics_Integration {
/** @var WC_Google_Analytics_Integration $instance Instance of this class. */
protected static $instance = null;
/**
* Initialize the plugin.
*/
public function __construct() {
if ( ! class_exists( 'WooCommerce' ) ) {
add_action( 'admin_notices', [ $this, 'woocommerce_missing_notice' ] );
return;
}
// Load plugin text domain
add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
// Track completed orders and determine whether the GA Pro notice should be displayed.
add_action( 'woocommerce_order_status_completed', array( $this, 'maybe_show_ga_pro_notices' ) );
// Checks which WooCommerce is installed.
if ( class_exists( 'WC_Integration' ) && defined( 'WOOCOMMERCE_VERSION' ) && version_compare( WOOCOMMERCE_VERSION, WC_GOOGLE_ANALYTICS_INTEGRATION_MIN_WC_VER, '>=' ) ) {
include_once 'includes/class-wc-google-analytics.php';
// Register the integration.
add_filter( 'woocommerce_integrations', array( $this, 'add_integration' ) );
} else {
add_action( 'admin_notices', array( $this, 'woocommerce_missing_notice' ) );
}
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_links' ) );
}
/**
* Gets the settings page URL.
*
* @since 1.5.17
*
* @return string Settings URL
*/
public function get_settings_url() {
return add_query_arg(
array(
'page' => 'wc-settings',
'tab' => 'integration',
'section' => 'google_analytics',
),
admin_url( 'admin.php' )
);
}
/**
* Add links on the plugins page (Settings & Support)
*
* @param array $links Default links
* @return array Default + added links
*/
public function plugin_links( $links ) {
$settings_url = $this->get_settings_url();
$support_url = 'https://wordpress.org/support/plugin/woocommerce-google-analytics-integration';
$docs_url = 'https://woo.com/document/google-analytics-integration/?utm_source=wordpress&utm_medium=all-plugins-page&utm_campaign=doc-link&utm_content=woocommerce-google-analytics-integration';
$plugin_links = array(
'<a href="' . esc_url( $settings_url ) . '">' . esc_html__( 'Settings', 'woocommerce-google-analytics-integration' ) . '</a>',
'<a href="' . esc_url( $support_url ) . '">' . esc_html__( 'Support', 'woocommerce-google-analytics-integration' ) . '</a>',
'<a href="' . esc_url( $docs_url ) . '">' . esc_html__( 'Documentation', 'woocommerce-google-analytics-integration' ) . '</a>',
);
return array_merge( $plugin_links, $links );
}
/**
* Return an instance of this class.
*
* @return WC_Google_Analytics_Integration A single instance of this class.
*/
public static function get_instance() {
// If the single instance hasn't been set, set it now.
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Load the plugin text domain for translation.
*/
public function load_plugin_textdomain() {
$locale = apply_filters( 'plugin_locale', get_locale(), 'woocommerce-google-analytics-integration' );
load_textdomain( 'woocommerce-google-analytics-integration', trailingslashit( WP_LANG_DIR ) . 'woocommerce-google-analytics-integration/woocommerce-google-analytics-integration-' . $locale . '.mo' );
load_plugin_textdomain( 'woocommerce-google-analytics-integration', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
}
/**
* WooCommerce fallback notice.
*/
public function woocommerce_missing_notice() {
if ( defined( 'WOOCOMMERCE_VERSION' ) ) {
/* translators: 1 is the required component, 2 the Woocommerce version */
$error = sprintf( __( 'Google Analytics for WooCommerce requires WooCommerce version %1$s or higher. You are using version %2$s', 'woocommerce-google-analytics-integration' ), WC_GOOGLE_ANALYTICS_INTEGRATION_MIN_WC_VER, WOOCOMMERCE_VERSION );
} else {
/* translators: 1 is the required component */
$error = sprintf( __( 'Google Analytics for WooCommerce requires WooCommerce version %1$s or higher.', 'woocommerce-google-analytics-integration' ), WC_GOOGLE_ANALYTICS_INTEGRATION_MIN_WC_VER );
}
echo '<div class="error"><p><strong>' . wp_kses_post( $error ) . '</strong></p></div>';
}
/**
* Add a new integration to WooCommerce.
*
* @param array $integrations WooCommerce integrations.
* @return array Google Analytics for WooCommerce added.
*/
public function add_integration( $integrations ) {
$integrations[] = 'WC_Google_Analytics';
return $integrations;
}
/**
* Get Google Analytics Integration
*
* @since 1.5.17
*
* @return WC_Google_Analytics The Google Analytics integration.
*/
public static function get_integration() {
return \WooCommerce::instance()->integrations->get_integration( 'google_analytics' );
}
/**
* Logic for Google Analytics Pro notices.
*/
public function maybe_show_ga_pro_notices() {
// Notice was already shown
if ( ! class_exists( 'WooCommerce' ) || get_option( 'woocommerce_google_analytics_pro_notice_shown', false ) ) {
return;
}
$completed_orders = wc_orders_count( 'completed' );
// Only show the notice if there are 10 <= completed orders <= 100.
if ( $completed_orders < 10 || $completed_orders > 100 ) {
update_option( 'woocommerce_google_analytics_pro_notice_shown', true );
return;
}
$notice_html = '<strong>' . esc_html__( 'Get detailed insights into your sales with Google Analytics Pro', 'woocommerce-google-analytics-integration' ) . '</strong><br><br>';
/* translators: 1: href link to GA pro */
$notice_html .= sprintf( __( 'Add advanced tracking for your sales funnel, coupons and more. [<a href="%s" target="_blank">Learn more</a> &gt;]', 'woocommerce-google-analytics-integration' ), 'https://woo.com/products/woocommerce-google-analytics-pro/?utm_source=woocommerce-google-analytics-integration&utm_medium=product&utm_campaign=google%20analytics%20free%20to%20pro%20extension%20upsell' );
WC_Admin_Notices::add_custom_notice( 'woocommerce_google_analytics_pro_notice', $notice_html );
update_option( 'woocommerce_google_analytics_pro_notice_shown', true );
}
/**
* Set default options during activation if no settings exist
*
* @since 2.0.0
*
* @return void
*/
public function maybe_set_defaults() {
$settings_key = 'woocommerce_google_analytics_settings';
if ( false === get_option( $settings_key, false ) ) {
update_option(
$settings_key,
array(
'ga_product_identifier' => 'product_id',
)
);
}
}
/**
* Get the path to something in the plugin dir.
*
* @param string $end End of the path.
* @return string
*/
public function path( $end = '' ) {
return untrailingslashit( __DIR__ ) . $end;
}
/**
* Get the URL to something in the plugin dir.
*
* @param string $end End of the URL.
*
* @return string
*/
public function url( $end = '' ) {
return untrailingslashit( plugin_dir_url( plugin_basename( __FILE__ ) ) ) . $end;
}
/**
* Get the URL to something in the plugin JS assets build dir.
*
* @param string $end End of the URL.
*
* @return string
*/
public function get_js_asset_url( $end = '' ) {
return $this->url( '/assets/js/build/' . $end );
}
/**
* Get the path to something in the plugin JS assets build dir.
*
* @param string $end End of the path.
* @return string
*/
public function get_js_asset_path( $end = '' ) {
return $this->path( '/assets/js/build/' . $end );
}
/**
* Gets the asset.php generated file for an asset name.
*
* @param string $asset_name The name of the asset to get the file from.
* @return array The asset file. Or an empty array if the file doesn't exist.
*/
public function get_js_asset_file( $asset_name ) {
try {
// Exclusion reason: No reaching any user input
// nosemgrep audit.php.lang.security.file.inclusion-arg
return require $this->get_js_asset_path( $asset_name . '.asset.php' );
} catch ( Exception $e ) {
return [];
}
}
/**
* Gets the dependencies for an assets based on its asset.php generated file.
*
* @param string $asset_name The name of the asset to get the dependencies from.
* @param array $extra_dependencies Array containing extra dependencies to include in the dependency array.
*
* @return array The dependencies array. Empty array if no dependencies.
*/
public function get_js_asset_dependencies( $asset_name, $extra_dependencies = array() ) {
$script_assets = $this->get_js_asset_file( $asset_name );
$dependencies = $script_assets['dependencies'] ?? [];
return array_unique( array_merge( $dependencies, $extra_dependencies ) );
}
/**
* Gets the version for an assets based on its asset.php generated file.
*
* @param string $asset_name The name of the asset to get the version from.
* @return string|false The version. False in case no version is found.
*/
public function get_js_asset_version( $asset_name ) {
$script_assets = $this->get_js_asset_file( $asset_name );
return $script_assets['version'] ?? false;
}
}
add_action( 'plugins_loaded', array( 'WC_Google_Analytics_Integration', 'get_instance' ), 0 );
}