AmauriC / tarteaucitron.js

RGPD friendly cookie manager
https://tarteaucitron.io/
MIT License
931 stars 370 forks source link

Ne pas bloquer le Google Tag Manager via tarteaucitron #1066

Closed SW-Vincent closed 1 year ago

SW-Vincent commented 1 year ago

Bonjour,

Nous utilisons actuellement la version pro de tarteaucitron et je voudrais être sûr de comprendre certains éléments fonctionnels.

Dans l'idéal, nous aimerions fonctionner en mode automatique, avec une gestion différenciée des tags envoyés par GTM (sans demander le consentement GTM comme cela semble être le cas actuellement). Cela requiert-il une implémentation spécifique de GTM ? D'intégrer tarteacitron via GTM ? Est-ce impossible avec la version pro ?

Note : Si j'ai bien compris, le mode manuel agit quand à lui comme un TMS qui supplanterait alors GTM pour les global site Tags (mais qui demande tout de même le consentement GTM, même s'il n'est pas paramétré dans les services).

AmauriC commented 1 year ago

Bonjour,

En v pro il y a le mode automatique, si il est actif, il va bloquer tous les services supportés par tarteaucitron et les charger quand l'utilisateur donnera son accord. Il n'est pas possible d'ajouter des exceptions et GTM fait partie des services nécessitant un consentement.

Si vous voulez charger GTM tout le temps, il faut passer au mode manuel ou placer le tag Javascript de GTM au dessus dans le code HTML du marqueur tarteaucitron.

SW-Vincent commented 1 year ago

Bonjour, merci pour ces informations.

C'est bien ce qu'il me semblait avoir compris pour le fonctionnement du mode automatique, le problème étant que bloquer GTM ne répond pas à la notion de "1 objectif -> 1 recueil de consentement" du RGPD.

Ce qui me trouble en mode manuel c'est que GTM est soumis à consentement même si je ne le paramètre pas.

Comme vous me l'avez proposé, je vais essayer de mettre le code GTM au dessus du code tarteaucitron pour voir si le mode automatique détecte alors les autres éléments (GA, Google Ads, etc.). Une solution pourrait aussi être d'intégrer tarteaucitron via GTM et les nouveaux déclencheurs "Consent Initialization" (un déclencheur page vue qui se déclenche nécessairement avant les autres).

Je reviens vers vous dès que ces solutions ont été testées.

AmauriC commented 1 year ago

Charger tarteaucitron depuis GTM est aussi une solution.

Pour le blocage auto de GTM en mode manuel c'est impossible, par contre le fichier tarteaucitron de configuration est mis en cache sur le navigateur 24h, si vous ne supprimez pas le cache, vous avez toujours l'ancienne conf (mode auto). Ca explique probablement le problème ;)

SW-Vincent commented 1 year ago

Il semblerait que GTM aie été bloqué à cause de la méthode d'intégration (problème résolu, merci pour tout !).

Quelques éléments me semblent nécessiter des correctifs / intégrations spécifiques mais mon point d'attention principal est le recaptcha : avec la version automatique, le bloc reCAPTCHA disparait tout bonnement (et l'envoi du formulaire échoue). Existe-t-il une méthode permettant d'avoir la possibilité de réactiver le consentement avant d'envoyer le formulaire ? Je m'excuse si cela fait écho à un autre ticket, mais je n'ai pas réussi à trouver mon bonheur dans les tickets existants sur le sujet.

AmauriC commented 1 year ago

En mode auto il y a normalement un call to action pour activer recaptcha à la place du tag Javascript. Par contre il est possible que ce soit dans le header donc invisible ou caché dans le footer en fonction de où est le tag de chargement du fichier js recaptcha. Avec l'url de la page je pourrait sûrement trouver une solution ;)

SW-Vincent commented 1 year ago

Voici l'équivalent prod de la page avec recaptcha : (edit : suppression du lien)

Note : Tarteaucitron n'est pas (encore) installé sur cet environnement, puisque pour l'instant les tests sont effectués en préprod

AmauriC commented 1 year ago

C'est bien ce que j'explique au dessus, le tag javascript recaptcha (<script src="https://www.google.com/recaptcha/api.js" type="text/javascript"></script>) est tout en bas du site, le bouton d'activation de tarteaucitron sera à cet endroit. Il faudrait placer ce tag à l'endroit ou le recaptcha doit s'afficher.

SW-Vincent commented 1 year ago

Bonjour Amauri,

Je reviens vers toi ayant actuellement un problème similaire à celui décrit ici : https://github.com/AmauriC/tarteaucitron.js/issues/1072 mais pour la version pro.

Afin d'ajouter les lignes de manière à être exécutée à la suite du script tarteaucitron pro :

// Force load AFTER init to avoid losing custom configuration
console.log('[tarteaucitron] force trigger load event');
tarteaucitron.initEvents.loadEvent(false);

J'ai tenté d'utiliser un callback :

(function() {
    function loadScript(src, callback) {
      var s,
        r,
        t;
      r = false;
      s = document.createElement('script');
      s.type = 'text/javascript';
      s.src = src;
      s.onload = s.onreadystatechange = function() {
        //console.log( this.readyState ); //uncomment this line to see which ready states are called.
        if (!r && (!this.readyState || this.readyState == 'complete')) {
          r = true;
          callback();
        }
      };
      t = document.getElementsByTagName('script')[0];
      t.parentNode.insertBefore(s, t);

      console.log('[tarteaucitron] force trigger load event');
      tarteaucitron.initEvents.loadEvent(false);
    }

    loadScript("https://tarteaucitron.io/load.js?domain=MY-DOMAIN&uuid=MY-ID", function() {
      // Callback function
    });
  })();

mais cela ne semble pas fonctionner comme attendu. Auriez-vous une piste ?

AmauriC commented 1 year ago

Salut,

Je ne comprend pas la problématique?

Il y a l'event window.addEventListener('tac.root_available', function() {});, ça ne peux pas servir?

SW-Vincent commented 1 year ago

Ma problématique est d'intégrer tarteaucitron pro, si possible en automatique, via GTM. La problématique elle même est très proche du ticket #1072 mentionné plus haut, et l'événement tac.root_available n'est jamais déclenché (ce qui, j'imagine, explique en partie certains dysfonctionnements du bandeau).

Dans mon cas, l'ajout de la ligne tarteaucitron.initEvents.loadEvent(false); ne fonctionne pas, certainement parce que l'utilisation du mode auto ne permet pas le même contrôle dans l'ordre de déclenchement des instructions (je suppose que pour ce la solution fonctionne, la ligne doit être appelée après les tags).

La version la plus proche d'un fonctionnement normal du code est d'intégrer le code suivant, qui ajoute seulement un événement lisible une fois le script source exécuté :

<script >
    (function() {
        function loadScript(src, callback) {
            var s,
                r,
                t;
            r = false;
            s = document.createElement('script');
            s.type = 'text/javascript';
            s.src = src;
            s.onload = s.onreadystatechange = function() {
                console.log(this.readyState); //uncomment this line to see which ready states are called.
                if (!r && (!this.readyState || this.readyState == 'complete')) {
                    r = true;
                    callback();
                }
            };
            t = document.getElementsByTagName('script')[0];
            t.parentNode.insertBefore(s, t);
        }

        loadScript("https://tarteaucitron.io/load.js?domain=MY-DOMAIN&uuid=MY-ID", function() {
            console.log('[tarteaucitron] services config');

            document.addEventListener('tac.root_available', function() {
                console.log('[tarteaucitron] root_available'); //Jamais exécuté
            });

            if (dataLayer) {
                dataLayer.push({
                    'event': 'tarteaucitronReady'
                });
            }

        });
    })(); 
</script>

L'événement tarteaucitronReady sert de déclencheur aux autres tags.

J'ai l'impression que cette manipulation ne fonctionne pas tout à fait comme elle le devrait car :

Dans l'hypothèse où certains tags ou événements (ex : clics) pourraient se déclencher avant la fin de l'initialisation de TAC, est-ce qu'une syntaxe comme suit est prise en compte correctement ? En pratique cela ne devrait pas arriver mais si j'intègre manuellement un pixel puis TAC, le code suivant fonctionne-t-il ?

var tarteaucitron = tarteaucitron || {};
  tarteaucitron.user = tarteaucitron.user || {}
  tarteaucitron.user.googleadsId = 'AW-XXXXXX';
  (tarteaucitron.job = tarteaucitron.job || []).push('googleads');
AmauriC commented 1 year ago

L'event tac.root_available est sur window., pas sur document., ça explique pourquoi il n'est pas lancé.

Pour la non détection, c'est sûrement parce que Gads est chargé avant tarteaucitron. Le mode auto s'initialise et analyse le chargement des balises script/iframe pour les bloquer si besoin mais ne peux pas agir si le chargement est déjà effectué. Ou alors c'est le fingerprint qui n'est pas bon? tarteaucitron cherche un script avec AW- comme paramètre.

Pour GA3-4 c'est voulu comme google utilise le même tag pour certains comptes.

Pour la confusion Facebook Ads je vais regarder si je peux faire quelque chose.

Utiliser le mode manuel avec le mode auto actif ne fonctionnera pas, le service ne sera jamais chargé, le fonctionnent est différent :/

SW-Vincent commented 1 year ago

Merci pour ces réponses !

Pour Facebook Ads, le custom HTML (tarteaucitron.job....) résoud le problème, mais je pense que c'est effectivement un défaut du mode auto. Pour Google Ads, le custom HTML a également résolu le problème. Je pense donc que le fonctionnement "manuel" (avec le mode auto inactif afin d'avoir un contrôle optimal et éviter les disparités selon les temps de chargement d'une session à l'autre) est la solution pour l'intégration via TMS.

J'aurais quelques quelques questions additionnelles :

AmauriC commented 1 year ago

En résumé, passage en mode manuel et chargement des services via GTM quand l'accord est donné? Si oui c'est tout bon :)

SW-Vincent commented 1 year ago

En résumé on est effectivement sur du mode manuel, avec un chargement des services via GTM à tac.root_available (ceci dit cela a l'air de fonctionner même si les tags se déclenchent avant).

Je suis en train de travailler sur l'ajout de paramètres aux tags et sur la syntaxe des tags de conversions pour les différentes régies), ce qui soulève un besoin de clarification sur certains éléments.

1) Sur GA4, le domaine du cookie est paramétré via la même instruction que la configuration. De fait si je rajoute simplement le cookie domain dans gtagMore il y a un double déclenchement de la page vue. Dois-je retirer la première ligne et renseigner la configuration dans autre ? Ex :

  //tarteaucitron.user.gtagUa = 'G-XXXX'; // ligne rendue inactive
  tarteaucitron.user.gtagMore = function () {
    gtag('config', 'G-XXXX', {
      'cookie_domain': 'top-level-domain'
    });
  };
  (tarteaucitron.job = tarteaucitron.job || []).push('gtag');

Ou alors existe-t-il une autre syntaxe du style :

  tarteaucitron.user.gtagUa = {'G-XXXX', {'cookie_domain': 'top-level-domain'}} 
  tarteaucitron.user.gtagMore = function () {};
  (tarteaucitron.job = tarteaucitron.job || []).push('gtag');

2) Il me reste aussi comme mentionné plus haut (je ne suis pas sûr d'avoir reçu une réponse là dessus) une interrogations sur l'éventualité de tags se déclenchant avant TAC, comme le clic sur un CTA si la page est mal optimisée et que l'utilisateur a une mauvaise connexion (ou l'intégration en dur de la version TAC du code recaptcha, qui serait lue avant l'appel de TAC)

AmauriC commented 1 year ago

Il y a une option gtagCrossdomain mais je ne sais pas si c'est la même chose que le cookie_domain: https://github.com/AmauriC/tarteaucitron.js/blob/master/tarteaucitron.services.js#L2644 La deuxième syntaxe est incorrecte et bloquera la chargement du script.

En mode manuel c'est tarteaucitron qui va charger le tag, il ne faut pas que vous les chargiez aussi via GTM sinon ça va faire doublon (voir le lien ci-dessus pour GA4).

SW-Vincent commented 1 year ago

Je suis conscient de l'option crossdomain, mais ce n'est pas un paramétrage nécessaire pour du subdomain tracking. Dans l'ensemble, plutôt que d'envoyer quelque chose comme gtag(config, 'MY-ID') je me questionne sur la meilleure démarche pour envoyer gtag(config, 'MY-ID', {some-parameters}). Utiliser l'option gtagMore à la place de gtagUa semble mener au bon déclenchement du tag avec les paramètres spécifiés, mais je vois aussi qu'il est ajouté au dataLayer "gtag config undefined" donc je me demande si ce n'est pas une mauvaise pratique.

Pour ce qui est du mode manuel, j'utilise le mode manuel avec 0 services et je pousse le tags avec la syntaxe TAC via GTM (tags custom HTML) pour avoir plus de contrôle sur leur contenu.

Note : pour l'envoi d'événements, utiliser gtagMore entraîne le même phénomène (initialisation d'une configuration "undefined" en plus de l'envoi correct de l'événement).

AmauriC commented 1 year ago

Si le gtagUA est absent, le script gtm sera chargé sans l'identifiant: https://www.googletagmanager.com/gtag/js?id=undefined

Il n'y a pas une option sur GA pour changer la config sans tout recharger?

SW-Vincent commented 1 year ago

Il est possible de ne pas envoyer la page vue via le code suivant, mais je ne suis pas sûr qu'envoyer le hit de page vue puis changer la config soit idéal (d'où l'idée de ne pas renseigner le tag-id dans tarteaucitron.user.gtagUa) :

gtag('config', 'TAG_ID', {
    send_page_view: false
});

Aussi, la même question se pose pour l'envoi d'événements avec la syntaxe TAC. Par exemple, si je veux envoyer le tag suivant,lorsque l'utilisateur clique sur un CTA, je suis obligé de désactiver la première ligne, sinon un doublon est déclenché avec le tag de page vue.

<script type="text/javascript">
  //tarteaucitron.user.gtagUa = 'TAG-ID'; //Envoie une page vue si n'est pas désactivé
  tarteaucitron.user.gtagMore = function () {
    gtag('event', 'exempleEventName',{'send_to': 'TAG-ID'});
  };
  (tarteaucitron.job = tarteaucitron.job || []).push('gtag');
</script>

A moins que seuls les tags "page vue" nécessitent le consentement ? (si on considère que l'absence d'une config rend le tag inopérant). Dans ce cas pourquoi certaines régies seulement ont un tag de conversion TAC et pas d'autres (Xandr et Google Adwords par exemple - https://tarteaucitron.io/fr/install/)

AmauriC commented 1 year ago

Il faut le consentement quand des données sont envoyées, peu importe que ce soit une page ou un event.

Le plus simple est de charger GA via tarteaucitron, comme ça le consentement est correctement géré et ensuite ajoutez vos gtag('event', 'exempleEventName',{'send_to': 'TAG-ID'}); là où vous en avez besoin en direct, si GA n'a pas été autorisé, GA n'est pas chargé et la fonction gtag() n'existera pas.

Pour les régies absentes, c'est que personne n'en a eu besoin ou n'a fait la démarche pour que ce soit ajouté sur tac.

SW-Vincent commented 1 year ago

Dans le cas spécifique de GA, j'utilise la syntaxe suivante (ça pourrait résoudre le problème d'autres personnes) :

<script type="text/javascript">

  //L'option gtagMore est utilisée pour initialiser le tracking en changeant le domaine du cookie
  //Le domaine du cookie est le domaine principal, afin de partager les données entre les sites qui le partagent
  //Le callback permet d'envoyer un événement personnalisé lorsque GA est prêt, à utiliser avec la fonctionnalité groupes de déclencheurs de GTM

  //tarteaucitron.user.gtagUa = 'G-ID';  //N'est pas utilisé
  tarteaucitron.user.gtagMore = function () {
    gtag('config','G-ID',{
      'cookie_domain': {{JS - Domaine principal}}, //variable JS permettant de récupérer le domaine principal (ex : domain.com au lieu de subdomain.domain.com)
      'send_page_view': false
    });
    gtag('event', 'page_view',{
      'send_to': 'G-ID',
      'event_callback': function() {
        dataLayer.push({'event' : 'ga4Ready'}); 
      }
    }); 
  };

  (tarteaucitron.job = tarteaucitron.job || []).push('gtag');
</script>

Cela me permet de m'assurer (grâce à l'événement personnalisé ga4Ready et à la fonction 'groupes de déclencheurs' de GTM) de m'assurer que tout événement GA4 se déclencherait après le tag GA4 (i.e. pas de risque de mauvais ordre de déclenchement alors que le consentement est donné, et ne se déclenche que si le consentement est donné).

L'inconvénient ici c'est que :

Dans le cas de ce tag on initie aussi des services "undefined" mais ça ne devrait pas poser problème tant que je spécifie "send-to" dans l'envoi de mes hits.

De ce que je vois sur le ticket #1072 il est possible d'ajouter un event listener pour peu que l'option "more" existe, mais j'ai l'impression que cette option n'existe pas pour certaines régies (ex : Xandr). Y aurait-il une alternative pour déclencher du code après que le pixel soit initialisé ?

Est-il possible de modifier le script tarteaucitron appelé de la façon suivante afin de suivre le déclenchement du script (et éventuellement conditionner dessus le déclenchement d'autres scripts ? ) :

(function() {
    var oldXandrJs = tarteaucitron.services.xandr.js;

    tarteaucitron.services.xandr.js = function() {
        oldXandrJs();

        console.log("script xandr exécuté");
        //dataLayer.push('event':'script-xandr-execute')
    };
})();

De ce que je vois la fonction associée au tag (ici window.pixie) serait alors définie avant mon événement personnalisé, donc un événement basé sur ce dernier même sile script xandr n'a pas fini d'être importé (https://github.com/AmauriC/tarteaucitron.js/blob/master/tarteaucitron.services.js#L899)

SW-Vincent commented 1 year ago

Bonjour @AmauriC,

Comme je le mentionnais plus haut j'ai le besoin de pouvoir détecter l'initialisation d'un service TAC. Lorsque l'élément more existe (ex : gtagMore), il est possible de s'en servir pour avoir quelque chose de satisfaisant mais ce n'est pas toujours le cas.

J'ai voulu utiliser un peu de code pour pousser une instruction exploitable dans chaque fonction service.js(), mais le problème avec ça est que cette instruction se déclenche avant le script appelé via tarteaucitron.addScript. Ici l'instruction ajoutée est un console.log() mais ça pourrait aussi bien être dataLayer.push

Voici ci-dessous mon code, penses-tu qu'il soit possible de l'améliorer pour aboutir à un résultat satisfaisant (i.e. envoyer une information quand le service a été initialisé pour les services "more" n'existant pas) :

(function() {
    function loadScript(src, callback) { //Ajoute des instructions au script source
        var s,
            r,
            t;
        r = false;
        s = document.createElement('script');
        s.type = 'text/javascript';
        s.src = src;
        s.onload = s.onreadystatechange = function() {
            if (!r && (!this.readyState || this.readyState == 'complete')) {
                r = true;
                callback();
            }
        };
        t = document.getElementsByTagName('script')[0];
        t.parentNode.insertBefore(s, t);
    }

    loadScript("https://tarteaucitron.io/load.js?domain=musee-armee.fr&uuid=MY-ID", function() {
        console.log('[tarteaucitron] services config');

        //Crée un déclencheur personnalisé lorsque tarteaucitron est prêt, ce déclencheur est utilisé pour les tags soumis à consentement
        window.addEventListener('tac.root_available', function() {
            // Ajouter une fonction pour exécuter chaque fois qu'un service se termine
            console.log('custom script init');
            for (var serviceKey in tarteaucitron.services) {
                if (tarteaucitron.services.hasOwnProperty(serviceKey)) {
                    if (typeof tarteaucitron.services[serviceKey].js === 'function') {
                        var service = tarteaucitron.services[serviceKey];
                        if (!service.js.__tarteaucitron) {
                            // Référence à la fonction d'origine
                            var originalFunction = service.js;

                            // Création d'une fonction de clôture pour capturer serviceKey
                            var newFunction = (function(key) {
                                return function() {
                                    originalFunction.call(this); // Appel à la référence à la fonction d'origine
                                    console.log("service", key, "executed");
                                }
                            })(serviceKey);

                            // Redéfinition de la fonction d'origine
                            service.js = newFunction;

                            // Permet d'indiquer que la fonction a été modifiée pour éviter une boucle infinie
                            service.js.__tarteaucitron = true;
                        };
                    };
                };
            };

            console.log('[tarteaucitron] root_available');
            if (dataLayer) {
                dataLayer.push({
                    'event': 'tarteaucitronReady'
                });
            };
        });

    });
})();
AmauriC commented 1 year ago

Ce ne serait pas plus simple d'utiliser l'event {SERVICE_KEY}_loaded ?

Exemple pour google maps: document.addEventListener('googlemaps_loaded', function() {});

SW-Vincent commented 1 year ago

Ce ne serait pas plus simple d'utiliser l'event {SERVICE_KEY}_loaded ?

Exemple pour google maps: document.addEventListener('googlemaps_loaded', function() {});

C'est le genre de fonctionnalité que je cherchais, effectivement et cela simplifie mon script. Cependant d'après mes tests cela pose le même problème, car l'événement {SERVICE_KEY}_loaded n'est pas appelé dans la fonction tarteaucitron.addScript() mais en parallèle de cette fonction (car le script appelé est toujours asynchrone).

C'est probablement la raison pour laquelle certaines personnes utilisent l'option more (malheureusement indisponible pour certaines régies).

AmauriC commented 1 year ago

Il faudrait donc un callback en plus dans le addScript.

Pas idéal mais un setTimeout de 600ms dans l'event _loaded règle souvent le problème.

SW-Vincent commented 1 year ago

Entendu, merci.

Aussi, je ne sais pas si c'est voulu mais parfois l'option moren'est pas toujours dans le callback de la fonction tarteaucitron.addScript, elle est parfois après (et peut donc se déclencher avant que le code de suivi n'ait été initialisé).

Par exemple, l'option more est dans le callback de tarteaucitron.addScript pour le service gtag mais pas pour le service tiktok.

SW-Vincent commented 1 year ago

Bonjour @AmauriC,

L'ajout d'un callback en plus dans le addScript comme énoncé dans votre message précédent est-il prévu ? Il est important pour moi de savoir si la solution des 600ms est temporaire ou permanente.

AmauriC commented 1 year ago

Ce serait bien mais je ne sais pas trop comment faire. Dans la fonction addScript il n'y a pas l'id du service, juste l'url du script.

Est-ce que faire un callback avec le chemin du script serait pertinent?

SW-Vincent commented 1 year ago

Il y a selon moi plusieurs options avec leurs avantages et leurs défauts :

*exemple :

tarteaucitron.addScript('https://amplify.outbrain.com/cp/obtp.js', '', function () {
            obApi('track', 'PAGE_VIEW');
        });

deviendrait

tarteaucitron.addScript('https://amplify.outbrain.com/cp/obtp.js', '', function () {
            obApi('track', 'PAGE_VIEW');
            `tarteaucitron.sendEvent('outbrain_initialized');`
        });

C'est peut-être une question bête de ma part mais y a-t-il une doc officielle ? J'ai eu du mal à trouver des informations sur par exemple {SERVICE_KEY}_loaded en dehors de quelques tickets github et du code directement.

AmauriC commented 1 year ago

L'option 3 est celle que j'envisageait. J'essai de m'en occuper au weekend.

Toujours pas de doc en dehors de Github, c'est aussi dans les choses à faire mais il y en a beaucoup :p

SW-Vincent commented 1 year ago

Bonjour @AmauriC, avez-vous des nouvelles sur le sujet ?

stale[bot] commented 1 year ago

This issue has been automatically marked as inactive because it has not had activity for 20 days. It will be closed in 5 days if no further activity occurs. Thank you for your contributions.