___TERMS_OF_SERVICE___

By creating or modifying this file you agree to Google Tag Manager's Community
Template Gallery Developer Terms of Service available at
https://developers.google.com/tag-manager/gallery-tos (or such other URL as
Google may provide), as modified from time to time.


___INFO___

{
  "type": "TAG",
  "id": "cvt_temp_public_id",
  "version": 1,
  "securityGroups": [],
  "displayName": "Pressidium Cookie Consent",
  "categories": ["TAG_MANAGEMENT", "PERSONALIZATION"],
  "brand": {
    "id": "brand_dummy",
    "displayName": "",
    "thumbnail": "data:image/png;base64, \u003d"
  },
  "description": "Lightweight, user-friendly and customizable cookie consent template to help you comply with the EU GDPR cookie law and CCPA regulations.",
  "containerContexts": [
    "WEB"
  ]
}


___TEMPLATE_PARAMETERS___

[
  {
    "type": "PARAM_TABLE",
    "name": "default_consent_states",
    "displayName": "Default consent states",
    "paramTableColumns": [
      {
        "param": {
          "type": "TEXT",
          "name": "region",
          "displayName": "Regions",
          "simpleValueType": true,
          "help": "Comma-separated ISO 3166-2 regions (leave blank to have selections apply to all regions)"
        },
        "isUnique": true
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "ad_storage",
          "checkboxText": "Ad storage",
          "simpleValueType": true,
          "help": "Enables storage, such as cookies, related to advertising"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "ad_user_data",
          "checkboxText": "Ad user data",
          "simpleValueType": true,
          "help": "Sets consent for sending user data to Google for online advertising purposes"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "ad_personalization",
          "checkboxText": "Ad personalization",
          "simpleValueType": true,
          "help": "Sets consent for personalized advertising"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "analytics_storage",
          "checkboxText": "Analytics storage",
          "simpleValueType": true,
          "help": "Enables storage, such as cookies, related to analytics (for example, visit duration)"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "functionality_storage",
          "checkboxText": "Functionality storage",
          "simpleValueType": true,
          "help": "Enables storage that supports the functionality of the website such as language settings"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "personalization_storage",
          "checkboxText": "Personalization storage",
          "simpleValueType": true,
          "help": "Enables storage related to personalization such as video recommendations"
        },
        "isUnique": false
      },
      {
        "param": {
          "type": "CHECKBOX",
          "name": "security_storage",
          "checkboxText": "Security storage",
          "simpleValueType": true,
          "help": "Enables storage related to security such as authentication functionality, fraud prevention, and other user protection"
        },
        "isUnique": false
      }
    ]
  },
  {
    "type": "CHECKBOX",
    "name": "ads_data_redaction",
    "checkboxText": "Ads data redaction",
    "simpleValueType": true,
    "help": "When the \u0027Ad storage\u0027 consent type is denied, ad click identifiers sent in network requests by Google Ads and Floodlight tags will be redacted. Network requests will also be sent through a cookieless domain"
  },
  {
    "type": "CHECKBOX",
    "name": "url_passthrough",
    "checkboxText": "URL passthrough",
    "simpleValueType": true,
    "help": "Will pass through ad click, client ID, and session ID information in URLs"
  }
]


___SANDBOXED_JS_FOR_WEB_TEMPLATE___

/*
 * Pressidium Cookie Consent
 * (Google Tag Manager consent template)
 *
 * Copyright (c) 2024 Pressidium
 */

const setDefaultConsentState = require('setDefaultConsentState');
const updateConsentState = require('updateConsentState');
const getCookieValues = require('getCookieValues');
const callInWindow = require('callInWindow');
const gtagSet = require('gtagSet');
const JSON = require('JSON');

const COOKIE_NAME = 'pressidium_cookie_consent';

const CONSENT_TYPES = [
  'ad_storage',
  'ad_user_data',
  'ad_personalization',
  'analytics_storage',
  'functionality_storage',
  'personalization_storage',
  'security_storage',
];

const ALLOWED_CATEGORIES = [
  'necessary',
  'analytics',
  'targeting',
  'preferences',
];

/**
 * Split the input string using comma as a delimiter, returning an array of strings.
 *
 * @param {string} input
 *
 * @return {string[]}
 */
const splitInput = (input) => {
  return input
    .split(',')
    .map((entry) => entry.trim())
    .filter((entry) => entry.length !== 0);
};

/**
 * Process a row of input from the default settings table, returning an object
 * which can be passed as an argument to `setDefaultConsentState`.
 *
 * @param {object} settings
 *
 * @return {void}
 */
const parseCommandData = (settings) => {
  const regions = splitInput(settings.region);

  const commandData = {
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    analytics_storage: 'denied',
    functionality_storage: 'denied',
    personalization_storage: 'denied',
    security_storage: 'denied',
  };

  CONSENT_TYPES.forEach((consentType) => {
    commandData[consentType] = settings[consentType] ? 'granted' : 'denied';
  });

  if (regions.length > 0) {
    commandData.region = regions;
  }

  return commandData;
};

/**
 * Called when consent changes. Assumes that consent object contains keys which
 * directly correspond to Google consent types.
 *
 * @param {object} consentModeStates
 *
 * @return {void}
 */
const onUserConsent = (consentModeStates) => {
  updateConsentState(consentModeStates);
};

/**
 * Return the GCM consent states based on the given accepted cookie categories.
 *
 * @param {string[]} categories Accepted cookie categories.
 *
 * @return {{
 *   ad_storage: string,
 *   ad_user_data: string,
 *   security_storage: string,
 *   functionality_storage: string,
 *   personalization_storage: string,
 *   ad_personalization: string,
 *   analytics_storage: string
 * }}
 */
const getConsentStatesByCategories = (categories) => {
  const consentModeStates = {
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    analytics_storage: 'denied',
    functionality_storage: 'denied',
    personalization_storage: 'denied',
    security_storage: 'denied',
  };

  const mapCategoryToType = {
    necessary: ['functionality_storage', 'security_storage'],
    preferences: ['personalization_storage'],
    analytics: ['analytics_storage'],
    targeting: ['ad_storage', 'ad_user_data', 'ad_personalization'],
  };

  categories.forEach((category) => {
    if (!mapCategoryToType.hasOwnProperty(category)) {
      return;
    }

    mapCategoryToType[category].forEach((consentType) => {
      consentModeStates[consentType] = 'granted';
    });
  });

  return consentModeStates;
};

/**
 * Return the GCM consent states based on the given cookie name.
 *
 * @param {string} cookieName Name of the cookie.
 *
 * @return {{
 *   ad_storage: string,
 *   ad_user_data: string,
 *   security_storage: string,
 *   functionality_storage: string,
 *   personalization_storage: string,
 *   ad_personalization: string,
 *   analytics_storage: string
 * }|null}
 */
const getConsentStatesByCookie = (cookieName) => {
  const cookieValues = getCookieValues(COOKIE_NAME) || [];

  if (cookieValues.length === 0) {
    // No cookie set, bail early
    return null;
  }

  const cookie = JSON.parse(cookieValues[0]);

  if (typeof cookie !== 'object') {
    // Invalid cookie, fail, and bail early
    data.gtmOnFailure();
    return null;
  }

  if (!cookie.hasOwnProperty('categories')) {
    // No categories set, bail early
    return null;
  }

  // Validate the cookie categories
  const categories = cookie.categories
    .map((category) => category.toLowerCase())
    .filter((category) => ALLOWED_CATEGORIES.indexOf(category) !== -1);

  // Return an object with the consent states formatted for GCM
  return getConsentStatesByCategories(categories);
};

/**
 * Execute the default command, and set up the consent update callback.
 *
 * @param {object} data
 *
 * @return {void}
 */
const main = (data) => {
  // Optional settings using `gtagSet()`
  gtagSet('ads_data_redaction', data.ads_data_redaction);
  gtagSet('url_passthrough', data.url_passthrough);

  // Set default consent states
  if (data.hasOwnProperty('default_consent_states')) {
    data.default_consent_states.forEach((settings) => {
      const defaultData = parseCommandData(settings);
      // `wait_for_update` (ms) allows for time to receive visitor choices
      defaultData.wait_for_update = 500;
      setDefaultConsentState(defaultData);
    });
  }

  /*
   * Check if cookie is set and has values that correspond to
   * Google consent types. If it does, run `onUserConsent()`.
   */
  const consentModeStates = getConsentStatesByCookie(COOKIE_NAME);
  if (consentModeStates !== null) {
    onUserConsent(consentModeStates);
  }

  /*
   * Add event listener to trigger update when consent changes.
   *
   * References an external method on the window object which accepts a
   * function as an argument. If you do not have such a method, you will need
   * to create one before continuing. This method should add the function
   * that is passed as an argument as a callback for an event emitted when
   * the user updates their consent. The callback should be called with an
   * object containing fields that correspond to the five built-in Google
   * consent types.
   */
  callInWindow('addPressidiumGCMConsentListener', onUserConsent);
};

main(data);
data.gtmOnSuccess();


___WEB_PERMISSIONS___

[
  {
    "instance": {
      "key": {
        "publicId": "access_globals",
        "versionId": "1"
      },
      "param": [
        {
          "key": "keys",
          "value": {
            "type": 2,
            "listItem": [
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "key"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  },
                  {
                    "type": 1,
                    "string": "execute"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "addPressidiumGCMConsentListener"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              }
            ]
          }
        }
      ]
    },
    "clientAnnotations": {
      "isEditedByUser": true
    },
    "isRequired": true
  },
  {
    "instance": {
      "key": {
        "publicId": "write_data_layer",
        "versionId": "1"
      },
      "param": [
        {
          "key": "keyPatterns",
          "value": {
            "type": 2,
            "listItem": [
              {
                "type": 1,
                "string": "ads_data_redaction"
              },
              {
                "type": 1,
                "string": "url_passthrough"
              }
            ]
          }
        }
      ]
    },
    "clientAnnotations": {
      "isEditedByUser": true
    },
    "isRequired": true
  },
  {
    "instance": {
      "key": {
        "publicId": "get_cookies",
        "versionId": "1"
      },
      "param": [
        {
          "key": "cookieAccess",
          "value": {
            "type": 1,
            "string": "specific"
          }
        },
        {
          "key": "cookieNames",
          "value": {
            "type": 2,
            "listItem": [
              {
                "type": 1,
                "string": "pressidium_cookie_consent"
              }
            ]
          }
        }
      ]
    },
    "clientAnnotations": {
      "isEditedByUser": true
    },
    "isRequired": true
  },
  {
    "instance": {
      "key": {
        "publicId": "access_consent",
        "versionId": "1"
      },
      "param": [
        {
          "key": "consentTypes",
          "value": {
            "type": 2,
            "listItem": [
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "ad_storage"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "ad_user_data"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "ad_personalization"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "analytics_storage"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "functionality_storage"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "personalization_storage"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              },
              {
                "type": 3,
                "mapKey": [
                  {
                    "type": 1,
                    "string": "consentType"
                  },
                  {
                    "type": 1,
                    "string": "read"
                  },
                  {
                    "type": 1,
                    "string": "write"
                  }
                ],
                "mapValue": [
                  {
                    "type": 1,
                    "string": "security_storage"
                  },
                  {
                    "type": 8,
                    "boolean": false
                  },
                  {
                    "type": 8,
                    "boolean": true
                  }
                ]
              }
            ]
          }
        }
      ]
    },
    "clientAnnotations": {
      "isEditedByUser": true
    },
    "isRequired": true
  }
]


___TESTS___

scenarios: []


___NOTES___

== Changelog ==

= GTM Template 1.0.0: Mar 21, 2024 =

* Initial version


