update plugins
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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§ion=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' )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user