Passa al contenuto principale

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-onFaseCosa fa
preemptive-blockpreSblocca gli script e gli iframe marcati nell'HTML quando c'è il consenso che chiedono
consent-archivepostInvia ogni scelta del visitatore all'archivio consensi del webspace
consent-eventpostManda l'evento avacy_consent del banner legacy sulla window, su ogni webspace v3
legacy-compatpostRimette 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-archive dal toggle di archiviazione automatica dei consensi, con l'URL generato dal SaaS;
  • preemptive-block quando almeno un fornitore custom del webspace ha URL di blocco preventivo;
  • consent-event su ogni webspace v3, sempre;
  • legacy-compat quando 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​

FaseQuando si scaricaIl boot lo aspetta?
preSubito, in parallelo con il bundleSì: init() aspetta i pre annunciati dal loader fino a core.addonsTimeoutMs (default 1500 ms, in tutto)
postDopo il primo avacy:readyNo

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:

ChiaveTipoDefaultDescrizione
placeholderbooleanfalseDisegna 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>
AttributoObbligatorioDescrizione
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-requiressìI requisiti di consenso, vedi sotto. È l'attributo che marca l'elemento: senza, l'add-on non lo vede.
data-avacy-srcper gli script esterniL'URL vero. Assente o vuoto = script inline (il contenuto del tag).
data-avacy-typenoIl 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,9 vuol dire 1 e 8 e 9.
AsseCosa controllaEsempio
vendorconsenso al fornitoretcf:vendor:755, custom:vendor:d116, acm:vendor:89
purposeconsenso alla finalità (per custom legge le finalità custom)tcf:purpose:1, gcm:purpose:4, custom:purpose:3
customPurposeconsenso a una finalità custom (stesso effetto di custom:purpose)custom:customPurpose:3
specialFeatureconsenso a una special feature TCFtcf:specialFeature:1
legintlegittimo 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:

  1. Serve un consenso valido. Prima della scelta del visitatore non si sblocca niente.
  2. Un id che non compare tra le scelte non è concesso: un fornitore tolto dalla lista non apre nulla.
  3. Framework non configurato. Un requisito su un framework che il webspace non ha (es. tcf:purpose:1 senza TCF in frameworks) ripiega sul fornitore custom della stessa marcatura: è soddisfatto se i requisiti custom:vendor:… dell'elemento sono concessi. Senza un fornitore custom nella marcatura, l'elemento resta bloccato.
  4. 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.
  5. 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.

warning

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', …) trova gtag.js già 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:

LegacyEquivale a
data-managed="as-oil"data-avacy-requires (marca l'elemento)
data-srcdata-avacy-src
data-typedata-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.


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.

ChiaveTipoDefaultDescrizione
urlstring— (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": "…" }
optinBottone
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 come avacy: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_id con 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, policyVersion alzata), 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.


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:

triggerQuando
page_loadAl caricamento, se c'è un consenso salvato
consent_updateA ogni scelta, anche dopo un rifiuto
window.addEventListener('avacy_consent', (e) => {
e.detail.customVendorsWithConsent.forEach((v) => console.log(v.id, v.name));
});
CampoContenuto
optinfalse solo dopo un «rifiuta tutto» (differenze)
purposes, legitimateInterests, specialFeaturesId TCF con consenso o legittimo interesse
iabVendorsId dei fornitori IAB con consenso
iabVendorsWithConsentGli stessi, come { id, name }: basta il consenso al fornitore, senza guardare le finalità
customPurposes{ id, c, li? }, con 1/0 come nel cookie legacy
customVendorsTutti i fornitori custom, { id, consent }, con 1/0 come nel legacy
customVendorsWithConsentQuelli con consenso al fornitore e a tutte le finalità che dichiarano, { id, name } (regola)
gcmI 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 riferimenti acm si 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 v2v3 con consent-event
iabVendorsWithConsentIl 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
customVendorsWithConsentContava solo le finalità. Il consenso al fornitore era ignorato: è un bug del v2Consenso al fornitore e tutte le finalità dichiarate. Il bug è corretto
optintrue in ogni consenso salvato, anche dopo «Rifiuta tutto»; false solo senza consensofalse anche dopo «Rifiuta tutto»
Prima visitapage_load con optin: false e liste vuoteNessun evento fino alla prima scelta

Cosa non fa​

  • avacy_consent_update nel dataLayer: non sta qui. Lo manda il core a ogni webspace con Consent Mode, subito dopo l'update.
  • window.AVACY: è di legacy-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.

MetodoCosa 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 a ping con cmpStatus: '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>