___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": "SSAK CMP - Cookie Banner + Consent Mode v2", "categories": ["TAG_MANAGEMENT", "PERSONALIZATION"], "brand": { "id": "brand_dummy", "displayName": "SSAK Agency" }, "description": "Baner cookies z Google Consent Mode v2 i server-side logiem zgód (RODO). Ustawia domyślne zgody na denied, ładuje baner i aktualizuje consent state. Fire na triggerze Consent Initialization - All Pages.", "containerContexts": ["WEB"] } ___TEMPLATE_PARAMETERS___ [ { "type": "LABEL", "name": "triggerInfo", "displayName": "⚠️ Ten tag MUSI być podpięty pod trigger \"Consent Initialization - All Pages\" (inaczej defaulty consent mode ustawią się za późno)." }, { "type": "GROUP", "name": "groupBanner", "displayName": "Wygląd banera", "groupStyle": "ZIPPY_OPEN", "subParams": [ { "type": "SELECT", "name": "lang", "displayName": "Język", "macrosInSelect": false, "selectItems": [ {"value": "auto", "displayValue": "Auto (wg języka strony)"}, {"value": "pl", "displayValue": "Polski"}, {"value": "en", "displayValue": "English"} ], "simpleValueType": true, "defaultValue": "auto" }, { "type": "SELECT", "name": "position", "displayName": "Pozycja banera", "macrosInSelect": false, "selectItems": [ {"value": "bar", "displayValue": "Pasek na dole"}, {"value": "box", "displayValue": "Box w lewym dolnym rogu"}, {"value": "center", "displayValue": "Modal na środku (z overlay)"} ], "simpleValueType": true, "defaultValue": "bar" }, { "type": "TEXT", "name": "accentColor", "displayName": "Kolor akcentu (przyciski)", "simpleValueType": true, "defaultValue": "#ffc5c5" }, { "type": "TEXT", "name": "logoUrl", "displayName": "URL logo (opcjonalne)", "simpleValueType": true }, { "type": "TEXT", "name": "privacyUrl", "displayName": "URL polityki prywatności", "simpleValueType": true }, { "type": "TEXT", "name": "cookieUrl", "displayName": "URL polityki cookies (opcjonalne)", "simpleValueType": true }, { "type": "TEXT", "name": "bannerTitle", "displayName": "Własny tytuł banera (opcjonalne - nadpisuje domyślny)", "simpleValueType": true }, { "type": "TEXT", "name": "bannerDesc", "displayName": "Własny opis banera (opcjonalne - nadpisuje domyślny)", "simpleValueType": true }, { "type": "CHECKBOX", "name": "floatingIcon", "checkboxText": "Pływająca ikonka do zmiany zgody (wymagane prawnie - łatwe wycofanie zgody; wyłącz tylko jeśli masz własny link w stopce)", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "showReject", "checkboxText": "Przycisk „Odrzucam\" na pierwszej warstwie", "simpleValueType": true, "defaultValue": true, "help": "UWAGA: ukrycie przycisku odrzucenia na pierwszej warstwie jest NIEZGODNE z wytycznymi EDPB/UODO (równorzędne odrzucenie) - realne ryzyko kary w UE. Wyłączaj wyłącznie na własną odpowiedzialność, np. dla ruchu spoza UE." } ] }, { "type": "GROUP", "name": "groupCategories", "displayName": "Kategorie zgód", "groupStyle": "ZIPPY_OPEN", "subParams": [ { "type": "CHECKBOX", "name": "catAnalytics", "checkboxText": "Analityczne (analytics_storage - GA4, Hotjar, Clarity...)", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "catMarketing", "checkboxText": "Marketingowe (ad_storage + ad_user_data + ad_personalization - Google Ads, Meta, TikTok...)", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "catPreferences", "checkboxText": "Preferencje (personalization_storage - język, region)", "simpleValueType": true, "defaultValue": false } ] }, { "type": "GROUP", "name": "groupCookies", "displayName": "Tabela cookies (2. warstwa banera)", "groupStyle": "ZIPPY_OPEN", "subParams": [ { "type": "LABEL", "name": "libInfo", "displayName": "Zaznacz narzędzia używane na stronie - baner pokaże gotowe tabele cookies (nazwy, czas życia, cel). Nietypowe cookies dopisz w tabeli niżej." }, { "type": "CHECKBOX", "name": "libGa4", "checkboxText": "Google Analytics 4", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "libGoogleAds", "checkboxText": "Google Ads", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "libMetaPixel", "checkboxText": "Meta Pixel (Facebook/Instagram)", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "libTiktok", "checkboxText": "TikTok Pixel", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libPinterest", "checkboxText": "Pinterest Tag", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libMicrosoftAds", "checkboxText": "Microsoft Ads (Bing)", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libHotjar", "checkboxText": "Hotjar", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libClarity", "checkboxText": "Microsoft Clarity", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libKlaviyo", "checkboxText": "Klaviyo", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libYoutube", "checkboxText": "YouTube (osadzone wideo)", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libShopify", "checkboxText": "Shopify (niezbędne)", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libWordpress", "checkboxText": "WordPress / WooCommerce (niezbędne)", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "libRecaptcha", "checkboxText": "Google reCAPTCHA (niezbędne)", "simpleValueType": true, "defaultValue": false }, { "type": "SIMPLE_TABLE", "name": "customCookies", "displayName": "Dodatkowe cookies (opcjonalne)", "simpleTableColumns": [ { "defaultValue": "", "displayName": "Nazwa cookie", "name": "name", "type": "TEXT" }, { "defaultValue": "necessary", "displayName": "Kategoria", "name": "category", "type": "SELECT", "selectItems": [ {"value": "necessary", "displayValue": "Niezbędne"}, {"value": "analytics", "displayValue": "Analityczne"}, {"value": "marketing", "displayValue": "Marketingowe"}, {"value": "preferences", "displayValue": "Preferencje"} ] }, { "defaultValue": "", "displayName": "Czas życia", "name": "duration", "type": "TEXT", "valueHint": "np. 1 rok" }, { "defaultValue": "", "displayName": "Dostawca", "name": "provider", "type": "TEXT" }, { "defaultValue": "", "displayName": "Cel", "name": "purpose", "type": "TEXT" } ], "newRowButtonText": "Dodaj cookie" } ] }, { "type": "GROUP", "name": "groupConsent", "displayName": "Ustawienia zgód", "groupStyle": "ZIPPY_CLOSED", "subParams": [ { "type": "TEXT", "name": "policyVersion", "displayName": "Wersja polityki (podbij, żeby wymusić ponowną zgodę u wszystkich)", "simpleValueType": true, "defaultValue": "1" }, { "type": "TEXT", "name": "expiryDays", "displayName": "Ważność zgody (dni)", "simpleValueType": true, "defaultValue": 365, "valueValidators": [{"type": "POSITIVE_NUMBER"}] }, { "type": "TEXT", "name": "waitForUpdate", "displayName": "wait_for_update (ms)", "simpleValueType": true, "defaultValue": 500, "valueUnit": "milliseconds", "valueValidators": [{"type": "NON_NEGATIVE_NUMBER"}] }, { "type": "CHECKBOX", "name": "logConsent", "checkboxText": "Loguj zgody server-side (dowód RODO - zalecane)", "simpleValueType": true, "defaultValue": true } ] }, { "type": "GROUP", "name": "groupAdvanced", "displayName": "Zaawansowane", "groupStyle": "ZIPPY_CLOSED", "subParams": [ { "type": "CHECKBOX", "name": "adsDataRedaction", "checkboxText": "ads_data_redaction (redakcja danych reklamowych przy odmowie - zalecane)", "simpleValueType": true, "defaultValue": true }, { "type": "CHECKBOX", "name": "urlPassthrough", "checkboxText": "url_passthrough (przekazywanie gclid w URL przy odmowie)", "simpleValueType": true, "defaultValue": false }, { "type": "CHECKBOX", "name": "shopifySync", "checkboxText": "Shopify: synchronizuj zgodę z Customer Privacy API (sklepy Shopify)", "simpleValueType": true, "defaultValue": true }, { "type": "TEXT", "name": "licenseKey", "displayName": "License key (usuwa branding SSAK Agency z banera)", "simpleValueType": true }, { "type": "TEXT", "name": "bannerUrl", "displayName": "URL skryptu banera", "simpleValueType": true, "defaultValue": "https://cmp.ssak-agency.pl/banner/v1.js" }, { "type": "TEXT", "name": "endpoint", "displayName": "Endpoint logowania zgód", "simpleValueType": true, "defaultValue": "https://cmp.ssak-agency.pl" } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ const setDefaultConsentState = require('setDefaultConsentState'); const updateConsentState = require('updateConsentState'); const gtagSet = require('gtagSet'); const getCookieValues = require('getCookieValues'); const injectScript = require('injectScript'); const queryPermission = require('queryPermission'); const setInWindow = require('setInWindow'); const makeNumber = require('makeNumber'); const makeString = require('makeString'); const JSON = require('JSON'); // 1) Advanced consent mode extras - przed defaultami gtagSet({ ads_data_redaction: data.adsDataRedaction !== false, url_passthrough: data.urlPassthrough === true }); // 2) Defaulty: wszystko denied (functionality/security granted) setDefaultConsentState({ ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied', personalization_storage: 'denied', functionality_storage: 'granted', security_storage: 'granted', wait_for_update: makeNumber(data.waitForUpdate) || 500 }); // 3) Powracający user: odczytaj cookie zgody i od razu zrób update // (zero czekania, poprawny gcs od pierwszego hitu) const raw = getCookieValues('ssak_consent')[0]; if (raw) { const saved = JSON.parse(raw); if (saved && saved.cats && saved.pv === makeString(data.policyVersion || '1')) { const c = saved.cats; updateConsentState({ analytics_storage: c.analytics ? 'granted' : 'denied', ad_storage: c.marketing ? 'granted' : 'denied', ad_user_data: c.marketing ? 'granted' : 'denied', ad_personalization: c.marketing ? 'granted' : 'denied', personalization_storage: c.preferences ? 'granted' : 'denied', functionality_storage: 'granted', security_storage: 'granted' }); } } // 4) Zbuduj config banera z pól template'u const categories = []; if (data.catAnalytics !== false) categories.push('analytics'); if (data.catMarketing !== false) categories.push('marketing'); if (data.catPreferences === true) categories.push('preferences'); const library = []; if (data.libGa4 !== false) library.push('ga4'); if (data.libGoogleAds !== false) library.push('google_ads'); if (data.libMetaPixel !== false) library.push('meta_pixel'); if (data.libTiktok === true) library.push('tiktok'); if (data.libPinterest === true) library.push('pinterest'); if (data.libMicrosoftAds === true) library.push('microsoft_ads'); if (data.libHotjar === true) library.push('hotjar'); if (data.libClarity === true) library.push('clarity'); if (data.libKlaviyo === true) library.push('klaviyo'); if (data.libYoutube === true) library.push('youtube'); if (data.libShopify === true) library.push('shopify'); if (data.libWordpress === true) library.push('wordpress'); if (data.libRecaptcha === true) library.push('recaptcha'); const texts = {}; if (data.bannerTitle) texts.title = data.bannerTitle; if (data.bannerDesc) texts.desc = data.bannerDesc; const config = { lang: data.lang || 'auto', position: data.position || 'bar', theme: { accent: data.accentColor || '#ffc5c5' }, logo: data.logoUrl || null, privacyUrl: data.privacyUrl || '', cookieUrl: data.cookieUrl || '', categories: categories, library: library, customCookies: data.customCookies || [], expiryDays: makeNumber(data.expiryDays) || 365, policyVersion: makeString(data.policyVersion || '1'), endpoint: data.endpoint || 'https://cmp.ssak-agency.pl', log: data.logConsent !== false, licenseKey: data.licenseKey || '', floatingIcon: data.floatingIcon !== false, showReject: data.showReject !== false, setDefaults: false, adsDataRedaction: data.adsDataRedaction !== false, urlPassthrough: data.urlPassthrough === true, shopify: data.shopifySync !== false, texts: texts }; setInWindow('__ssakCmp', config, true); // 5) Załaduj baner const url = data.bannerUrl || 'https://cmp.ssak-agency.pl/banner/v1.js'; if (queryPermission('inject_script', url)) { injectScript(url, data.gtmOnSuccess, data.gtmOnFailure); } else { data.gtmOnFailure(); } ___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": true}, {"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": true}, {"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": true}, {"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": true}, {"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": true}, {"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": true}, {"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": true}, {"type": 8, "boolean": true} ] } ] } } ] }, "clientAnnotations": {"isEditedByUser": true}, "isRequired": true }, { "instance": { "key": {"publicId": "inject_script", "versionId": "1"}, "param": [ { "key": "urls", "value": { "type": 2, "listItem": [ {"type": 1, "string": "https://cmp.ssak-agency.pl/*"}, {"type": 1, "string": "https://web-production-bdeb9.up.railway.app/*"} ] } } ] }, "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": "__ssakCmp"}, {"type": 8, "boolean": true}, {"type": 8, "boolean": true}, {"type": 8, "boolean": false} ] } ] } } ] }, "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": "ssak_consent"}] } } ] }, "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 } ] ___TESTS___ scenarios: - name: sets denied defaults and injects banner code: |- runCode(mockData); assertApi('setDefaultConsentState').wasCalledWith({ ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied', personalization_storage: 'denied', functionality_storage: 'granted', security_storage: 'granted', wait_for_update: 500 }); assertApi('injectScript').wasCalled(); - name: returning visitor gets immediate consent update code: |- mock('getCookieValues', (name) => { return ['{"id":"x","cats":{"analytics":true,"marketing":false},"pv":"1"}']; }); runCode(mockData); assertApi('updateConsentState').wasCalledWith({ analytics_storage: 'granted', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', personalization_storage: 'denied', functionality_storage: 'granted', security_storage: 'granted' }); - name: stale policy version does not update code: |- mock('getCookieValues', (name) => { return ['{"id":"x","cats":{"analytics":true},"pv":"0"}']; }); runCode(mockData); assertApi('updateConsentState').wasNotCalled(); setup: |- const mockData = { lang: 'pl', position: 'bar', policyVersion: '1', waitForUpdate: 500, expiryDays: 365, bannerUrl: 'https://cmp.ssak-agency.pl/banner/v1.js', endpoint: 'https://cmp.ssak-agency.pl' }; ___NOTES___ SSAK CMP - instrukcja: 1. Importuj template (Templates > New > Import). 2. Utwórz tag z tego template'u, trigger: Consent Initialization - All Pages. 3. Tagi Google (GA4, Ads) mają wbudowane consent checks - nic nie zmieniasz. 4. Tagi nie-Google (custom HTML): dodaj additional consent checks albo trigger na eventy dataLayer: ssak_consent_update / ssak_consent_ready (ssak_consent.analytics / .marketing / .preferences = true/false). 5. Sklepy Shopify: patrz docs/SHOPIFY.md w repo (theme.liquid + custom pixel na checkout).