___TERMS_OF_SERVICE___ By creating or modifying this file you agree to Google Tag Manager's Community Template Gallery Developer Terms of Service available at https://developers.google.com/tag-manager/gallery-tos (or such other URL as Google may provide), as modified from time to time. ___INFO___ { "type": "TAG", "id": "cvt_temp_public_id", "version": 1, "securityGroups": [], "displayName": "Cookiemark - Consent Mode", "brand": { "id": "brand_dummy", "displayName": "Cookiemark" }, "description": "Sets the Google Consent Mode v2 defaults for a site that uses Cookiemark, restores a stored choice from the consent cookie and follows later choices. Fire it on Consent Initialization - All Pages, and switch off Google Consent Mode in Cookiemark when you use it.", "containerContexts": [ "WEB" ] } ___TEMPLATE_PARAMETERS___ [ { "type": "TEXT", "name": "cookieName", "displayName": "Consent cookie name", "simpleValueType": true, "defaultValue": "cookiemark_consent", "help": "The cookie in which Cookiemark stores the choice. Leave it as it is.", "valueValidators": [ { "type": "NON_EMPTY" } ] }, { "type": "TEXT", "name": "consentVersion", "displayName": "Consent version", "simpleValueType": true, "defaultValue": 1, "help": "Use the same number as the consent version in Cookiemark, under the site's Settings. A stored choice with another version is ignored, and the banner asks the visitor again.", "valueValidators": [ { "type": "POSITIVE_NUMBER" } ], "alwaysInSummary": true }, { "type": "GROUP", "name": "mappingGroup", "displayName": "Category per consent type", "groupStyle": "ZIPPY_OPEN", "subParams": [ { "type": "SELECT", "name": "mapAdStorage", "displayName": "ad_storage", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "marketing", "help": "The cookie category that grants ad_storage. security_storage is always granted." }, { "type": "SELECT", "name": "mapAdUserData", "displayName": "ad_user_data", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "marketing", "help": "The cookie category that grants ad_user_data. security_storage is always granted." }, { "type": "SELECT", "name": "mapAdPersonalization", "displayName": "ad_personalization", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "marketing", "help": "The cookie category that grants ad_personalization. security_storage is always granted." }, { "type": "SELECT", "name": "mapAnalyticsStorage", "displayName": "analytics_storage", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "statistics", "help": "The cookie category that grants analytics_storage. security_storage is always granted." }, { "type": "SELECT", "name": "mapFunctionalityStorage", "displayName": "functionality_storage", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "preferences", "help": "The cookie category that grants functionality_storage. security_storage is always granted." }, { "type": "SELECT", "name": "mapPersonalizationStorage", "displayName": "personalization_storage", "macrosInSelect": false, "selectItems": [ { "value": "preferences", "displayValue": "Preferences" }, { "value": "statistics", "displayValue": "Statistics" }, { "value": "marketing", "displayValue": "Marketing" } ], "simpleValueType": true, "defaultValue": "preferences", "help": "The cookie category that grants personalization_storage. security_storage is always granted." } ] }, { "type": "GROUP", "name": "defaultsGroup", "displayName": "Defaults", "groupStyle": "ZIPPY_OPEN", "subParams": [ { "type": "TEXT", "name": "waitForUpdate", "displayName": "Wait for update", "simpleValueType": true, "valueUnit": "milliseconds", "defaultValue": 500, "help": "How long Google tags wait for a stored choice before they act on the defaults.", "valueValidators": [ { "type": "NON_NEGATIVE_NUMBER" } ] }, { "type": "TEXT", "name": "regions", "displayName": "Regions", "simpleValueType": true, "help": "Leave this empty to deny by default for every visitor. Enter country codes separated by commas, for example NL, BE, DE, to deny by default in those countries only. Use the same countries as in Cookiemark, under the site's Regions." }, { "type": "CHECKBOX", "name": "adsDataRedaction", "checkboxText": "Redact ads data (ads_data_redaction)", "simpleValueType": true, "defaultValue": true, "help": "Removes ad click identifiers from requests while ad_storage is denied." }, { "type": "CHECKBOX", "name": "urlPassthrough", "checkboxText": "Pass ad click information through URLs (url_passthrough)", "simpleValueType": true, "defaultValue": false, "help": "Carries ad click information in links between pages while cookies are denied. This adds parameters to your URLs." } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ /* * Cookiemark: Google Consent Mode v2 template. * * Fire this tag on "Consent Initialization - All Pages". It sets the consent * defaults, restores a stored choice from the consent cookie, and listens for * later choices through the window.cookiemarkListeners queue. */ const createQueue = require('createQueue'); const getCookieValues = require('getCookieValues'); const getType = require('getType'); const gtagSet = require('gtagSet'); const JSON = require('JSON'); const logToConsole = require('logToConsole'); const makeNumber = require('makeNumber'); const setDefaultConsentState = require('setDefaultConsentState'); const updateConsentState = require('updateConsentState'); const TYPES = [ 'ad_storage', 'ad_user_data', 'ad_personalization', 'analytics_storage', 'functionality_storage', 'personalization_storage' ]; // Consent type to cookie category, as chosen in the tag's fields. const MAPPING = { ad_storage: data.mapAdStorage, ad_user_data: data.mapAdUserData, ad_personalization: data.mapAdPersonalization, analytics_storage: data.mapAnalyticsStorage, functionality_storage: data.mapFunctionalityStorage, personalization_storage: data.mapPersonalizationStorage }; // Logs only in preview mode: the logging permission is limited to debug. const log = (message, value) => { logToConsole('Cookiemark template: ' + message, value); }; // Turns { statistics: true, marketing: false, ... } into a consent state. const fromCategories = (categories) => { const state = {}; TYPES.forEach((type) => { state[type] = categories[MAPPING[type]] === true ? 'granted' : 'denied'; }); return state; }; // Keeps the known consent types of a state Cookiemark has already mapped. const fromConsentState = (input) => { const state = {}; TYPES.forEach((type) => { if (input[type] === 'granted' || input[type] === 'denied') { state[type] = input[type]; } }); return state; }; // 1. Defaults: everything denied until the visitor chooses. const defaults = { security_storage: 'granted' }; TYPES.forEach((type) => { defaults[type] = 'denied'; }); defaults.wait_for_update = makeNumber(data.waitForUpdate) || 0; const regions = (data.regions || '').split(',').map((region) => { return region.trim().toUpperCase(); }).filter((region) => { return region.length > 0; }); if (regions.length > 0) { defaults.region = regions; } setDefaultConsentState(defaults); log('defaults set', defaults); if (data.adsDataRedaction || data.urlPassthrough) { const settings = {}; if (data.adsDataRedaction) { settings.ads_data_redaction = true; } if (data.urlPassthrough) { settings.url_passthrough = true; } gtagSet(settings); } // 2. A returning visitor: restore the stored choice before any tag fires. const values = getCookieValues(data.cookieName) || []; const stored = values.length > 0 ? JSON.parse(values[0]) : undefined; if (getType(stored) === 'object' && makeNumber(stored.v) === makeNumber(data.consentVersion)) { const restored = fromCategories(stored); updateConsentState(restored); log('stored choice restored', restored); } // 3. Later choices. Cookiemark calls every function on window.cookiemarkListeners with // the consent state it mapped itself and with the plain category choices. The // category choices are preferred, so this tag's own mapping decides. const listen = createQueue('cookiemarkListeners'); listen((consentState, categories) => { let update = {}; if (getType(categories) === 'object') { update = fromCategories(categories); } else if (getType(consentState) === 'object') { update = fromConsentState(consentState); } else { return; } updateConsentState(update); log('choice updated', update); }); data.gtmOnSuccess(); ___WEB_PERMISSIONS___ [ { "instance": { "key": { "publicId": "access_consent", "versionId": "1" }, "param": [ { "key": "consentTypes", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_user_data" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_personalization" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "analytics_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "functionality_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "personalization_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "security_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "get_cookies", "versionId": "1" }, "param": [ { "key": "cookieAccess", "value": { "type": 1, "string": "specific" } }, { "key": "cookieNames", "value": { "type": 2, "listItem": [ { "type": 1, "string": "cookiemark_consent" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "access_globals", "versionId": "1" }, "param": [ { "key": "keys", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "cookiemarkListeners" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "write_data_layer", "versionId": "1" }, "param": [ { "key": "keyPatterns", "value": { "type": 2, "listItem": [ { "type": 1, "string": "ads_data_redaction" }, { "type": 1, "string": "url_passthrough" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "logging", "versionId": "1" }, "param": [ { "key": "environments", "value": { "type": 1, "string": "debug" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true } ] ___TESTS___ scenarios: - name: Sets denied defaults and waits for an update code: |- mock('getCookieValues', () => []); mock('createQueue', () => () => {}); runCode(mockData); assertApi('setDefaultConsentState').wasCalledWith({ security_storage: 'granted', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied', functionality_storage: 'denied', personalization_storage: 'denied', wait_for_update: 500 }); assertApi('updateConsentState').wasNotCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: Limits the defaults to the given regions code: |- mock('getCookieValues', () => []); mock('createQueue', () => () => {}); mockData.regions = 'nl, BE ,de,'; runCode(mockData); assertApi('setDefaultConsentState').wasCalledWith({ security_storage: 'granted', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied', functionality_storage: 'denied', personalization_storage: 'denied', wait_for_update: 500, region: ['NL', 'BE', 'DE'] }); assertApi('gtmOnSuccess').wasCalled(); - name: Sets ads data redaction and URL passthrough code: |- mock('getCookieValues', () => []); mock('createQueue', () => () => {}); mockData.urlPassthrough = true; runCode(mockData); assertApi('gtagSet').wasCalledWith({ ads_data_redaction: true, url_passthrough: true }); assertApi('gtmOnSuccess').wasCalled(); - name: Leaves the flags alone when both are off code: |- mock('getCookieValues', () => []); mock('createQueue', () => () => {}); mockData.adsDataRedaction = false; runCode(mockData); assertApi('gtagSet').wasNotCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: Restores a stored choice with the current consent version code: |- mock('getCookieValues', () => [ '{"necessary":true,"preferences":false,"statistics":true,"marketing":false,"v":1,"id":"c1a2","ts":1760000000}' ]); mock('createQueue', () => () => {}); runCode(mockData); assertApi('updateConsentState').wasCalledWith({ ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'granted', functionality_storage: 'denied', personalization_storage: 'denied' }); assertApi('gtmOnSuccess').wasCalled(); - name: Ignores a stored choice with an older consent version code: |- mock('getCookieValues', () => [ '{"necessary":true,"preferences":true,"statistics":true,"marketing":true,"v":1,"id":"c1a2","ts":1760000000}' ]); mock('createQueue', () => () => {}); mockData.consentVersion = 2; runCode(mockData); assertApi('updateConsentState').wasNotCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: Ignores a cookie that is not JSON code: |- mock('getCookieValues', () => ['not-json']); mock('createQueue', () => () => {}); runCode(mockData); assertApi('updateConsentState').wasNotCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: Updates the state when the visitor chooses later code: |- let queueName = ''; let listener; mock('getCookieValues', () => []); mock('createQueue', (name) => { queueName = name; return (fn) => { listener = fn; }; }); runCode(mockData); assertApi('updateConsentState').wasNotCalled(); assertThat(queueName).isEqualTo('cookiemarkListeners'); listener( { analytics_storage: 'denied', ad_storage: 'granted' }, { necessary: true, preferences: true, statistics: false, marketing: true } ); assertApi('updateConsentState').wasCalledWith({ ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted', analytics_storage: 'denied', functionality_storage: 'granted', personalization_storage: 'granted' }); - name: Uses its own mapping for a later choice code: |- let listener; mock('getCookieValues', () => []); mock('createQueue', () => (fn) => { listener = fn; }); mockData.mapFunctionalityStorage = 'statistics'; runCode(mockData); listener({}, { necessary: true, preferences: false, statistics: true, marketing: false }); assertApi('updateConsentState').wasCalledWith({ ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'granted', functionality_storage: 'granted', personalization_storage: 'denied' }); - name: Accepts a consent state without category choices code: |- let listener; mock('getCookieValues', () => []); mock('createQueue', () => (fn) => { listener = fn; }); runCode(mockData); listener({ analytics_storage: 'granted', ad_storage: 'denied', unknown_type: 'granted' }); assertApi('updateConsentState').wasCalledWith({ ad_storage: 'denied', analytics_storage: 'granted' }); setup: |- const mockData = { cookieName: 'cookiemark_consent', consentVersion: 1, mapAdStorage: 'marketing', mapAdUserData: 'marketing', mapAdPersonalization: 'marketing', mapAnalyticsStorage: 'statistics', mapFunctionalityStorage: 'preferences', mapPersonalizationStorage: 'preferences', waitForUpdate: 500, regions: '', adsDataRedaction: true, urlPassthrough: false }; ___NOTES___ Created on 14/10/2026, 20:00:00 Part of Cookiemark, https://cookiemark.com. Use this template only when Tag Manager sets the consent defaults. Switch off Google Consent Mode in Cookiemark in that case, or the defaults are set twice.