Giter Site home page Giter Site logo

cookies_demo's Introduction

COOKiES Demo

Introduction

The COOKiES module for Drupal 8+9 prevents the installation of cookies in accordance with the GDPR until the user has given his consent that cookies may be installed. There are many third-party integration modules on Drupal.org that have gotten into a dilemma due to the newer GDPR: They can only be used in compliance with GDPR if they could connect to user-consent management, which not yet exists within the Drupal API. There are many external Drupal user-consent-management systems that independently integrate third-party services. If you use these external Drupal tools, you would have to do without the configurability, authorizations and control that are provided by corresponding third-party integration modules (Contrib modules). - The COOKiES module closes this gap by providing user consent management within the Drupal API.

In principle, developers of third-party integration modules could connect their modules directly to the COOKiES consent management; Of course they wouldn't do that as long as COOKiES is not in the Drupal core. However, there is an alternative to bridge the gap between a third-party-integration module and the COOKiES module: namely by means of an additional bridge module that intercepts third-party integration within the Drupal API (knock-out) and re-animates as soon as the user has given his consent in the COOKiES UI. - In this way, the third-party integration module remains independent of COOKiES and can still use its user consent management.

The COOKiES Demo Module should show how this works and help to create your own bridge modules.

PS.: We collect bridge modules to combine them in a COOKIES extend module. If you have developed a bridge module for a frequently used third-party integration module, please send us your work. (J. Feltkamp)

What has a bridge module to do to connect my third-party-integration module to COOKiES?

  1. In the COOKiES UI, a new user decision for my third-party-integration module must be added.
  2. The third-party integration must be effectively intercepted (knock-out).
  3. The third-party integration must be reactivated after the user has given consent (re-animation) - without negatively affecting its function.

1. Add a service requiring approval

To add another point to the list of services requiring approval in the COOKiES UI, all you need is a "cookies.cookies_service" type of config entity, which is stored in the "config / install" folder of the bridge module. (see example)

2. Knock-out the third-party integration

An effective knock-out of third-party integration can only take place on the server side. Third-party cookies are installed (essentially) in two ways: either by a JavaScript that is loaded from a remote server, or by an iframe, that loads its contents from a remote server. In order to prevent the execution of a JavaScript, it is sufficient to set the attribute type="text/plain" to the script tag. This way, all content stays as it is, and it's easy to re-animate the Javascript later.

<!-- before -->
<script src="https://ext-service.net/js/install_many_cookies.js"></script>
<!-- after (knocked out javascript) -->
<script type="text/plain" data-sid="extservice" src="https://ext-service.net/js/install_many_cookies.js" ></script>

To prevent an iFrame from installing cookies you must manipulate the src attribute and preventing the iframe from loading the external page. We can write the content of the src attribute to another attribute e.g. data-src.

<!-- before -->
<iframe src="https://www.youtube.com/embed/XGT82nnmF4c" width="560" height="315"></iframe>
<!-- after (knocked out iframe) -->
<iframe src="" data-sid="youtube" data-src="https://www.youtube.com/embed/XGT82nnmF4c" width="560" height="315"></iframe>

The Drupal hook system offers extensive options for manipulating source code, libraries, header tags, etc. Actually, it's always the same 3-5 hooks that you need to get to your goal.

For Javascript:

hook_library_info_alter();
hook_page_attachments();
hook_js_alter();

For iframes (if they are based on a field)

hook_preprocess_field();

Re-animate the third-party integration

These knock-outs are re-animated in the frontend using Javascript. As soon as the user saves his consent in the COOKiES-UI (and every time a page is loaded) a Javascript event "cookiesjsrUserConsent" is fired, in which the decisions of the user are communicated.

Re-animate a <script/>

The re-animation takes place in such a way that an event listener for the event "cookiesjsrUserConsent" must be set up, and depending on how the user has decided regarding my service, the Javascript or the iframe is re-animated.

document.addEventListener('cookiesjsrUserConsent', function (event) {
  var service = (typeof event.detail.services === 'object') ? event.detail.services : {};
  if (typeof service['cookies_demo'] !== 'undefined' && service['cookies_demo']) {
    // Manipulate DOM to reanimate your third-party integration.
  }
});

For the Javascript, which we paralyzed with the type attribute, it is not enough to simply remove the attribute again or to correct its content. We still have to make sure it actually runs. We do this by cloning the script tag, then removing the type attribute, and replacing the old tag with the new one. Then the Javascript is executed immediately.

jQuery('script[data-sid="extservice"]').each(function() {
    var $replacement = jQuery(this).clone().removeAttr('type');
    jQuery(this).replaceWith($replacement);
});

Re-animate a <iframe/>

We have it easier with the iframe. Here we only need to read the content of the data-src attribute and write it back into the src attribute.

jQuery('iframe[data-sid="youtube"]').each(function() {
    jQuery(this).attr('src', jQuery(this).data('src'));
});

However, there is another problem with the iframe. An empty iframe leaves a big gap in the layout. It would be nice if this gap were used to display a text in it that the iframe is deactivated because the user has not yet given his consent. It would be even better if there was a button to get approval with one click and thus activate the iframe immediately. For this function, the COOKiES module comes with a jQuery extension cookiesOverlay(cookies_service.id) that makes it real.

A complete script to re-animate an iframe will look like this.

document.addEventListener('cookiesjsrUserConsent', function (event) {
    var service = (typeof event.detail.services === 'object') ? event.detail.services : {};
    if (typeof service['youtube'] !== 'undefined' && service['youtube']) {
        jQuery('iframe[data-sid="youtube"]').each(function() {
            jQuery(this).attr('src', jQuery(this).data('src'));
        });
    } else {
        $('iframe[data-sid="youtube"]', context).cookiesOverlay('youtube');
    }
});

Some pitfalls and trip hazards:

  • Javascript aggregation: In the production environment javascript is usually compressed and aggregated. Our javascripts, which we want to deactivate, are no longer displayed in their own script tag in the source code. To prevent this, the attribute preprocess: false must be set for the corresponding file in the library.
  • Cache: The cookies module does not (yet) have its own cache context. In principle, it would also be possible to evaluate user decisions in the backend, and avoid to knock-out scripts or iframes, since the cookie (in which the user decisions are stored) can also be accessed from backend. However, it is important to pay attention to the cache, which is usually deactivated in the dev environment. On the production environment suddenly nothing works because the cache has been activated.
  • Module weight: It is always a good idea to use the same hooks the third-party-integration module uses to implement their libraries and code. But you have to keep in mind, that your hooks are executed after the hook of the third-party-integration module. Otherwise you don't find what you are looking for. You can achieve this by the module weight to be set in the install file of your module. The weight must be heigher than that of the third-party-integration module.
// cookie_demo.install
/**
 * Implements hook_install().
 */
function cookies_demo_install() {
  module_set_weight('cookies_demo', 11);
}

cookies_demo's People

Contributors

jfeltkamp avatar jpustkuchen avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Forkers

jpustkuchen

cookies_demo's Issues

Add / improve comments in cookies_demo.js attach()

https://github.com/jfeltkamp/cookies_demo/blob/master/js/cookies_demo.js

    attach: function (context) {
      var self = this;
      document.addEventListener('cookiesjsrUserConsent', function (event) {
        var service = (typeof event.detail.services === 'object') ? event.detail.services : {};
        if (typeof service[self.id] !== 'undefined' && service[self.id]) {
          self.activate(context);
        } else {
          self.fallback(context);
        }
      });
    }

could need some more comments as explanations what happens when. As this is a starter template, we shouldn't assume the developers knows what happens.

I'd suggest something like this (please see if it's correct or can be improved):

    attach: function (context) {
      var self = this;
      document.addEventListener('cookiesjsrUserConsent', function (event) {
        // cookiesjsrUserConsent event was fired, user made a cookie consent decision.
        var service = (typeof event.detail.services === 'object') ? event.detail.services : {};
        if (typeof service[self.id] !== 'undefined' && service[self.id]) {
          // Consent was given for this service. Heal Knockout:
          self.activate(context);
        } else {
          // Consent was not given or even revoked for this service. Handle Knockout:
          self.fallback(context);
        }
      });
    }

BTW Should there be information on cookiesjsrUserConsent if the consent was revoked not given? Perhaps that might be a separate topic / issue... unsure if it would help to clean up if consent was revoked and for example delete existing cookies etc.

Use text/plain instead of application/jsonto ignore src and its loading?

In
cookies_demo.module

you're using this code:

// The Knock-out. A script tag of type 'application/json' will not be
// executed.
'type' => 'application/json',

to disable execution of an external JavaScript.

I guess it could make sense to use text/plain instead for examples (and perhaps in implementations) like Cookiebot suggests: https://www.cookiebot.com/en/manual-implementation/

As I read here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type for the "type" attribute:

Any other value: The embedded content is treated as a data block which won't be processed by the browser. Developers must use a valid MIME type that is not a JavaScript MIME type to denote data blocks. The src attribute will be ignored.

This would be nice, if all browsers indeed behave that way and perhaps don't even load the external source.
GDPR hardliners suggest (and I think they're not totally wrong) that you shouldn't even call the remote URL as the remove server could at least track your IP without given consent.

Changing src to data-src doesn't seem possible in Drupals libraries API as it assumes the src to exist.

Before we make that change, we should ensure

  1. All / major browsers ignore src if text/plain is used
  2. After type is swtched back to application/javascript the code is executed
  3. There are no other unexpected side-effects

But if that's the case, it would be a useful change!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.