klaro-org / klaro-js

Klaro Privacy Manager. An open-source, privacy-friendly & compliant consent manager for your website.
https://klaro.org
Other
1.19k stars 254 forks source link

Reload script add by Google Tags Manager #103

Open Mickaeldicurzio opened 5 years ago

Mickaeldicurzio commented 5 years ago

hello everybody,

I work with Klaro on project and the customer want to add scripts with google tags manager. We decided to add script with data-name allowing script by category.

GTM is allowed by Klaro too. So, all GTM script a execute after Klaro execution. I manage somehow a solution. I listen DOM changes with an mutationObserver on script and iframe and updateConsent of this category twice.

var mutationObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if (mutation.addedNodes[0] && (mutation.addedNodes[0].localName === "script" || mutation.addedNodes[0].localName === "iframe")) {
            if (mutation.addedNodes[0].dataset.name === 'cat') {
                window.klaro.getManager().updateConsent('cat', !window.klaro.getManager().getConsent('cat'))
                window.klaro.getManager().saveAndApplyConsents();
                window.klaro.getManager().updateConsent('cat',  !window.klaro.getManager().getConsent('cat'))
                window.klaro.getManager().saveAndApplyConsents();
            }
        }
    });
});
mutationObserver.observe(document.documentElement, {
    attributes: true,
    childList: true,
    subtree: true,
});

I've wonder if there's a better and more cleaner way to do.

Here is my klaro app config:

// This is a list of third-party apps that Klaro will manage for you.
    apps: [

        // The apps will appear in the modal in the same order as defined here.
        {
            name: 'google-map',
            title: 'googleMap',
            default_value: null,
            purposes: ['externaltracker'],
            description: 'ma description'
        },
        {
            name: 'iframe',
            title: 'Iframe',
            default_value: null,
            purposes: ['externaltracker'],
            description: 'my description',
        },
        {
            name: 'gtm',
            title: 'Google Tag Manager',
            default_value: null,
            purposes: ['externaltracker'],
            description: 'my description',
        },
        {
            name: 'recaptcha',
            title: 'recaptcha',
            default_value: null,
            purposes: ['externaltracker'],
            description: 'my description'
        },
        {
            name: 'cat',
            title: 'my first category',
            default_value: null,
            purposes: ['externaltracker'],
            description: 'my description',
        },
    ],

thanks for your answers !! :smile:

jaller94 commented 5 years ago

Hi, is this a general question or is something not working? If something is not working could you describe how to reproduce the issue?

I'm not familiar with Google Tags Manager, but maybe someone else reading this has a good solution for you. :)

What is the observer listing to and when is that action toggling the consent management in Klaro? What does the abbreviation(?) cat stand for? Category? 🐱

amenk commented 4 years ago

Are you trying to toggle the scripts which are added by Google Tag Manager? So you changed the tags in GTM too? Which of the tags come from GTM? Interesting solution, do not really have a better idea.

amenk commented 4 years ago

ping @Mickaeldicurzio

Mickaeldicurzio commented 4 years ago

update :

var mutationObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if (mutation.addedNodes[0] && (mutation.addedNodes[0].localName === "script" || mutation.addedNodes[0].localName === "iframe")) {
            // console.log(mutation.addedNodes[0].dataset.name);
            let categoryName = mutation.addedNodes[0].dataset.name;
            let webpack = window.klaro.getManager();
            if (typeof(categoryName) !== 'undefined' && categoryName.includes('lazy-')) {
                webpack.updateConsent(categoryName, !window.klaro.getManager().getConsent(categoryName))
                webpack.saveAndApplyConsents();
                webpack.updateConsent(categoryName, !window.klaro.getManager().getConsent(categoryName))
                webpack.saveAndApplyConsents();
            }
        }
    });
});

// Starts listening for changes in the root HTML element of the page.
mutationObserver.observe(document.documentElement, {
    childList: true,
    subtree: true,
});
Mickaeldicurzio commented 4 years ago

ping @amenk

We have put this script in order to listen dom element injected by GTM. If a script contain an data-name attribute started by "lazy-", we will inject it to Klaro

exemple:

<script data-type="text/javascript" data-name="lazy-pub" type="opt-in" data-src="somescript.js" async="true"></script>

but, unfortunately, we have to update consent 2 time for make it works.

webpack.updateConsent(categoryName, !window.klaro.getManager().getConsent(categoryName))
webpack.saveAndApplyConsents();
webpack.updateConsent(categoryName, !window.klaro.getManager().getConsent(categoryName))
webpack.saveAndApplyConsents();
Mickaeldicurzio commented 3 years ago

well, I will make an Update ( for everyone who have the same issues ) Observers work but it can appear to be a bas solution. In fact, it can create infinte loop on prod envs.

I recommend now to use dataLayer from GTM : https://developers.google.com/tag-manager/devguide and push event in klaro calback for activate gtm tags

callback: function(consent, app) {
            if (consent) {
                dataLayer.push({'event': 'gtm-event-1'});
            }
        }
caioricciuti commented 3 years ago

@Mickaeldicurzio My problem is that I have a lot of fields to consent.. (google_analytics, facebok, etc) and every time config.js loads (which is every page) the call back runs and push all the consents one by one in data layer... Did you know a way to send all the consents in one push? Like an array or something else?

this is my func so far, I would like to send only once with all data, it this possible, can you help me? Thanks a lot!

  callback: function (consent, service) {
    dataLayer.push({
      event: "consent",
      tool: service.name,
      consent: consent,
    });
  },
};