From 7998c838cfade8b8b43b6fe11d9095729a208bff Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 16 May 2024 13:35:42 +0100 Subject: [PATCH 01/14] Register extension with WP Consent API --- includes/class-wc-google-analytics.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/class-wc-google-analytics.php b/includes/class-wc-google-analytics.php index 7bd02f58..59c9d141 100644 --- a/includes/class-wc-google-analytics.php +++ b/includes/class-wc-google-analytics.php @@ -58,6 +58,9 @@ public function __construct() { // utm_nooverride parameter for Google AdWords add_filter( 'woocommerce_get_return_url', array( $this, 'utm_nooverride' ) ); + // Mark extension as compatible with WP Consent API + add_filter( 'wp_consent_api_registered_' . plugin_basename( __FILE__ ), '__return_true' ); + // 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( From 57ec41e6337a02c38e7c85873b646d2bafc929f2 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 16 May 2024 14:38:26 +0100 Subject: [PATCH 02/14] Set wait_for_update on default consent event --- includes/class-wc-google-gtag-js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 3a95a49e..3d3ffb24 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -94,7 +94,7 @@ private function register_scripts(): void { function %2$s(){dataLayer.push(arguments);} // Set up default consent state. for ( const mode of %4$s || [] ) { - %2$s( "consent", "default", mode ); + %2$s( "consent", "default", { ...mode, "wait_for_update": 500 } ); } %2$s("js", new Date()); %2$s("set", "developer_id.%3$s", true); From d1e35928b4e2521a8f4e54c6804120169005298d Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 16 May 2024 14:58:43 +0100 Subject: [PATCH 03/14] Set current consent state when initializing tracking --- assets/js/src/index.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 1ef34fb8..85edcfb8 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -15,7 +15,37 @@ if ( window.ga4w ) { window.addEventListener( 'load', warnIfDataMissing ); } } + +const consentMap = { + statistics: [ + 'analytics_storage', + ], + marketing: [ + 'ad_storage', + 'ad_user_data', + 'ad_personalization', + ], +}; + function initializeTracking() { + if ( typeof wp_has_consent === 'function' ) { + window.wp_consent_type = 'optin'; + + const consentState = {}; + + for ( const [ category, types ] of Object.entries( consentMap ) ) { + if ( wp_has_consent( category ) ) { + types.forEach( type => { + consentState[ type ] = 'granted'; + } ); + } + } + + if ( Object.keys( consentState ).length > 0 ) { + gtag( 'consent', 'update', consentState ); + } + } + const getEventHandler = setupEventHandlers( window.ga4w.settings ); classicTracking( getEventHandler, window.ga4w.data ); From 3e10ac4e62b425283ac75510ee2c66ef49efae46 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Thu, 16 May 2024 15:01:03 +0100 Subject: [PATCH 04/14] Listen for consent state changes --- assets/js/src/index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 85edcfb8..d9ed7260 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -60,3 +60,20 @@ function warnIfDataMissing() { ); } } + +document.addEventListener( 'wp_listen_for_consent_change', function ( event ) { + const consentUpdate = {}; + + const types = consentMap[ Object.keys( event.detail )[0] ]; + const state = Object.values( event.detail )[0] === 'allow' ? 'granted' : 'deny'; + + if ( types !== undefined ) { + types.forEach( type => { + consentUpdate[ type ] = state; + } ); + + if ( Object.keys( consentUpdate ).length > 0 ) { + gtag( 'consent', 'update', consentUpdate ); + } + } +}); From e2006427bc7131db59108e82b96a74a02c4d71ee Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Fri, 17 May 2024 18:07:39 +0100 Subject: [PATCH 05/14] Fix linting errors --- assets/js/src/index.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index d9ed7260..648b5563 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -17,32 +17,26 @@ if ( window.ga4w ) { } const consentMap = { - statistics: [ - 'analytics_storage', - ], - marketing: [ - 'ad_storage', - 'ad_user_data', - 'ad_personalization', - ], + statistics: [ 'analytics_storage' ], + marketing: [ 'ad_storage', 'ad_user_data', 'ad_personalization' ], }; function initializeTracking() { - if ( typeof wp_has_consent === 'function' ) { + if ( typeof wp_has_consent === 'function' ) { // eslint-disable-line camelcase window.wp_consent_type = 'optin'; const consentState = {}; for ( const [ category, types ] of Object.entries( consentMap ) ) { - if ( wp_has_consent( category ) ) { - types.forEach( type => { + if ( wp_has_consent( category ) ) { // eslint-disable-line camelcase, no-undef + types.forEach( ( type ) => { consentState[ type ] = 'granted'; } ); } } if ( Object.keys( consentState ).length > 0 ) { - gtag( 'consent', 'update', consentState ); + gtag( 'consent', 'update', consentState ); // eslint-disable-line no-undef } } @@ -64,16 +58,17 @@ function warnIfDataMissing() { document.addEventListener( 'wp_listen_for_consent_change', function ( event ) { const consentUpdate = {}; - const types = consentMap[ Object.keys( event.detail )[0] ]; - const state = Object.values( event.detail )[0] === 'allow' ? 'granted' : 'deny'; + const types = consentMap[ Object.keys( event.detail )[ 0 ] ]; + const state = + Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'deny'; if ( types !== undefined ) { - types.forEach( type => { + types.forEach( ( type ) => { consentUpdate[ type ] = state; } ); if ( Object.keys( consentUpdate ).length > 0 ) { - gtag( 'consent', 'update', consentUpdate ); + gtag( 'consent', 'update', consentUpdate ); // eslint-disable-line no-undef } } -}); +} ); From 4c3a0362f33b32f755884b0c03c338f7a1f71b1a Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Fri, 17 May 2024 18:10:41 +0100 Subject: [PATCH 06/14] Fix linting errors --- assets/js/src/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 648b5563..7e6f0e55 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -22,13 +22,15 @@ const consentMap = { }; function initializeTracking() { - if ( typeof wp_has_consent === 'function' ) { // eslint-disable-line camelcase + // eslint-disable-next-line camelcase + if ( typeof wp_has_consent === 'function' ) { window.wp_consent_type = 'optin'; const consentState = {}; for ( const [ category, types ] of Object.entries( consentMap ) ) { - if ( wp_has_consent( category ) ) { // eslint-disable-line camelcase, no-undef + // eslint-disable-next-line camelcase, no-undef + if ( wp_has_consent( category ) ) { types.forEach( ( type ) => { consentState[ type ] = 'granted'; } ); From 4360ccec31ec83c1e9d239e5b498075836b46183 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Wed, 22 May 2024 18:20:08 +0100 Subject: [PATCH 07/14] Move WP Consent API integration to separate file --- assets/js/src/index.js | 40 ++------------ assets/js/src/integrations/wp-consent-api.js | 56 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 37 deletions(-) create mode 100644 assets/js/src/integrations/wp-consent-api.js diff --git a/assets/js/src/index.js b/assets/js/src/index.js index 7e6f0e55..c500ef6e 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -1,6 +1,7 @@ import { setupEventHandlers } from './tracker'; import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; +import { setCurrentConsentState, addConsentStateChangeEventListener } from './integrations/wp-consent-api'; // Wait for 'ga4w:ready' event if `window.ga4w` is not there yet. if ( window.ga4w ) { @@ -22,25 +23,8 @@ const consentMap = { }; function initializeTracking() { - // eslint-disable-next-line camelcase - if ( typeof wp_has_consent === 'function' ) { - window.wp_consent_type = 'optin'; - - const consentState = {}; - - for ( const [ category, types ] of Object.entries( consentMap ) ) { - // eslint-disable-next-line camelcase, no-undef - if ( wp_has_consent( category ) ) { - types.forEach( ( type ) => { - consentState[ type ] = 'granted'; - } ); - } - } - - if ( Object.keys( consentState ).length > 0 ) { - gtag( 'consent', 'update', consentState ); // eslint-disable-line no-undef - } - } + setCurrentConsentState( window.ga4w.settings ); + addConsentStateChangeEventListener( window.ga4w.settings ); const getEventHandler = setupEventHandlers( window.ga4w.settings ); @@ -56,21 +40,3 @@ function warnIfDataMissing() { ); } } - -document.addEventListener( 'wp_listen_for_consent_change', function ( event ) { - const consentUpdate = {}; - - const types = consentMap[ Object.keys( event.detail )[ 0 ] ]; - const state = - Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'deny'; - - if ( types !== undefined ) { - types.forEach( ( type ) => { - consentUpdate[ type ] = state; - } ); - - if ( Object.keys( consentUpdate ).length > 0 ) { - gtag( 'consent', 'update', consentUpdate ); // eslint-disable-line no-undef - } - } -} ); diff --git a/assets/js/src/integrations/wp-consent-api.js b/assets/js/src/integrations/wp-consent-api.js new file mode 100644 index 00000000..f77ead74 --- /dev/null +++ b/assets/js/src/integrations/wp-consent-api.js @@ -0,0 +1,56 @@ +const consentMap = { + statistics: [ 'analytics_storage' ], + marketing: [ 'ad_storage', 'ad_user_data', 'ad_personalization' ], +}; + +export const setCurrentConsentState = ( { + tracker_function_name: trackerFunctionName, +} ) => { + // eslint-disable-next-line camelcase + if ( typeof wp_has_consent === 'function' ) { + if ( window.wp_consent_type === undefined ) { + window.wp_consent_type = 'optin'; + } + + const consentState = {}; + + for ( const [ category, types ] of Object.entries( consentMap ) ) { + // eslint-disable-next-line camelcase, no-undef + if ( wp_has_consent( category ) ) { + types.forEach( ( type ) => { + consentState[ type ] = 'granted'; + } ); + } + } + + if ( Object.keys( consentState ).length > 0 ) { + window[ trackerFunctionName ]( 'consent', 'update', consentState ); + } + } +}; + +export const addConsentStateChangeEventListener = ( { + tracker_function_name: trackerFunctionName, +} ) => { + document.addEventListener( 'wp_listen_for_consent_change', ( event ) => { + const consentUpdate = {}; + + const types = consentMap[ Object.keys( event.detail )[ 0 ] ]; + const state = + Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'deny'; + + if ( types !== undefined ) { + types.forEach( ( type ) => { + consentUpdate[ type ] = state; + } ); + + if ( Object.keys( consentUpdate ).length > 0 ) { + window[ trackerFunctionName ]( + 'consent', + 'update', + consentUpdate + ); + } + } + } ); +}; From 4b9cc2cb95ef6bf878b00db81605e8c5a1756290 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Wed, 22 May 2024 18:21:25 +0100 Subject: [PATCH 08/14] Support overwriting wait_for_update via filter --- includes/class-wc-google-gtag-js.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-google-gtag-js.php b/includes/class-wc-google-gtag-js.php index 3d3ffb24..dbb52028 100644 --- a/includes/class-wc-google-gtag-js.php +++ b/includes/class-wc-google-gtag-js.php @@ -94,7 +94,7 @@ private function register_scripts(): void { function %2$s(){dataLayer.push(arguments);} // Set up default consent state. for ( const mode of %4$s || [] ) { - %2$s( "consent", "default", { ...mode, "wait_for_update": 500 } ); + %2$s( "consent", "default", { "wait_for_update": 500, ...mode } ); } %2$s("js", new Date()); %2$s("set", "developer_id.%3$s", true); From 10c2af730145ec370fea1951fa636bcf8eaa382a Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Wed, 22 May 2024 18:31:15 +0100 Subject: [PATCH 09/14] Add comment --- assets/js/src/integrations/wp-consent-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/src/integrations/wp-consent-api.js b/assets/js/src/integrations/wp-consent-api.js index f77ead74..ee78ee57 100644 --- a/assets/js/src/integrations/wp-consent-api.js +++ b/assets/js/src/integrations/wp-consent-api.js @@ -6,7 +6,7 @@ const consentMap = { export const setCurrentConsentState = ( { tracker_function_name: trackerFunctionName, } ) => { - // eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase -- `wp_has_consent` is defined by the WP Consent API plugin. if ( typeof wp_has_consent === 'function' ) { if ( window.wp_consent_type === undefined ) { window.wp_consent_type = 'optin'; From 18a696ef93643b9e2cf214e6d599c017b7ef2c0b Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Wed, 22 May 2024 19:07:54 +0100 Subject: [PATCH 10/14] Fix linting errors --- assets/js/src/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index c500ef6e..a7694844 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -1,7 +1,10 @@ import { setupEventHandlers } from './tracker'; import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; -import { setCurrentConsentState, addConsentStateChangeEventListener } from './integrations/wp-consent-api'; +import { + setCurrentConsentState, + addConsentStateChangeEventListener +} from './integrations/wp-consent-api'; // Wait for 'ga4w:ready' event if `window.ga4w` is not there yet. if ( window.ga4w ) { @@ -17,11 +20,6 @@ if ( window.ga4w ) { } } -const consentMap = { - statistics: [ 'analytics_storage' ], - marketing: [ 'ad_storage', 'ad_user_data', 'ad_personalization' ], -}; - function initializeTracking() { setCurrentConsentState( window.ga4w.settings ); addConsentStateChangeEventListener( window.ga4w.settings ); From c0a7c69925ee8e424e886ecd844a5da22f0a2bd2 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Wed, 22 May 2024 19:11:37 +0100 Subject: [PATCH 11/14] Add trailing comma --- assets/js/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/src/index.js b/assets/js/src/index.js index a7694844..006e30b4 100644 --- a/assets/js/src/index.js +++ b/assets/js/src/index.js @@ -3,7 +3,7 @@ import { classicTracking } from './integrations/classic'; import { blocksTracking } from './integrations/blocks'; import { setCurrentConsentState, - addConsentStateChangeEventListener + addConsentStateChangeEventListener, } from './integrations/wp-consent-api'; // Wait for 'ga4w:ready' event if `window.ga4w` is not there yet. From ff8ef0ac6138932aa0af099b0caa942106babce3 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Tue, 28 May 2024 11:28:16 +0100 Subject: [PATCH 12/14] Fix value --- assets/js/src/integrations/wp-consent-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/src/integrations/wp-consent-api.js b/assets/js/src/integrations/wp-consent-api.js index ee78ee57..2c0297b0 100644 --- a/assets/js/src/integrations/wp-consent-api.js +++ b/assets/js/src/integrations/wp-consent-api.js @@ -37,7 +37,7 @@ export const addConsentStateChangeEventListener = ( { const types = consentMap[ Object.keys( event.detail )[ 0 ] ]; const state = - Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'deny'; + Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'denied'; if ( types !== undefined ) { types.forEach( ( type ) => { From 540e75470cb8cd1d126a78dcb06e26f5da0bb8f4 Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Tue, 28 May 2024 11:29:11 +0100 Subject: [PATCH 13/14] Add comment --- assets/js/src/integrations/wp-consent-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/src/integrations/wp-consent-api.js b/assets/js/src/integrations/wp-consent-api.js index 2c0297b0..c175d7f3 100644 --- a/assets/js/src/integrations/wp-consent-api.js +++ b/assets/js/src/integrations/wp-consent-api.js @@ -15,7 +15,7 @@ export const setCurrentConsentState = ( { const consentState = {}; for ( const [ category, types ] of Object.entries( consentMap ) ) { - // eslint-disable-next-line camelcase, no-undef + // eslint-disable-next-line camelcase, no-undef -- `wp_has_consent` is defined by the WP Consent API plugin. if ( wp_has_consent( category ) ) { types.forEach( ( type ) => { consentState[ type ] = 'granted'; From 86e43c7687b9e7892fd028a53731f1d63b83b39a Mon Sep 17 00:00:00 2001 From: Martyn Jones Date: Tue, 28 May 2024 14:09:06 +0100 Subject: [PATCH 14/14] Check for presence of cookie before triggering consent update --- assets/js/src/integrations/wp-consent-api.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/assets/js/src/integrations/wp-consent-api.js b/assets/js/src/integrations/wp-consent-api.js index c175d7f3..d2176eb2 100644 --- a/assets/js/src/integrations/wp-consent-api.js +++ b/assets/js/src/integrations/wp-consent-api.js @@ -15,10 +15,19 @@ export const setCurrentConsentState = ( { const consentState = {}; for ( const [ category, types ] of Object.entries( consentMap ) ) { - // eslint-disable-next-line camelcase, no-undef -- `wp_has_consent` is defined by the WP Consent API plugin. - if ( wp_has_consent( category ) ) { + if ( + // eslint-disable-next-line camelcase, no-undef -- `consent_api_get_cookie` is defined by the WP Consent API plugin. + consent_api_get_cookie( + window.consent_api.cookie_prefix + '_' + category + ) !== '' + ) { + // eslint-disable-next-line camelcase, no-undef -- `wp_has_consent` is defined by the WP Consent API plugin. + const hasConsent = wp_has_consent( category ) + ? 'granted' + : 'denied'; + types.forEach( ( type ) => { - consentState[ type ] = 'granted'; + consentState[ type ] = hasConsent; } ); } } @@ -37,7 +46,9 @@ export const addConsentStateChangeEventListener = ( { const types = consentMap[ Object.keys( event.detail )[ 0 ] ]; const state = - Object.values( event.detail )[ 0 ] === 'allow' ? 'granted' : 'denied'; + Object.values( event.detail )[ 0 ] === 'allow' + ? 'granted' + : 'denied'; if ( types !== undefined ) { types.forEach( ( type ) => {