By james, 16 December, 2023
Door 16 containing cookie consent

It’s day 16, and time to open another door of the Drupal Advent Calendar. Today we are joined by João Limas (jolimas) to talk about cookies.

What are cookies, and why do we need them?

Cookies are small pieces of data stored on a user’s device by a web browser while the user is browsing a website. The web server sends these pieces of data to the browser and then sends them back unchanged by the browser each time it accesses that server.

Diagram showing the way the browser sends cookies between the server and the client

Cookies serve various purposes and play a crucial role in enhancing the user experience on the web. They are used to remember your preferences, such as your language settings, search history, and shopping cart contents. Cookies can also track your browsing activity across different websites.

There are two main types of cookies:

  • First-party cookies are typically used to remember your preferences and improve your browsing experience. The website you’re visiting sets them.
  • Third-party cookies are often used for advertising and analytics. A different website from the one you’re visiting sets them.

Cookies in Drupal?

Drupal uses cookies for various purposes. They affect multiple aspects of website functionality, user experience, and data management. Some key points are:

  • User authentication: Drupal uses cookies to authenticate and keep users logged in.
  • Session management: Drupal uses cookies to manage user sessions. It allows Drupal to keep track of what pages a user has visited and what information they have entered into forms.
  • Personalization: Drupal can use cookies to personalize the user experience. For example, Drupal can use cookies to remember a user’s preferred language, theme, or content layout.
  • Tracking and Analytics: Drupal can track website traffic and user behavior through cookies. Drupal also enables the integration of analytics tools that use cookies (third-party cookies) to gather data about how users interact with the website. This information allows us to improve the website’s design and functionality.

The GDPR and Cookie Consent

The General Data Protection Regulation (GDPR) is a regulation in European Union (EU) law on data protection and privacy in the EU and the European Economic Area (EEA). It also addresses the transfer of personal data outside the EU and EEA areas. It was adopted on 14 April 2016 and became enforceable after a two-year transition period beginning on 25 May 2018.

It has a significant impact on how organizations handle personal data, including the use of cookies. Cookies can collect and process personal data, and as such, they fall under the purview of the GDPR. It means that websites must obtain consent from users before storing or accessing cookies on their devices. This consent must be:

  • Freely given: Users must be able to choose whether or not to consent to cookies.
  • Specific: Specific information about the cookies used and their purpose must be available to the users.
  • Informed: Clear and concise information about the cookies must be open to the users so that it’s easy for them to understand what’s at stake. 
  • Unambiguous: Users must explicitly consent to cookies by taking an affirmative action, such as clicking a button.
  • Revocable: Users must be able to withdraw their consent at any time.

It’s essential for Drupal website owners and developers to be aware of privacy regulations and to configure their Drupal installations accordingly. It includes implementing proper cookie consent mechanisms, providing clear information about the use of cookies, and ensuring compliance with data protection laws.

Drupal provides tools for handling cookie consent in compliance with privacy regulations like GDPR. Websites can use Drupal modules to display cookie consent banners and allow users to manage their cookie preferences.

But they only sometimes fit our needs for some specific use cases. Let me show you one specific use case and how we addressed it in our company.

Use Case for iframe loading without consent mode

We once had a client whose editors were embedding third-party content, such as Google Maps, YouTube videos, and Vimeo videos, directly into the body content using iframes.

These iframes loaded without user consent, as consent management solutions couldn’t control data originating from the embedded content.

So, using the IframeManagerCookieConsent, and some custom Drupal code, we solved the problem.

IframeManager is a lightweight JavaScript plugin that helps you comply with GDPR by initially removing iframes and setting a notice relative to that service. Iframes are loaded only after consent.

CookieConsent is a lightweight and GDPR-compliant cookie consent plugin written in plain JavaScript and used in other web apps of the client. Check the demo.

But we needed to guarantee that the iframes didn’t load before the user consented to them. So, we needed to block them on the server side.

We preprocessed the body field and replaced all the iframe HTML tags with the IframeManager configuration options. 

Let me show you an example of how it works for YouTube videos:

/**
 * Implements hook_preprocess_HOOK().
 */
function your_theme_preprocess_field(&$variables) {
  $element = $variables['element'];
  // At field body, replace all iframe tags with iframe manager API div.
  if ($element['#field_name'] == 'body') {
    // Check if the field has content.
    if(!empty($variables["items"][0]["content"]["#text"])){
      // Create a new DOMDocument.
      $dom = new DOMDocument();
        $dom->loadHTML(mb_convert_encoding($variables["items"][0]["content"]["#text"], 'HTML-ENTITIES', 'UTF-8'));
      // Find all iframe elements.
      $iframes = $dom->getElementsByTagName('iframe');
      // Iterate through each iframe and replace with div and iframe manager configuration options.
      foreach ($iframes as $iframe) {
        $parse = parse_url($iframe->getAttribute('src'));
        // Check if the iframe is from youtube.
        // And replace it with configuration options for youtube.
        switch ($parse['host']){
          case "www.youtube.com":
            // Get the youtube video id.
        preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $iframe->getAttribute('src'), $match);
            $youtube_id = $match[1];
            // Create a new div element with iframe manager configuration options.
            $replacementDiv = $dom->createElement('div', '');
            $replacementDiv->setAttribute('class', 'replacement-div');
            $replacementDiv->setAttribute('data-service', 'youtube');
            $replacementDiv->setAttribute('data-id', $youtube_id);
            $replacementDiv->setAttribute('data-autoscale', '');
            // Replace the iframe with the new div element.
            $iframe->parentNode->replaceChild($replacementDiv, $iframe);
            break;
        }
      }
      // Get the modified HTML content
      $modifiedContent = $dom->saveHTML();
      // Set the modified HTML content to the field.
      $variables["items"][0]["content"]["#text"] = $modifiedContent;
    }
  }
}

Running the IframeManager on the client side, we can now control the preprocessed body fields with the div element and their configuration options.

At your IframeManager JavaScript, you can create your new service:

    const im = iframemanager();
   
    // Example with youtube embed
    im.run({
      currLang: "en",
      services: {
        youtube: {
          embedUrl: "https://www.youtube-nocookie.com/embed/{data-id}",
          thumbnailUrl: "https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg",
          iframe: {
            allow:
              "accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;",
          },
          languages: {
            en: {
              notice: `This third-party content was disabled because you didn't consent to it.<br>You can <a href="#editCookieSettings">review your consent for "Third-party content"</a> or, alternatively, click the link <a href="https://www.youtube-nocookie.com/embed/{data-id}" target="_blank" title="Youtube vídeo">https://www.youtube-nocookie.com/embed/{data-id}</a> to view the video directly on their platform.`,
              loadBtn: "Play vídeo",
            },
          },
        },
      },
    });

When the user accepts/rejects the cookie consent, you can call the IframeManager method .acceptService(<service_name>) or .rejectService(<service_name>), where the service_name can be the service added at .run method (example before) or ‘all’.

The final result would be:

A video frame with a cookie consent notice below it

With this solution, we were able to ensure that the iframes embedded by the editors did not load any third-party content without the user’s consent and that the user had complete control over their cookie preferences. This way, we improved the privacy and compliance of our Drupal website while still providing a rich and engaging user experience.

Photo of João LimasJoão Limas is the co-founder of Pictonio, a company based in Aveiro, Portugal, highly focused on Drupal services. With a Computer Science degree from Aveiro University, his passion for technology has been a driving force throughout his career. He’s been working with Drupal since version 6. Besides co-managing the company, he’s responsible for a team of Drupal developers, onboarding new members to the Drupal ecosystem, architecting multiple projects, and hands-on implementation of new features. Beyond the realm of code, he is a proud father to two wonderful children. In the moments between lines of code and managing teams, he finds an escape in surfing, hiking, and exploring new destinations.

Comments

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.