diff --git a/assets/src/js/cookies.js b/assets/src/js/cookies.js index 244c375466..ac47f607d1 100644 --- a/assets/src/js/cookies.js +++ b/assets/src/js/cookies.js @@ -10,7 +10,7 @@ export const setupCookies = () => { const NECESSARY_ANALYTICAL = '3'; const NECESSARY_ANALYTICAL_MARKETING = '4'; - const ALL_COOKIES = ENABLE_ANALYTICAL_COOKIES ? NECESSARY_ANALYTICAL_MARKETING : NECESSARY_MARKETING; + const ALL_COOKIES = NECESSARY_ANALYTICAL_MARKETING; function gtag() { dataLayer.push(arguments); @@ -95,7 +95,7 @@ export const setupCookies = () => { ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted', - ...ENABLE_ANALYTICAL_COOKIES && {analytics_storage: 'granted'}, + analytics_storage: 'granted', }); } @@ -156,12 +156,20 @@ export const setupCookies = () => { // Update ad storage and analytics storage if Google Consent Mode is enabled if (ENABLE_GOOGLE_CONSENT_MODE) { - updateGoogleConsent({ - ad_storage: marketingCookiesChecked ? 'granted' : 'denied', - ad_user_data: marketingCookiesChecked ? 'granted' : 'denied', - ad_personalization: marketingCookiesChecked ? 'granted' : 'denied', - ...ENABLE_ANALYTICAL_COOKIES && {analytics_storage: analyticalCookiesChecked ? 'granted' : 'denied'}, - }); + if (ENABLE_ANALYTICAL_COOKIES) { + updateGoogleConsent({ + ad_storage: marketingCookiesChecked ? 'granted' : 'denied', + ad_user_data: marketingCookiesChecked ? 'granted' : 'denied', + ad_personalization: marketingCookiesChecked ? 'granted' : 'denied', + analytics_storage: analyticalCookiesChecked ? 'granted' : 'denied', + }); + } else { + updateGoogleConsent({ + ad_storage: marketingCookiesChecked ? 'granted' : 'denied', + ad_user_data: marketingCookiesChecked ? 'granted' : 'denied', + ad_personalization: marketingCookiesChecked ? 'granted' : 'denied', + }); + } } hideCookiesBox(); @@ -188,4 +196,48 @@ export const setupCookies = () => { const rejectAllCookiesButtons = [...document.querySelectorAll('.reject-all-cookies')]; rejectAllCookiesButtons.forEach(rejectAllCookiesButton => rejectAllCookiesButton.onclick = rejectAllCookies); + + const getConsentModeValues = () => { + const consentValues = { + analytics_storage: null, + ad_user_data: null, + ad_storage: null, + ad_personalization: null, + }; + + if (Array.isArray(window.dataLayer)) { + // Iterate through the events history in dataLayer and find most recent consent values. + for (let i = 0; i < window.dataLayer.length; i++) { + const event = window.dataLayer[i]; + + if (event.event === 'defaultConsent' || event.event === 'updateConsent') { + if (event.analytics_storage !== undefined) { + consentValues.analytics_storage = event.analytics_storage; + } + if (event.ad_user_data !== undefined) { + consentValues.ad_user_data = event.ad_user_data; + } + if (event.ad_storage !== undefined) { + consentValues.ad_storage = event.ad_storage; + } + if (event.ad_personalization !== undefined) { + consentValues.ad_personalization = event.ad_personalization; + } + } + } + } + + return consentValues; + }; + + // Set the gp_user_id cookie when the event is triggered, but check for consent first. + document.addEventListener('gp_user_id_set', event => { + if (event.detail.hasOwnProperty('gp_user_id') && event.detail.gp_user_id !== '') { + const consentModeValues = getConsentModeValues(); + + if (consentModeValues.analytics_storage === 'granted') { + createCookie('gp_user_id', event.detail.gp_user_id, 365); + } + } + }); }; diff --git a/src/GravityFormsExtensions.php b/src/GravityFormsExtensions.php index 8a860101c8..d59f367cd5 100644 --- a/src/GravityFormsExtensions.php +++ b/src/GravityFormsExtensions.php @@ -311,6 +311,50 @@ public function p4_gf_confirmation_settings(array $fields): array return $fields; } + /** + * Find an email address in a form entry and return a SHA-256 hash of it. The returned hash is base64 encoded + * and any '/' characters are removed. + * + * @param array $form The form setting. + * @param array $entry A form entry. + * + * @return string The hashed email address or empty string if no email address is found. + */ + public function p4_gf_get_email_hash(array $form, array $entry): string + { + $email_address = ''; + + // Find the first email field in the form and extract the email address + foreach ($form["fields"] as $i => $field) { + if (get_class($field) === "GF_Field_Email") { + $email_address = $entry[$field["id"]]; + break; + } + } + + if (empty($email_address)) { + // Find any email address as a value in the form entry, in any type of form field + foreach ($entry as $key => $value) { + if (is_numeric($key) && filter_var(trim($value), FILTER_VALIDATE_EMAIL)) { + $email_address = trim($value); + break; + } + } + } + + if (!empty($email_address)) { + // Hash and base64 encode the email using SHA-256 + $hashed_email = base64_encode(hash('sha256', $email_address, true)); + + // Remove '/' characters if present in Base64 encoding + $hashed_email = str_replace('/', '', $hashed_email); + + return $hashed_email; + } + + return ''; + } + /** * * Return custom confirmation message for Gravity Forms. @@ -342,12 +386,15 @@ public function p4_gf_custom_confirmation($confirmation, $form, $entry) } } + $email_hash = $this->p4_gf_get_email_hash($form, $entry); + $event_parameters = [ "event" => "formSubmission", "formID" => $form['id'], "formPlugin" => "Gravity Form", "gGoal" => ($form['p4_gf_type'] ?? self::DEFAULT_GF_TYPE), "formTitle" => $form['title'], + "gpUserId" => $email_hash, "userEmail" => $userEmail, "eventTimeout" => 2000, ]; @@ -362,6 +409,10 @@ public function p4_gf_custom_confirmation($confirmation, $form, $entry) window.dataLayer.push(push_data); + const gp_user_id_event = new CustomEvent("gp_user_id_set", {"detail": + {"gp_user_id": "' . $email_hash . '" }}); + document.dispatchEvent(gp_user_id_event); + // Disable GFTrackEvent (GFTrackEvent belongs to Gravity Forms Google Analytics Add-On) window.parent.gfgaTagManagerEventSent = true; '; @@ -701,12 +752,15 @@ public function p4_gf_custom_confirmation_redirect($confirmation, $form, $entry) $options = get_option('planet4_options'); $gtm_id = $options['google_tag_manager_identifier']; + $email_hash = $this->p4_gf_get_email_hash($form, $entry); + $event_parameters = [ "event" => "formSubmission", "formID" => $form['id'], "formPlugin" => "Gravity Form", "gGoal" => ($form['p4_gf_type'] ?? self::DEFAULT_GF_TYPE), "formTitle" => $form['title'], + "gpUserId" => $email_hash, "userEmail" => $userEmail, "eventTimeout" => 2000, ]; @@ -732,6 +786,10 @@ public function p4_gf_custom_confirmation_redirect($confirmation, $form, $entry) push_data = Object.assign(push_data, event_parameters); window.dataLayer.push(push_data); + + const gp_user_id_event = new CustomEvent("gp_user_id_set", {"detail": + {"gp_user_id": "' . $email_hash . '" }}); + document.dispatchEvent(gp_user_id_event); } else { // Redirect latest after two seconds. // This is a failsafe in case the request to tag manager is blocked. diff --git a/src/MasterSite.php b/src/MasterSite.php index 0acbc4aaee..8e4eb5c87a 100644 --- a/src/MasterSite.php +++ b/src/MasterSite.php @@ -661,6 +661,14 @@ function ($item) { $context['google_tag_value'] = $options['google_tag_manager_identifier'] ?? ''; $context['google_tag_domain'] = !empty($options['google_tag_manager_domain']) ? $options['google_tag_manager_domain'] : 'www.googletagmanager.com'; + $context['consent_default_analytics_storage'] = + planet4_get_option('consent_default_analytics_storage') ?? 'denied'; + $context['consent_default_ad_storage'] = + planet4_get_option('consent_default_ad_storage') ?? 'denied'; + $context['consent_default_ad_user_data'] = + planet4_get_option('consent_default_ad_user_data') ?? 'denied'; + $context['consent_default_ad_personalization'] = + planet4_get_option('consent_default_ad_personalization') ?? 'denied'; $context['ab_hide_selector'] = $options['ab_hide_selector'] ?? null; $context['facebook_page_id'] = $options['facebook_page_id'] ?? ''; $context['preconnect_domains'] = []; diff --git a/src/Settings.php b/src/Settings.php index df8c2c47b5..03fcc662b5 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -310,6 +310,68 @@ public function __construct() 'id' => 'enable_reject_all_cookies', 'type' => 'checkbox', ], + [ + 'name' => __('Enable Google Consent Mode', 'planet4-master-theme-backend'), + 'desc' => __("Enabling the Consent Mode will affect your setup in Google Tag Manager. The Consent Mode will prevent tags with built-in consent checks (eg. Google Analytics) from running before the user's consent is granted.", 'planet4-master-theme-backend'), + 'id' => 'enable_google_consent_mode', + 'type' => 'checkbox', + ], + [ + 'name' => __('Consent default: analytics_storage', 'planet4-master-theme-backend'), + 'desc' => __( + 'The default value for analytics_storage consent before visitors make their choice in the cookies box (Google Consent Mode V2).', + 'planet4-master-theme-backend' + ), + 'id' => 'consent_default_analytics_storage', + 'type' => 'select', + 'default' => 'denied', + 'options' => [ + 'denied' => __('Denied', 'planet4-master-theme-backend'), + 'granted' => __('Granted', 'planet4-master-theme-backend'), + ], + ], + [ + 'name' => __('Consent default: ad_storage', 'planet4-master-theme-backend'), + 'desc' => __( + 'The default value for ad_storage consent before visitors make their choice in the cookies box (Google Consent Mode V2).', + 'planet4-master-theme-backend' + ), + 'id' => 'consent_default_ad_storage', + 'type' => 'select', + 'default' => 'denied', + 'options' => [ + 'denied' => __('Denied', 'planet4-master-theme-backend'), + 'granted' => __('Granted', 'planet4-master-theme-backend'), + ], + ], + [ + 'name' => __('Consent default: ad_user_data', 'planet4-master-theme-backend'), + 'desc' => __( + 'The default value for ad_user_data consent before visitors make their choice in the cookies box (Google Consent Mode V2).', + 'planet4-master-theme-backend' + ), + 'id' => 'consent_default_ad_user_data', + 'type' => 'select', + 'default' => 'denied', + 'options' => [ + 'denied' => __('Denied', 'planet4-master-theme-backend'), + 'granted' => __('Granted', 'planet4-master-theme-backend'), + ], + ], + [ + 'name' => __('Consent default: ad_personalization', 'planet4-master-theme-backend'), + 'desc' => __( + 'The default value for ad_personalization consent before visitors make their choice in the cookies box (Google Consent Mode V2).', + 'planet4-master-theme-backend' + ), + 'id' => 'consent_default_ad_personalization', + 'type' => 'select', + 'default' => 'denied', + 'options' => [ + 'denied' => __('Denied', 'planet4-master-theme-backend'), + 'granted' => __('Granted', 'planet4-master-theme-backend'), + ], + ], ], ], 'planet4_settings_social' => [ @@ -420,12 +482,6 @@ public function __construct() 'id' => 'analytics_local_google_sheet_id', 'type' => 'text', ], - [ - 'name' => __('Enable Google Consent Mode', 'planet4-master-theme-backend'), - 'desc' => __("Enabling the Consent Mode will affect your setup in Google Tag Manager. The Consent Mode will prevent tags with built-in consent checks (eg. Google Analytics) from running before the user's consent is granted.", 'planet4-master-theme-backend'), - 'id' => 'enable_google_consent_mode', - 'type' => 'checkbox', - ], // New IA special pages. [ 'name' => __('Select "Get Informed" page', 'planet4-master-theme-backend'), diff --git a/templates/blocks/google_tag_manager.twig b/templates/blocks/google_tag_manager.twig index 85f8e7af22..be4cb776a9 100644 --- a/templates/blocks/google_tag_manager.twig +++ b/templates/blocks/google_tag_manager.twig @@ -2,6 +2,10 @@