Deployment

Stand-alone or sync with backend services?

Before you start deploying it is worth considering how the CMP will operate session and UX point of view. The following will focus on stand-alone configuration and deployment for the sake of simlicity. If you are deploying with backend integration, be it Gravito CDP or your own endpoints for retrieving and storing consents from CMP, please look into specific doc about the superwrapper.js

Ok, I go for stand-alone or I have already configured superwrapper.js?

You will need wrapper.js file, edit it and deploy using e.g. with tag management system (code below is ready to be copied as-is to Google Tag Manager as custom HTML tag).

What to edit? Following is specific to service-scope use, defined to "gravitoCMPConfig" object:

Variable

Description

Release

core.settingBtnClassName

Class name for link(s) that will be used to re-open the CMP modal when user wants to review or change consent settings.

1.0.0

core.version

Indicates the version of the configuration. If the version number is changed, CMP UI will be shown again to user allowing to inform and collect new purposes, vendors etc.

1.0.0

core.allowedVendors

Defines allowed OOB vendors (only when CMP is configured to global scope)

1.0.0

core.vendors

List of vendors at https://iabeurope.eu/vendor-list-tcf-v2-0/

1.0.0

core.purposes

Define purposes and/or stacks you need to enable

1.0.0

core.nonTCFVendors

If you have any, you need to maintain vendor-id mapping by yourself

1.0.0

core.customPurposes

Aka. publisher purposes. If you need consents for purposes separately.

1.0.0

core.publisherRestrictions

If you want to override some vendor-originating settings, e.g. force consent for purposes the vendor states the legitimate interest only

1.0.0

style.fonts

Set to fonts your site is using or leave as is to use gravitoFont

1.0.0

style.logoUrl

URL location of your brand logo.

1.0.0

style.primaryColor

You can set to your brand's primary color, it is used on primary buttons, link colors etc.

1.0.0

style.secondaryColor

Is used as background of cancel buttons etc., you can customize if needed.

1.0.0

core.languageCode

Two digit ISO code for the language CMP is shown

1.0.1

core.purposeOneTreatment

True or false (default), set to true only if the target market has legislative need for enabling this.

1.0.1

core.publisherCountryCode

Two digit ISO country code the publisher is residing

1.0.1

core.useTopDomain

Defaults to false, set to "true" if you want the cookie to be shared between subdomains (cookie is set to .domain.xx)

1.0.2

style.consentInputType

Defaults to checkbox, value "toggle" changes the UI

1.0.3

style.useAccordionForPurpose

Default is false, true enables accordion UI for purpose lists on 2nd layer

1.0.3

style.layoutType

Defaults to "2-tabs", "3-tabs" enables the split on UI between consents and legitimate interests

1.0.3

style.disableConfirmationModal

Default is false, "true" disables the confirmation when user turns consent off

1.0.3

core.googleTcfId

755 (avoiding to hard-code Google related settings), this enables the personalization consent check.

1.0.3

core.withBackendIntegration

true/false (default) tells the CMP whether the is superwrapper present

1.0.6

core.syncEvents

["cmpui:closed","layer1:opt-in-all"] the list of events that are sent to superwrapper, allowing it to store the consents to backend

1.0.6

style.showScrollBars

true/false, sets the scrollbar visible on 2nd layer

2.0.1

style.scrollBarWidth

if showScrollBars is set to true, this defines the width of the scrollbar, set it to e.g. "10px"

2.0.1

Some texts can be altered but the content of the message cannot change, ie. CMP has been validated with following texts and changing the wording might invalidate the CMP against the TCF 2.0 policy (and cause harms to Gravito as CMP vendor).

<script>
var customCSS="";
var gravitoCMPConfig = {
core: {
cookieName: "TcString",
cookieExpiry: 365,
isServiceSpecific:true,
settingBtnClassname:"manageSettings",
version:1,
languageCode:"fi",
purposeOneTreatment:false,
publisherCountryCode:"FI",
googleTcfId:755,
allowedVendors:[
2,8,11
],
purposes: [
{
type: "purposes",
id: 1
},
{
type: "stacks",
id: 26
},
{
type: "purposes",
id: 10
}
],
specialFeatures: [
1, 2
],
specialPurposes: [
1, 2
],
features: [
1, 2, 3
],
vendors: [
2,
6,
8,
511,
11,
14
],
nonTCFVendors: [
{
type: "non-tcf-vendors",
id: 1,
name: "Facebook",
description: "Facebookin kerää tietoa verkkovierailusta ja sisällöistä myös Facebookin ulkopuolella. Facebookia käytetään mainontaan ja kerättyjä tietoja parempaan mainonnan kohdentamiseen.<br><br><a href=\"https://www.facebook.com/help/568137493302217\">https://www.facebook.com/help/568137493302217</a>",
isConsentable:true,
},
{
type: "non-tcf-vendors",
id: 2,
name: "Amazon",
description: "Amazonin kerää tietoja verkkovierailusta ja sisällöistä myös Amazonin ulkopuolella. Amazonia käytetään mainontaan ja kerättyjä tietoja parempaan mainonnan kohdentamiseen.",
isConsentable:true,
isLegitimate:false
}
],
customPurposes: [
{
type: "customPurpose",
id: 1,
name: "Tietojen keräys",
description: "Gravito voi kerätä tietoja verkkokäyttäytymisestä ja käyttää niitä käyttäjäkokemuksen parantamiseksi",
descriptionLegal: "Gravito voi kerätä tietoja verkkokäyttäytymisestä ja käyttää niitä käyttäjäkokemuksen parantamiseksi",
isConsentable:false,
isLegitimate:true
},
{
type: "customPurpose",
id: 2,
name: "Kohdentaminen ja personointi",
description: "Gravito voi käyttää tietoja sisältöjen kohdentamiseen ja personointiin",
descriptionLegal: "Gravito voi käyttää tietoja sisältöjen kohdentamiseen ja personointiin",
isConsentable:true,
isLegitimate:false
}
],
publisherRestrictions: [
{
purposeID: 1,
restrictionType: "REQUIRE_CONSENT",
vendors: [
6,
12
]
},
{
purposeID: 1,
restrictionType: "REQUIRE_CONSENT",
vendors: [
8
]
},
{
purposeID: 2,
restrictionType: "REQUIRE_CONSENT",
vendors: [
8
]
}
]
},
text: {
firstLayer: {
title: "Tarvitsemme suostumuksesi tarjotaksemme personoitua palvelua",
introductionText: "Gravito ja sen <span id='partners-link'>kolmannen osapuolen toimittajat</span> keräävät henkilötietoja (esim. IP-osoite tai laitetunniste) käyttäen evästeitä ja muita teknisiä keinoja tietojen tallentamiseen ja lukemiseen laitteellasi tarjotakseen sinulle tarkoituksenmukaisia mainoksia ja parhaan mahdollisen asiakaskokemuksen",
consentableItemDescription: "Gravito ja sen kumppanit tarvitsevat suostumuksesi seuraaviin:",
legalFooter: "Hyväksymällä sallit tietojesi käsittelyn. Suostumuksesi koskee tätä palvelua, hyväksymättä jättäminen voi vaikuttaa asiakaskokemukseesi. Jotkut teknologiat saattavat perustella tietojen käsittelyä oikeutetulla edulla, voit vastustaa tätä tai muuttaa muita asetuksia klikkaamalla \"Asetukset\" linkkiä.",
privacyPolicyUrl: "https://www.gravito.net/#privacy-policy",
privacyLabel:"Tietosuoja",
actions: ["Asetukset","Hyväksy kaikki"]
},
secondLayer: {
title: "Asetukset",
introductionText: "Valitse oheiselta listalta haluamasi tarkoitukset ja autat meitä palvelemaan sinua paremmin.",
tabLabels: ["Tarkoitukset ja ominaisuudet","Toimittajat"],
checkBoxLabels:{
"consent": "Suostumus",
"legitimateInterest": "Oikeutettu etu"
},
actions: ["Hyväksy kaikki","Hyväksy valitut","Estä kaikki"]
},
thirdLayer: {
confirmationForUncheck: {
heading: "Oletko varma että haluat olla antamatta suostumusta?",
paragraphs: [
"Nämä evästeet tai muut tekniset keinot ovat tärkeitä parhaan ja personoidun käyttäjäkokemuksen tuottamiseksi."
]
},
confirmationForAcceptSelected: {
heading: "Oletko varma että haluat olla antamatta suostumusta?",
paragraphs:[
"Nämä evästeet tai muut tekniset keinot ovat tärkeitä parhaan ja personoidun käyttäjäkokemuksen tuottamiseksi.",
"Ilman evästeitä tai muita vastaavia teknisiä keinoja on kykymme kehittää palveluitamme ja personoida käyttökokemustasi vaikeampaa, mistä johtuen voi olla että jotkin ominaisuudet ovat pois käytöstä ja käyttökokemuksesi voi olla heikompi."
]
},
actions: ["Peruuta","Kyllä"]
},
commonTerms:{
purposes:"Tarkoitukset",
consent:"Suostumus",
legitimateInterest:"Oikeutettu etu",
specialPurposes:"Erityiset tarkoitukset",
specialFeatures:"Erityisominaisuudet",
features:"Ominaisuudet",
policyURl:"Tietosuojaselosteen osoite",
nonTCFVendors:"Toimittajat jotka eivät kuulu IAB:n TCF verkostoon",
vendors:"Toimittajat",
customPurposes:"Graviton toimintaan liittyvät luvat"
}
},
style:{
logoUrl: "https://cdn.gravito.net/logos/gravito_logo_white_background.png",
primaryColor: "orange",
secondaryColor: "#666",
fonts: [
{
url:"https://fonts.gstatic.com/s/manrope/v1/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_M-bnBeA.woff2",
unicodeRange:"U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;"
},
{
url:"https://fonts.gstatic.com/s/manrope/v1/xn7_YHE41ni1AdIRqAuZuw1Bx9mbZk79FN_C-bk.woff2",
unicodeRange:"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;"
}
],
customCSS:customCSS,
consentInputType:"toggle",
useAccordionForPurpose:true,
layoutType:"3-tabs",
disableConfirmationModal:true
}
}
window.gravitoCMPConfig = gravitoCMPConfig;
//TCF specific base url
var baseUrl="https://cdn.gravito.net/tcf-v2";
//js components specific base url
var componentUrl="https://cdn.gravito.net/cmp";
function browserSupportsSymbol(){
return window.Symbol?true:false;
}
var browserSupportsES6 = function() {
try {
new Function("(a = 0) => a");
return true;
}
catch (err) {
return false;
}
}();
function addPolyfill(){
var isSymbolPresent= browserSupportsSymbol();
var isEs6FeaturePresent= browserSupportsES6;
if(isSymbolPresent===false&&isEs6FeaturePresent===false){
var polyfillScriptTag = document.createElement('script');
polyfillScriptTag.src='https://polyfill.io/v3/polyfill.min.js?features=es6%2CSymbol';
polyfillScriptTag.addEventListener('load',function(){
addbundles()
})
document.head.appendChild(polyfillScriptTag);
return
}
if(isSymbolPresent===true&&isEs6FeaturePresent===false){
var polyfillScriptTag = document.createElement('script');
polyfillScriptTag.src='https://polyfill.io/v3/polyfill.min.js?features=es6';
polyfillScriptTag.addEventListener('load',function(){
addbundles()
})
document.head.appendChild(polyfillScriptTag);
return
}
if(isSymbolPresent===false&&isEs6FeaturePresent===true){
var polyfillScriptTag = document.createElement('script');
polyfillScriptTag.src='https://polyfill.io/v3/polyfill.min.js?features=Symbol';
polyfillScriptTag.addEventListener('load',function(){
addbundles()
})
document.head.appendChild(polyfillScriptTag);
return
}
addbundles()
}
//add stub
function addstub (){
var stubScriptTag = document.createElement('script');
stubScriptTag.innerHTML='"use strict";!function(){var e=function(){var e,t="__tcfapiLocator",a=[],n=window;for(;n;){try{if(n.frames[t]){e=n;break}}catch(e){}if(n===window.top)break;n=n.parent}e||(!function e(){var a=n.document,r=!!n.frames[t];if(!r)if(a.body){var s=a.createElement("iframe");s.style.cssText="display:none",s.name=t,a.body.appendChild(s)}else setTimeout(e,5);return!r}(),n.__tcfapi=function(){for(var e,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.length)return a;if("setGdprApplies"===n[0])n.length>3&&2===parseInt(n[1],10)&&"boolean"==typeof n[3]&&(e=n[3],"function"==typeof n[2]&&n[2]("set",!0));else if("ping"===n[0]){var s={gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"};"function"==typeof n[2]&&n[2](s)}else a.push(n)},n.addEventListener("message",(function(e){var t="string"==typeof e.data,a={};try{a=t?JSON.parse(e.data):e.data}catch(e){}var n=a.__tcfapiCall;n&&window.__tcfapi(n.command,n.version,(function(a,r){var s={__tcfapiReturn:{returnValue:a,success:r,callId:n.callId}};t&&(s=JSON.stringify(s)),e&&e.source&&e.source.postMessage&&e.source.postMessage(s,"*")}),n.parameter)}),!1))};"undefined"!=typeof module?module.exports=e:e()}();'
document.head.appendChild(stubScriptTag)
}
function addPrivateConfig (){
window.gravitoCMP={
baseUrl:baseUrl
}
}
function addbundles (){
var ui_path= componentUrl + "/uibundle_latest.js"
var cmp_path=componentUrl + "/bundle_latest.js"
var el = document.createElement('script');
el.src = cmp_path
el.async = 'true';
el.addEventListener('load', function() {
var ui_script = document.createElement("script");
ui_script.src = ui_path;
document.head.appendChild(ui_script);
});
document.head.appendChild(el);
}
function initDataLayer(){
window.gravitoData?window.gravitoData.CMP = {}: createDataLayer();
}
function createDataLayer() {
window.gravitoData = {};
window.gravitoData.CMP = {};
}
addPrivateConfig();
addstub();
addPolyfill();
initDataLayer();
</script>

Versioning

Above example links to always latest bundle libraries, doing so loads always the latest production build of Gravito CMP. If you want to stick on certain version and/or include your own QA control to the process, you can link the configuration to load specific versions by modifying these attributes within configuration:

var ui_path= componentUrl + "/uibundle-1.0.0.js"
var cmp_path=componentUrl + "/bundle-1.0.0.js"

Inject CSS to CMP

custom CSS classes or other definitions can be injected to CMP, this helps with cases where base CSS on the site CMP runs on is too greedy and overrides styling over CMP.

Example of CSS injection, basically you need to define customCSS as first thing on the wrapper code:

var customCSS = ".gravitoCMP-list-heading { padding-top: 4px !important; font-size: 0.7em !important; margin-right: 0; }";

Global scope

If you want to configure CMP to global scope, ie. consent string is set on euconsent-v2 cookie, you need to change configuration:

isServiceSpecific:false

Google Ad Manager?

If you are using Google Ad Manager and you are seeing 2.1A errors on your reports, you might want to affect the ad tags and request personalized ads only when there is consent given via CMP. You can enable this e.g. by adding following script on your orchestration (requires CMP version 1.0.3 and googleTcfId to be configured):

<script>
if(gravitoCMP && gravitoCMP.eventsHandler.getGooglePersonalizationStatus()){
googletag.cmd.push(function(){
googletag.pubads().refresh();
});
}else{
googletag.cmd.push(function(){
googletag.pubads().setRequestNonPersonalizedAds(1);
googletag.pubads().refresh();
});
}
</script>