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 tre:
| 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 |
tcf-stub | pre | Installa subito lo stub IAB __tcfapi sui webspace con TCF |
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;tcf-stubnon sta inaddons[]del SaaS: lo aggiunge il loader quandoframeworkscontienetcf(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, tcf-stub compreso. Su un webspace TCF va ripetuto: { "addons": ["preemptive-block", "tcf-stub"] }.
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.
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="") = elemento lasciato bloccato. → 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.
tcf-stub
Fase pre. Installa lo stub ufficiale IAB (@iabtechlabtcf/stub): un window.__tcfapi minimo che risponde a ping con cmpStatus: 'stub' e mette in coda gli altri comandi finché il CMP vero non prende il controllo. Senza, un fornitore che chiama __tcfapi prima del banner riceve un errore e spesso rinuncia per tutta la pagina.
- Acceso dal loader, non dal SaaS: se
frameworksdibanner.jsoncontienetcf, il loader lo aggiunge sia ai download sia adaddons[]. Un webspace senza TCF non ha__tcfapi. - Si fa da parte se trova già una funzione
window.__tcfapi(lo stub incluso a mano, il CMP già partito, un altro CMP): non la sovrascrive. - Arriva tardi per i primissimi istanti: parte dentro
init(), dopobanner.json, il bundle e il proprio file. Un fornitore che chiama__tcfapiprima può ancora non trovarlo.
Un sito che non può permettersi quella finestra, e 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>
Con lo stub già in pagina, l'add-on vede __tcfapi e non fa nulla.