squidfunk / mkdocs-material

Documentation that simply works
https://squidfunk.github.io/mkdocs-material/
MIT License
20.79k stars 3.53k forks source link

Feature request: cookie consent #1914

Closed chrieke closed 3 years ago

chrieke commented 4 years ago

Struggled with adding a cookie consent banner via Google Tag Manager (GTM) injection until a frontend colleague helped. Since I could not find anything about this topic related to mkdocs or mkdocs material I thought I'd open an issue here for others to google. Could maybe be added as a feature if relevant for more people, otherwise feel free to close soon.

HOWTO: Use the mkdocs theme override functionality and create a main.html file with the following javascript code taken from here. The first, custom part of the code ensures that the GTM is not being active when running mkdocs serve locally since this blocks from interacting with the site. Also see this PR and repository as an example for the structure of the overrides.

<!-- Elements added to main will be displayed on all pages -->
{% extends "base.html" %}

{% block libs %}
<!-- Google Tag Manager -->
<script>
var gtmId = 'GTM-YOURGTMCODE;
if (typeof window !== 'undefined' && window.location.href.includes('127.0.0.1')) {
  gtmId = 'GTM-XXX'
}
(function(w,d,s,l,i){
  w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer', gtmId);
</script>
  <!-- End Google Tag Manager -->
{% endblock %}

Description

If this would be added by default I could imagine it being used similarly to the Google Analytics tag in the mkdocs.yml file. Depending on your GTM configuration adds a cookie banner. However probably not relevant for too many users. Don't know much about cookies and was also told that when using Google Analytics anonymized this would not actually be neccessary (was still decided to add it for the future).

Screenshots / Mockups

image

squidfunk commented 4 years ago

Thanks for providing your solution! We switched from Google Analytics to Tag Manager at some point but reverted due to a huge increase in byte size (GTM + GA vs. GA only). Furthermore, I wonder how this complies with GDPR, as a third-party service is used to serve the cookie consent. I'll revisit this topic as soon as possible!

Are you using the Osana service, or how did you implement Cookie Consent?

Arnaud-UP42 commented 4 years ago

Hello @squidfunk - so far the load from GTM is not too heavy. We are hand-picking which tags are injected in the mkdocs by adding either an exception or a trigger based on the hostname. That way the GTM container injects very few tags in the mkdocs. These scripts are quite light - I am surprised that you had a noticeable increase in byte size.

We are using a third party to manage the cookie consent (in our case, this is powered by One Trust). There is indeed a catch: the cookie consent banner needs to drop a cookie to check if the users accepted the cookie or not - this is paradoxical but inevitable. Also, using a third party sounds counter-intuitive as well. However, to our knowledge, this is justified as a legitimate interest in providing a functional and legally certain service.

squidfunk commented 4 years ago

Is there some open source solution that doesn't need a third-party service? Or is the third-party service mandatory due to the way this must be handled?

Arnaud-UP42 commented 4 years ago

I do not know an ready-to-use open-source solution for this, unfortunately. However, a third-party service is absolutely not necessary. You could definitely build this in-house. This would all come down to designing a banner and a front-end script that allows the GTM container to load or not.

The way the one-trust banner works is that it stores the consent information in an event variable. Each type of cookie is named with a code (C1,C2,C3,C4...). If the user picks all or one of them, the code is stored in the event variable "cookie consent". From there, tags are fired in GTM depending on what is in the variable (eg: if OneTrust's variable "cookie consent" contains "C1" then trigger Google Analytics Snippet tag). I'm pretty sure it is easy to reproduce. We didn't have the time to set it up on our side though, and found this tool a faster solution, especially given the price.

But, once again, no need for a third party here. I would say that an open-source or a in-house solution is even better.

squidfunk commented 4 years ago

Thanks for clearing things up. I'd just have thought that you'd save the cookie consent in local storage and run any tracking based on whether the user has accepted cookies or not. However, there may be a thousand possible combinations of cookies, so a third-party solution might be easier to integrate and configure, as tailoring would be done by the third-party.

Arnaud-UP42 commented 4 years ago

Indeed - that is a bit how we've started the discussion here:

Far from being impossible though - the script provided by the third party doesn't seem to be something extremely advanced.

squidfunk commented 4 years ago

Thank you all for the input on this topic. I'm still wondering whether there's any value in providing a completely self-contained solution for this, i.e. asking the user, setting a value in local storage, and firing an event when the value in local storage allows for tracking. What we could do is provide a simple yes/no solution for Google Analytics. Anything else would require some customizations, obviously, as there are too many potential services to integrate. So wrapping this in a template block could work.

maximiliancs commented 3 years ago

@squidfunk What's the status on this? It's necessary to ask a user for consent before using e.g. Google Analytics. Otherwise the use wouldn't comply with GDPR. A simple yes/no solution would be really great.

squidfunk commented 3 years ago

I've moved other features up the priority list, but I will try to provide a simple yes/no implementation in the coming weeks!

champsblanc commented 3 years ago

Any updates on this?

Thanks!

squidfunk commented 3 years ago

Yes, I'm actively working on it. Can't put a time estimate on it as there are some tricky things to consider, in particular how to keep things flexible and simple at the same time, but it should be available soon.

champsblanc commented 3 years ago

Superb, thanks for all your work Martin!

squidfunk commented 3 years ago

Insiders 2.10.0 just shipped a cookie consent implementation! 🎊

It's fully integrated with Google Analytics (analytics.js and gtag.js), extensible and hackable. If enabled, the tracking scripts are only injected if the user agrees to Google Analytics (obviously). I'd still consider it experimental because I want to learn what configuration options we need from user feedback. The text is currently static but can be changed via theme extension. Furthermore, custom checkboxes can be added and handled via extension, so it's completely configurable.

https://user-images.githubusercontent.com/932156/125164904-e1860c00-e194-11eb-9df9-137d90cd3f01.mp4

JulioV commented 3 years ago

I'd show the cookie list immediately and have a Decline and Accept button. Personally, I consider the extra click a dark pattern that people usually use to force users to accept the cookies

squidfunk commented 3 years ago

@JulioV thanks for your feedback! We can make that configurable if more users desire that, but some users will surely want it that way, as it is practically the default on the Internet.

martinbira commented 3 years ago

I've had a quick go with it this morning. I build my site with 4 different builds and merge them into one larger site using Jenkins to have a custom landing page and 3 product specific "sub-sites" with separate search indexes and other configs. For me, I would need to add the consent extra to all 4 mkdocs.yml-files, which is logical and I'm 100% onboard with, especially if a visitor directly goes to a sub-site. All good so far!

But, If start on my landing-page and accept cookies, I'm immediately met with the same dialogue once I go to /sub-site-1. If this could be avoided in some way, that would be awesome.

squidfunk commented 3 years ago

Thanks for testing! That's an interesting use case I haven't thought of. In general, local and session storage are scoped to the site_url, which is necessary for deploying multiple MkDocs projects on the same domain, e.g. on GitHub pages:

Bildschirmfoto 2021-07-14 um 09 40 27

If we wouldn't scope local and session storage, the values in the storages would affect each other. In the example above, if you would go to the website of Material for MkDocs, switch to dark mode, and then to my other project Isotopes, it would also appear in the same colors.

However, I guess the cookie consent is a valid exception we could make. I'll see how we can achieve your desired behavior. maybe it's even valid to lift the scoping entirely (behind a configuration flag), so the dark mode toggle would also be consistent on your project across subsites.

martinbira commented 3 years ago

In general, local and session storage are scoped to the site_url, which is necessary for deploying multiple MkDocs projects on the same domain, e.g. on GitHub pages

Aha. Yeah my site URLs differ as they are as following: landing-page: https://www.site.com/ sub-site-1: https://www.site.com/sub-site-1/ sub-site-2: https://www.site.com/sub-site-2/ sub-site-3: https://www.site.com/sub-site-3/

squidfunk commented 3 years ago

@martinbira the latest Insiders commit adds the possibility to specify the prefix with which preferences are saved:

extra:
  scope: /

By setting it to /, it should allow you to share the following preferences across the main site and all sub sites:

Could you test the new setting and check whether it solves the problem you outlined? If that's the case, I'll issue a release and add some documentation in the coming days.

martinbira commented 3 years ago

I'll try to give it a test next week! #vacationmodeactivated

squidfunk commented 3 years ago

The latest Insiders commit parametrizes the cookie consent so that it can be completely configured from mkdocs.yml:

extra:
  consent:
    title: A custom title
    description: A custom description which can include HTML (links et al.)
    cookies:
      analytics: Google Analytics

Now, title and description are required, so make sure to update it, if you're already using the cookie consent.

The cookies parameter is optional and allows to set the title of each cookie. It defaults to the analytics cookie, if analytics is enabled. The analytics key is linked to the Google Analytics integration. Further cookies can be added in a key-value manner, e.g. if you want to get consent for further tracking scripts. For example, let's say you add a custom cookie:

extra:
  consent:
    cookies:
      custom: A custom cookie name

Then, to check whether a visitor accepted the cookie, you can add custom JavaScript and use:

var consent = __md_get("__consent")
if (consent.custom) {
 // set up custom tracking script
}

I think this is a very flexible solution that caters to a lot of cases. Also, theme extension is always an option, but I wanted to make it as easy as possible to get going. I'll update the documentation for this later on.

squidfunk commented 3 years ago

The changes have been released as part of Insiders 2.11.0, so I'd consider the cookie consent done. As noted in my last comment, it's now fully configurable. I'll improve the documentation in the coming days, adding more information on the specific way to configure it, but for now, I wanted to push it out the door.

Docsumentation: https://squidfunk.github.io/mkdocs-material/setup/setting-up-site-analytics/#cookie-consent

Feedback appreciated!

squidfunk commented 3 years ago

The custom analytics and cookie consent documentation is now up-to-date.

wilhelmer commented 3 years ago

@squidfunk According to the DSGVO, the user must be able to easily revoke his consent. So we need a way to display the cookie window again after consent has been given, e.g., by adding an "opt-out" link to the Privacy Policy page.

Clicking that link should either remove the consent setting or (preferably) display the consent modal. Could you provide a code sample for that and maybe add it to the docs?

Edit: document.querySelector("[data-md-component=consent]").hidden = false; seems to work fine. Don't know if there's a better solution.

squidfunk commented 3 years ago

That's a pretty good solution and would be exactly what I'd recommend. We probably can't add a general link to the template, because it would display on every page, but what we could do is provide a code sample in the documentation that triggers the consent again, which authors could then integrate into their privacy policy page. Maybe something like:

<button data-md-component="consent-trigger">
  Click here to change your settings
</button>

... or something similar.

wilhelmer commented 3 years ago

Yes, I'd definitely vote to add something like that to the docs, as the current solution is not DSGVO-compliant without the opt-out link/button.

squidfunk commented 3 years ago

Feel free to create a new issue, so we can keep track of it.

squidfunk commented 3 years ago

I went ahead and implemented a very simple solution. In order to display the cookie consent from anywhere in the documentation, you just have to create a simple link anywhere in your documentation (e.g. in the privacy policy):

[Change privacy settings](#__consent)

Result:

https://user-images.githubusercontent.com/932156/126369107-a6cbea59-07b0-4c0a-89a9-21c85bafec26.mp4

This will display the consent again and fill in the previously selected values, which was not done by the previously discussed solution. It's already on the master of Insiders and was documented in 79e528557.

wilhelmer commented 3 years ago

Fantastic! You're the best :trophy:

squidfunk commented 3 years ago

@martinbira did you find some time to try the new (still undocumented) extra.scope setting? See https://github.com/squidfunk/mkdocs-material/issues/1914#issuecomment-882025594.

martinbira commented 3 years ago

@squidfunk Oh, yea sorry. Vacation and all came inbetween. It works like a charm!

squidfunk commented 3 years ago

Great, thanks for testing! We can add it to the docs then.