Add-on
Un add-on è un pezzo del banner che non sta nel bundle: un file a parte, scaricato solo dai webspace che lo usano. Oggi sono quattro:
| Add-on | Fase | Cosa fa |
|---|---|---|
preemptive-block | pre | Sblocca gli script e gli iframe marcati nell'HTML quando c'è il consenso che chiedono |
consent-archive | post | Invia ogni scelta del visitatore all'archivio consensi del webspace |
consent-event | post | Manda l'evento avacy_consent del banner legacy sulla window, su ogni webspace v3 |
legacy-compat | post | Rimette window.AVACY del banner legacy, per i webspace migrati |
Sorgenti: packages/addons/<nome>/.
Come si accendono
Un add-on è acceso quando compare in addons[] della config, con la stessa forma di frameworks[]:
{
"addons": [
"preemptive-block",
{ "consent-archive": { "url": "https://…/consents" } }
]
}
Il nome da solo usa le impostazioni di default dell'add-on; la forma a oggetto passa le impostazioni. Il core deduplica per nome. Dettaglio della chiave: Config › Add-on.
Chi scrive addons[]: il SaaS, nel banner.json del webspace. Il cliente non lo compila a mano:
consent-archivedal toggle di archiviazione automatica dei consensi, con l'URL generato dal SaaS;preemptive-blockquando almeno un fornitore custom del webspace ha URL di blocco preventivo;consent-eventsu ogni webspace v3, sempre;legacy-compatquando il webspace è stato migrato da v2, a ogni salvataggio del banner.
Lo stub TCF non è un add-on: sta in testa al loader, nelle varianti tcf e tcf-gcm (vedi sotto).
Chi li scarica: il loader v3, e solo lui. Legge addons[] da banner.json, scarica da /v3/addons/<nome>/<versione>/index.js quelli che fanno parte della release (il manifest è dentro il loader) e consegna ciascuno al core. Un nome che la release non conosce viene ignorato con un warning in console ([avacy-loader] '<nome>' is not part of this release; ignored.). Nell'inclusione manuale il loader non c'è e nessun add-on viene scaricato.
pre e post
| Fase | Quando si scarica | Il boot lo aspetta? |
|---|---|---|
pre | Subito, in parallelo con il bundle | Sì: init() aspetta i pre annunciati dal loader fino a core.addonsTimeoutMs (default 1500 ms, in tutto) |
post | Dopo il primo avacy:ready | No |
Un add-on che arriva tardi (un pre oltre il timeout, ogni post) parte comunque appena si registra, e recupera gli eventi che si è perso: il core gli passa la storia del boot. Un add-on che non si scarica viene segnalato in console dal loader ([avacy-loader] add-on '<nome>' failed to load); uno che fallisce il setup viene saltato e segnalato nei log del CMP (visibili con loggers). In entrambi i casi il banner va avanti senza.
Override dalla pagina
Un addons scritto nel blocco #avacy-config sostituisce la lista che il core avvia (gli array non si fondono). Quindi dalla pagina si può:
- spegnere un add-on del webspace, lasciandolo fuori dalla lista;
- cambiarne le impostazioni, riscrivendo la sua voce.
Non si può accenderne uno che il SaaS non ha attivato: la lista dei file da scaricare il loader la prende solo da banner.json, e un add-on mai scaricato non c'è.
<!-- spegne consent-archive su questa pagina, tiene preemptive-block -->
<script type="application/json" id="avacy-config">
{ "addons": ["preemptive-block"] }
</script>
Attenzione: la lista in pagina prende il posto di quella di banner.json per intero.
preemptive-block
Fase pre. Il blocco vero lo fa il markup: uno script con un type che il browser non esegue, un iframe senza src. L'add-on fa solo il contrario: quando c'è un consenso valido che soddisfa i requisiti dell'elemento, lo rimette com'era. Senza l'add-on acceso, gli elementi marcati restano bloccati per sempre.
Impostazioni:
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
placeholder | boolean | false | Disegna un segnaposto dentro ogni iframe ancora bloccato, con un testo e un bottone che apre le preferenze. Vedi Segnaposto. |
Marcare uno script
<!-- script esterno -->
<script type="text/avacy-blocked"
data-avacy-src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"
data-avacy-requires="gcm:purpose:4"></script>
<!-- script inline -->
<script type="text/avacy-blocked"
data-avacy-requires="gcm:purpose:4">
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-XXXX');
</script>
<!-- modulo ES -->
<script type="text/avacy-blocked"
data-avacy-type="module"
data-avacy-src="https://example.com/widget.mjs"
data-avacy-requires="custom:vendor:d116"></script>
| Attributo | Obbligatorio | Descrizione |
|---|---|---|
type="text/avacy-blocked" | sì (in pratica) | Impedisce al browser di eseguire lo script. L'add-on non legge questo valore: qualunque type non eseguibile funziona, ma text/avacy-blocked è la convenzione. |
data-avacy-requires | sì | I requisiti di consenso, vedi sotto. È l'attributo che marca l'elemento: senza, l'add-on non lo vede. |
data-avacy-src | per gli script esterni | L'URL vero. Assente o vuoto = script inline (il contenuto del tag). |
data-avacy-type | no | Il type da rimettere allo sblocco, es. module. Assente = script classico. |
Allo sblocco lo script viene sostituito da un nodo nuovo (un type cambiato su un nodo già in pagina non lo farebbe partire), con gli stessi attributi tranne quelli di blocco.
Marcare un iframe
<iframe data-avacy-src="https://www.youtube.com/embed/XXXX"
data-avacy-requires="custom:vendor:d42"
width="560" height="315"></iframe>
L'iframe si scrive senza src; l'add-on lo rimette da data-avacy-src allo sblocco.
Sintassi di data-avacy-requires
data-avacy-requires="custom:vendor:d116;tcf:purpose:1,8,9"
- Un requisito è
framework:asse:id, esattamente tre pezzi separati da:. ;separa i requisiti. Tutti devono essere soddisfatti (AND).- Più id separati da
,nello stesso requisito sono anch'essi in AND:tcf:purpose:1,8,9vuol dire 1 e 8 e 9.
| Asse | Cosa controlla | Esempio |
|---|---|---|
vendor | consenso al fornitore | tcf:vendor:755, custom:vendor:d116, acm:vendor:89 |
purpose | consenso alla finalità (per custom legge le finalità custom) | tcf:purpose:1, gcm:purpose:4, custom:purpose:3 |
customPurpose | consenso a una finalità custom (stesso effetto di custom:purpose) | custom:customPurpose:3 |
specialFeature | consenso a una special feature TCF | tcf:specialFeature:1 |
legint | legittimo interesse (non consenso) sulla finalità | tcf:legint:2 |
framework è il nome di un framework della config: tcf, gcm, custom, acm. Gli id sono quelli del framework: numeri per TCF/GCM/ACM, id testuali per i fornitori custom (d116). I purpose GCM vanno da 1 a 7 (4 = analytics_storage; elenco completo in Config › GCM).
Regole di valutazione:
- Serve un consenso valido. Prima della scelta del visitatore non si sblocca niente.
- Un id che non compare tra le scelte non è concesso: un fornitore tolto dalla lista non apre nulla.
- Framework non configurato. Un requisito su un framework che il webspace non ha (es.
tcf:purpose:1senza TCF inframeworks) ripiega sul fornitore custom della stessa marcatura: è soddisfatto se i requisiticustom:vendor:…dell'elemento sono concessi. Senza un fornitore custom nella marcatura, l'elemento resta bloccato. - Marcatura illeggibile ⇒ bloccato. Un asse sconosciuto, un requisito senza id, più di tre pezzi (
:scritto al posto di;): l'elemento resta bloccato, con una riga di log (unreadable marking, left blocked). Mai sbloccato per errore. - Attributo vuoto (
data-avacy-requires=""): nessun requisito, l'elemento si sblocca al primo consenso valido, qualunque sia, anche un rifiuto totale.
Non esiste una forma "tutte le finalità": se servono tutte, si elencano.
Con il TCF acceso (anche senza fornitori IAB), una marcatura scritta a mano deve chiedere solo finalità e special feature che il fornitore custom dichiara, e solo come consenso (non legint). Altrimenti lo script resta bloccato.
Ordine e ritiro del consenso
- Gli elementi si sbloccano nell'ordine del documento. Gli script esterni scaricano in parallelo ma si eseguono in ordine; uno script inline aspetta che gli esterni sopra di lui siano caricati (max 5 secondi l'uno), così
gtag('config', …)trovagtag.jsgià pronto. - Ogni nuovo consenso rivaluta gli elementi ancora bloccati: un consenso più ampio sblocca quello che ora è concesso.
- Non ri-blocca mai: uno script già partito non si può fermare. Se il visitatore ritira un consenso, l'effetto si vede al caricamento di pagina successivo.
Segnaposto degli iframe
Con placeholder: true, ogni iframe ancora bloccato mostra un piccolo documento (via srcdoc) con il testo IFRAME_BLOCKED_TEXT e un bottone IFRAME_BLOCKED_BTN che apre il pannello preferenze. Il segnaposto c'è anche prima di qualunque scelta, e sparisce allo sblocco (un eventuale sandbox dell'iframe viene sospeso durante il segnaposto e rimesso prima di caricare il contenuto vero).
I colori vengono dal tema del webspace (ui.colors.backgroundColor, textColorSecondary, btnCustomizeBackground, btnCustomizeTextColor, ui.font.fontFamily). Il CSS del sito può sovrascriverli su :root:
:root {
--avacy-placeholder-bg: #f5f5f5;
--avacy-placeholder-fg: #222;
--avacy-placeholder-radius: 8px;
--avacy-placeholder-font: Inter, sans-serif;
--avacy-placeholder-accent: #0060e6;
--avacy-placeholder-accent-fg: #fff;
}
I due testi sono tradotti in en, it e de; nelle altre lingue escono in inglese (si possono impostare con customLabels).
Marcatura legacy (data-managed="as-oil")
L'add-on legge anche la marcatura del banner legacy, quella che scrivono i siti col plugin WordPress precedente o i temi marcati a mano. Gli attributi si traducono per nome:
| Legacy | Equivale a |
|---|---|
data-managed="as-oil" | data-avacy-requires (marca l'elemento) |
data-src | data-avacy-src |
data-type | data-avacy-type |
data-custom-vendor="d116" | custom:vendor:d116 |
data-iab-vendor="755" | tcf:vendor:755 |
data-purposes="1,2,4" | tcf:purpose:1,2,4 |
data-legints="2" | tcf:legint:2 |
data-special-features="1" | tcf:specialFeature:1 |
Stesse regole del formato nuovo: tutto in AND, attributo assente = nessun requisito su quell'asse. Attributo presente ma vuoto: data-purposes="", data-legints="" e data-special-features="" lasciano l'elemento bloccato, con un avviso [Avacy] Preemptive block in console; data-iab-vendor="" e data-custom-vendor="" non richiedono niente, come nel legacy. → Migrazione dal banner legacy.
consent-archive
Fase post. Invia ogni scelta del visitatore sul banner all'archivio consensi del webspace (la "prova del consenso"). È il sostituto di sendConsent del banner legacy.
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
url | string | — (obbligatoria) | Endpoint dell'archivio, <api>/<tenant>/v4/webspaces/<uuid>/consents. Lo scrive il SaaS. Senza url l'add-on fallisce al setup e viene saltato; il banner parte lo stesso. |
Cosa invia, e quando
Una POST JSON per ogni decisione presa con un bottone del banner o del pannello preferenze:
{ "consent_type": "banner", "optin": "consent-all", "uuid": "…" }
optin | Bottone |
|---|---|
consent-all | "Accetta tutto" (banner o pannello) |
reject-all | "Rifiuta tutto" (banner o pannello) |
close-banner | "Continua senza accettare" / la X del banner |
save-preferences | "Salva" nel pannello, anche quando le scelte risultano un accetta/rifiuta tutto |
Niente TC String né scelte di dettaglio: IP, dominio e versioni delle policy li aggiunge il backend. Le scritture via API (window.Avacy.acceptAll(), updateConsent(), triggerConsent()…) non vengono archiviate: non corrispondono a un bottone premuto.
Essendo post, l'add-on arriva dopo avacy:ready: una scelta fatta prima del suo arrivo viene recuperata dalla storia del boot e inviata con l'ora del click. Le richieste partono una alla volta, in ordine.
Id di correlazione
La prima risposta dell'archivio contiene un uuid; l'add-on lo salva e lo manda in tutte le richieste successive, così le decisioni dello stesso visitatore restano legate.
- Dove: dentro il record del consenso,
avacy_consent.meta.consentId. L'add-on non scrive lo storage: chiede al core di aggiornare il record (host.consent), che parte comeavacy:consent-updated, non come una nuova scelta. Una scelta successiva si porta dietro l'id. Se lo store non tiene il record, l'id resta solo in memoria per la pagina. - Release precedenti: la chiave a sé
${store.consentKey}_id(avacy_consent_idcon la config di default) viene migrata nel record al boot e poi cancellata. - Azzeramento: se all'avvio non c'è un consenso valido (prima visita, consenso scaduto,
policyVersionalzata), l'id viene cancellato e la catena riparte.
In caso di errore
Best effort: timeout 15 s, keepalive, nessun cookie inviato (credentials: 'omit'), nessun retry. Un archivio lento o giù non blocca mai il salvataggio del consenso né il banner; l'errore finisce nei log del CMP come warning.
consent-event
Fase post. Manda sulla window l'evento avacy_consent del banner legacy, così i tag e gli script dei clienti che lo ascoltano funzionano anche col v3 (#746).
Chi lo accende: il SaaS, da solo, su ogni webspace v3, migrato o nato v3, a ogni salvataggio del banner. Non c'è un interruttore e non ha impostazioni. Un clone lo riceve come ogni webspace v3.
Un CustomEvent sulla window, negli stessi momenti del legacy:
trigger | Quando |
|---|---|
page_load | Al caricamento, se c'è un consenso salvato |
consent_update | A ogni scelta, anche dopo un rifiuto |
window.addEventListener('avacy_consent', (e) => {
e.detail.customVendorsWithConsent.forEach((v) => console.log(v.id, v.name));
});
| Campo | Contenuto |
|---|---|
optin | false solo dopo un «rifiuta tutto» (differenze) |
purposes, legitimateInterests, specialFeatures | Id TCF con consenso o legittimo interesse |
iabVendors | Id dei fornitori IAB con consenso |
iabVendorsWithConsent | Gli stessi, come { id, name }: basta il consenso al fornitore, senza guardare le finalità |
customPurposes | { id, c, li? }, con 1/0 come nel cookie legacy |
customVendors | Tutti i fornitori custom, { id, consent }, con 1/0 come nel legacy |
customVendorsWithConsent | Quelli con consenso al fornitore e a tutte le finalità che dichiarano, { id, name } (regola) |
gcm | I tipi di Consent Mode concessi, per esempio ['ad_storage'] |
L'evento descrive la scelta appena fatta: lo stato si legge da quella, non da una lettura successiva.
Fornitori custom con consenso
Un fornitore custom entra in customVendorsWithConsent se ha il consenso al fornitore e tutte le sue finalità dichiarate hanno il consenso.
- Le finalità dichiarate sono quelle del fornitore nel file dei fornitori custom (
vendors[].purposes). Possono essere TCF, di Consent Mode o custom, e si leggono dalle scelte del consenso. I riferimentiacmsi ignorano, come fa il core. - Un fornitore che non dichiara finalità entra col solo consenso al fornitore.
- Un fornitore che il file non elenca resta fuori.
- Se il file non arriva (nessuna risposta entro 3 secondi, errore di rete o di stato), nessun fornitore custom entra: senza il file non si sblocca niente. Il resto dell'evento parte lo stesso, con un warning in console.
Il file dei fornitori. Serve per le finalità e per i nomi. Si scarica una volta per pagina quando almeno un fornitore custom ha il consenso, anche al page_load di un visitatore di ritorno: chi guarda la rete al boot vede quella richiesta. Se il file è dentro la config del banner (customVendorsConfig) non si scarica niente. L'evento aspetta il file fino a 3 secondi.
I nomi. Quelli custom vengono dalle liste che il pannello preferenze ha già costruito, poi dal file. Quelli IAB ci sono solo se il pannello è già stato aperto: per l'evento non si scarica la GVL.
Differenze dal banner v2
| Banner v2 | v3 con consent-event | |
|---|---|---|
iabVendorsWithConsent | Il fornitore entrava solo se tutte le sue finalità erano accettate (serviva la GVL) | Basta il consenso al fornitore: la GVL non si scarica all'avvio |
customVendorsWithConsent | Contava solo le finalità. Il consenso al fornitore era ignorato: è un bug del v2 | Consenso al fornitore e tutte le finalità dichiarate. Il bug è corretto |
optin | true in ogni consenso salvato, anche dopo «Rifiuta tutto»; false solo senza consenso | false anche dopo «Rifiuta tutto» |
| Prima visita | page_load con optin: false e liste vuote | Nessun evento fino alla prima scelta |
Cosa non fa
avacy_consent_updateneldataLayer: non sta qui. Lo manda il core a ogni webspace con Consent Mode, subito dopo l'update.window.AVACY: è dilegacy-compat.
legacy-compat
Fase post. Rimette sul sito window.AVACY del banner legacy, che il v3 non ha, cos ì i link dei clienti migrati continuano a funzionare senza toccarli (#746). L'evento avacy_consent non è qui: è consent-event.
Chi lo accende: il SaaS, da solo, sui webspace migrati da v2. Vale dal primo publish v3, anche nella migrazione forzata. Non c'è un interruttore e non ha impostazioni. Un clone lo eredita dal sorgente, perché serve lo stesso sito; lo riceve anche il clone v3 di un webspace v2. Un webspace nato v3 non lo riceve, nemmeno se è un wizard lasciato a metà e portato a v3: il banner v2 sul sito non l'ha mai avuto.
| Metodo | Cosa fa nel v3 |
|---|---|
AVACY.showPreferenceCenter(mode) | Apre il pannello in overlay, con 'absolute', 'inline' o senza argomento. Il legacy 'inline' usava il contenitore #oil-preference-center: l'add-on non lo cerca, e nessun sito censito lo usa |
AVACY.getPurposeConsents() | Promise con le finalità TCF da 1 a 11, true/false |
È il link «preferenze cookie» dei siti migrati, cioè la revoca del consenso. Se la pagina ha già un suo window.AVACY, l'add-on aggiunge solo i metodi che mancano.
Lo stub TCF non è un add-on
Fino al 12/10/2026 lo stub IAB __tcfapi era l'add-on tcf-stub, avviato dentro init(): arrivava dopo banner.json e il bundle. Ora sta in testa al loader, sincrono (#625, #628): la CDN serve la variante tcf o tcf-gcm ai webspace con TCF, leggendo i flag che il SaaS scrive a ogni pubblicazione. __tcfapi esiste prima che parta qualunque altro script della pagina.
- Lo stub è quello ufficiale IAB (
@iabtechlabtcf/stub): risponde apingconcmpStatus: 'stub'e mette in coda gli altri comandi finché il CMP vero non prende il controllo. - Si fa da parte se trova già una funzione
window.__tcfapi(lo stub legacy, un altro CMP). - Se la CDN non riesce a leggere i flag serve il loader
base, senza stub: buco accettato.
Chi fa l'inclusione manuale include lo stub come script sincrono nell'<head>, prima di qualunque script che chiami __tcfapi:
<script src="https://<tenant>.avacy-cdn.com/v3/web/<versione>/tcf-stub.js"></script>