We’re using Piwik PRO Consent & Tag Managers, and we are running ads with Google Ads. We’re using the built-in Google Ads conversion & remarketing tags.
I don’t think Piwik PRO is a certified Google CMP partner: https://cmppartnerprogram.withgoogle.com/. How can I implement Google Consent Mode using Piwik PRO Consent & Tag Managers?
By following gtag.js method, you should be able to install Consent Mode with Piwik PRO Tag & Consent Managers. Make sure you trigger the tags in the correct order: default values first.
I did, but I think that following Google’s guidelines is better. The main reason is that it clearly invites you to set default values to “denied” first. It is not the case in this Piwik PRO’s tutorial: it’s only update commands.
Additionally, Google’s guidelines integrate the new parameters “ad_user_data” and “ad_personalization”. I think Piwik PRO documentation would benefit from a little update.
Hi @anthonybartczak ,
The first step to integrate Google Consent Mode v2, with all the denied default.
It must be on the webpages code, or can it be a tag in Tag Manager ?
Thanks for your help,
In my opinion (and Google’s documentation mentions this method) it’s best to embed the defaults directly in your website, before any tag managment or tracking tool is loaded. It’s the safest way because it skips potential issues with 3rd party tools.
But doesn’t that apply only to the rare cases when somebody first gives their consent (or even rarer, when they update consent)?
=> If we only use that stg.consentDecisionMade Event, there will be no Consent given for any Pageviews where there was no interaction with the Consent Manager (=99% of Pageviews)…
The documentation is missing the crucial info that the update command needs to be fired on every pageview (or that the Consent “default” settings need to reflect the user’s current consent state on every page).
Last question:
When I try to get {{ Consents }} via a Custom HTML Tag, it always refers to window.sevenTag.getVariableValue('Consents', 3). This 3 refers to t.variables[3][“Consents”] which sometimes contains a filled Consents object, but also sometimes not…
E.g. here (trying to receive “Consents” with the stg.consentDecisionMade Event: Consents.current_state is just an empty object → setting Consent Mode via the googleConsentModeMapping function from the documentation fails (everything gets “denied”…
Why does a reference to a Variable not always get the latest version of it? (=which would contain the correct values)
Looking at this again, it seems like PP TM, when 'eval’ing the Custom HTML Tag (with all comments, yikes, performance anyone?!), injects the INDEX of the data layer event that triggered the current tag into the window.sevenTag.getVariableValue('Consents', INDEX);call.
So if a tag has multiple triggers (like stg.pageView OR stg.consentDecisionMade, the value counts that was in the data layer when the tag was first called…
So if stg.pageView is the 3rd event (INDEX = 3) and stg.consentDecisionMade the 22nd (INDEX = 22), Piwik TM will still get the data of the Consents variable as it existed when the 3rd event happened…
So when somebody updates their consent level, this will never make it to the Google Consent Mode update as it will “update” it based on data layer INDEX 3 again (when Consent still did not exist)…
That is totally not how I expect a Variable reference to work. In GTM, it always takes the last value… I will have to re-visit quite some code now that I found this out…
I will replace Variable reference by this manual call now which always gets the latest version of a Variable:
“It’s not about ability, it’s just recommended.”
=> I understand that it is only your recommendation, but I think a TMS is precisely there to avoid such stuff having to go on a website, so it is weird to get such a recommendation from a TMS vendor…
“And also the page view event”
=> What I wrote is: If I use both pageview and consentDecisionMade, the content of the Consents variable that is evaluated in the Custom HTML tag is always based on the first event on the page that triggered the tag (=the pageview, when we did not have consent yet).
This is what finally works now (took me 4 hours to build sth that should take 30 minutes):
One Custom HTML Tag, fired upon pageView and stg.consentDecisionMade:
<script>
// fired this upon stg.pageView and stg.consentDecisionMade
window.gtag = window.gtag || function(){dataLayer.push(arguments)};
/*
Validate: Check Google Ads Tag and look for these parameters:
- gcs: G110 => Ads granted, Analytics denied
- gcd: 13r3q3r3r5 => denied by default and granted after update, except analytics storage
*/
if (!window.googleConsentModeMapping) { // initialize the function only once per page
function googleConsentModeMapping(consents) {
var consentLevels = window.sevenTag.getVariableValue('Consents', dataLayer.length-1);
TMSHelper.console("Consent Levels: ", consentLevels);
if (consentLevels) {
var consObj = {};
Object.keys(consents).forEach(function(consent) {
consObj[consent] = (consentLevels.current_state[consents[consent]] === 1) ? 'granted' : 'denied';
});
gtag('consent', 'update', consObj);
}
}
// set default consent values for the page, they are directly updated afterwards and when Consents are given or change
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
}
googleConsentModeMapping({
"ad_storage": "custom_consent", // "custom_consent" is the value which we also use to fire Google Ads Pixels
"ad_user_data": "custom_consent",
"ad_personalization": "custom_consent",
"analytics_storage": "denied" // we don't use Google Analytics, so we don't need to give analytics storage rights
});
</script>
I needed to use window.sevenTag.getVariableValue('Consents', dataLayer.length-1);,
otherwise the stg.consentDecisionMade Event never leads to an actual “update” of the google consent levels because it always refers to window.sevenTag.getVariableValue('Consents', 3); => 3 the INDEX of the pageView Event which first fired the Tag…
Defining the googleConsentModeMappingfunction in a way that it does not get redefined every time the Tag is triggered (by checking for !window.googleConsentModeMapping before defining it).
Checking for the {{ Consents }} variable INSIDE that function.
If we write {{ Consents }} in a tag, Piwik TM turns this into window.sevenTag.getVariableValue('Consents', INDEX); => the INDEX refers to the index of the dataLayer of the Event which first fired the Tag… So if “pageView” triggered the Tag and “pageView” was the 3rd Event, the index will be 3 => window.sevenTag.getVariableValue('Consents', 3);
Now if this is INSIDE the function, the function will use the value of the 3rd data layer event (pageView) even if the function is called later, e.g. on stg.consentDecisionMade. => Thus any future update of the tag will result in a reset to the Consent values at the state of the pageView => my problem.
So in short, I would have expected Piwik TM to not hard-write the value 3 into a function but instead use a dynamic function that always gets the latest variable value at runtime (like in GTM).
Anyway, the solution is:
to take the {{ Consents }} out of the function
add a another argument consentLevels to the function
put {{ Consents }} into the function call at the end
=> This seems to be the best compromise. We are not re-defining the function every time the Tag is called and we are still getting always the latest value.
<script>
// fired on stg.pageView and stg.consentDecisionMade
window.gtag = window.gtag || function(){dataLayer.push(arguments)};
/*
Validate: Check Google Ads Tag and look for these parameters:
- gcs: G110 => Ads granted, Analytics denied
- gcd: 13r3q3r3r5 => denied by default and granted after update, except analytics storage
*/
if (!window.googleConsentModeMapping) { // initialize the function only once per page
function googleConsentModeMapping(consents, consentLevels) {
console.log("Consent Levels: ", consentLevels);
if (consentLevels) {
var consObj = {};
Object.keys(consents).forEach(function(consent) {
consObj[consent] = (consentLevels.current_state[consents[consent]] === 1) ? 'granted' : 'denied';
});
gtag('consent', 'update', consObj);
}
}
// set default consent values for the page, they are directly updated afterwards and when Consents are given or change
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
}
googleConsentModeMapping({
"ad_storage": "custom_consent", // "custom_consent" is the value for "external media" which we also use to fire Google Ads Pixels
"ad_user_data": "custom_consent",
"ad_personalization": "custom_consent",
"analytics_storage": "denied" // we don't use Google Analytics, so we don't need to give analytics storage rights
}, {{ Consents }}); // the "Consents" variable needs to remain outside of the function because otherwise it will always contain the consent levels of when the tag was first called.
</script>