Piwik PRO - Custom consent form example

Implementation guide

This code is written having further modifications in mind. Feel free to change the styles, structure and texts. If you are an advanced user, you can also modify the JavaScript part.

To implement this solution in your Piwik PRO setup, follow those steps:

  1. In Tag Manager:
    1. Add one of the following tags to your website
    2. Add a trigger to this tag - “All page views”
    3. In “Advanced tag settings”, set “Does not require consent” consent type
  2. Enable Custom consent form mode (Developers portal)
  3. Publish your website using “Publish” button in the upper right corner of the Tag Manager app

Tag codes

Bottom bar
<style type="text/css">
.PiwikPROConsentForm-hidden {
    display: none;
}

.PiwikPROConsentForm-container {

}

.PiwikPROConsentForm-content {
    display: flex;
    flex-direction: column;
    border: 1px dashed #cccccc;
    position: fixed;
    left: 0;
    bottom: 0;
    padding: 10px;
    margin: 0;
    width: 100%;
    font-family: sans-serif;
    box-sizing: border-box;
    background-color: #FFFFFF;
    box-shadow: 0 0 10px;
    max-height: 80vh;
    overflow: auto;
}

.PiwikPROConsentForm-row-top {
    display: flex;
    margin-bottom: 10px;
}

.PiwikPROConsentForm-row-middle {
    flex-grow: 1;
    overflow: auto;
}

.PiwikPROConsentForm-row-bottom {

}

.PiwikPROConsentForm-header {
    font-weight: bold;
    font-size: 22px;
    flex-grow: 1;
}

.PiwikPROConsentForm-close-button {
    height: 100%;
    border: none;
    background-color: transparent;
    padding: 0;
    cursor: pointer;
    font-size: 0;
}

.PiwikPROConsentForm-close-button svg {
    width: 20px;
    height: 20px;
}

.PiwikPROConsentForm-intro {
    text-align: justify;
}

.PiwikPROConsentForm-consent-types {
    display: block;
}

.PiwikPROConsentForm-consent-types ul {
    margin: 10px 0 10px -10px;
    display: flex;
    flex-wrap: wrap;
}

.PiwikPROConsentForm-consent-types ul li {
    padding: 0;
    margin: 0 30px 0 0;
}

.PiwikPROConsentForm-toggle-details {
    color: #107EF1;
}

.PiwikPROConsentForm-consents {
    border: 1px solid #ccc;
    margin: 10px 0;
}

.PiwikPROConsentForm-consent-item {
    padding: 5px;
    display: flex;
    align-items: flex-start;
}

.PiwikPROConsentForm-consent-item-type {
    font-weight: bold;
}

.PiwikPROConsentForm-consent-item-description {

}

.PiwikPROConsentForm-consent-checkbox {
    margin: 5px;
}

.PiwikPROConsentForm-buttons {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -8px;
}

.PiwikPROConsentForm-buttons button {
    margin: 8px;
    cursor: pointer;
}

.PiwikPROConsentForm-save-button {
    color: #131313;
    border-radius: 2px;
    border: 1px solid #cccccc;
    background-color: #eeeeee;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}

.PiwikPROConsentForm-agree-to-all-button {
    color: #ffffff;
    border-radius: 2px;
    border: 1px solid #0B4BB3;
    background-color: #2775F2;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}

.PiwikPROConsentForm-reject-all-button {
    color: #ffffff;
    border-radius: 2px;
    border: 1px solid #0B4BB3;
    background-color: #2775F2;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}
</style>

<div id="PiwikPROConsentForm-container" class="PiwikPROConsentForm-container PiwikPROConsentForm-hidden">
    <div id="PiwikPROConsentForm-container-inner" class="PiwikPROConsentForm-container-inner">
        <div id="PiwikPROConsentForm-content" class="PiwikPROConsentForm-content">
            <div class="PiwikPROConsentForm-row-top">
                <div class="PiwikPROConsentForm-header">Privacy settings</div>
                <button id="PiwikPROConsentForm-close-consent-form" class="PiwikPROConsentForm-close-button">
                    <svg viewbox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
                        <g>
                            <path d="M2.188 4.406l2.244-2.25 9.725 9.75-2.244 2.25-9.726-9.75"></path>
                            <path d="M11.913 2.156l2.244 2.25-9.725 9.75-2.245-2.25 9.726-9.75"></path>
                        </g>
                    </svg>
                </button>
            </div>
            <div class="PiwikPROConsentForm-row-middle">
                <p class="PiwikPROConsentForm-intro">
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus leo elit, pulvinar non sagittis ac,
                    iaculis id sapien. Phasellus eros ipsum, molestie in malesuada nec, eleifend non tellus. Aliquam
                    aliquam
                    placerat turpis, et commodo lorem tempus sit amet. Curabitur venenatis ex quis dolor pretium, nec
                    pharetra tellus cursus. More in <a href="#">privacy policy</a>.
                </p>
                <a href="#" id="PiwikPROConsentForm-toggle-details" class="PiwikPROConsentForm-toggle-details">
                    Toggle details
                </a>
                <div id="PiwikPROConsentForm-consent-types" class="PiwikPROConsentForm-consent-types"></div>
                <div id="PiwikPROConsentForm-consents"
                     class="PiwikPROConsentForm-consents PiwikPROConsentForm-hidden"></div>
            </div>
            <div class="PiwikPROConsentForm-row-bottom">
                <div class="PiwikPROConsentForm-buttons">
                    <button id="PiwikPROConsentForm-save" class="PiwikPROConsentForm-save-button">
                        Save
                    </button>
                    <button id="PiwikPROConsentForm-agree-to-all" class="PiwikPROConsentForm-agree-to-all-button">
                        Agree to all
                    </button>
                    <button id="PiwikPROConsentForm-reject-all" class="PiwikPROConsentForm-reject-all-button">
                        Reject all
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript">
(function () {
  'use strict';

  var OPEN_CONSENT_FORM_ID = 'PiwikPROConsentForm-open-consent-form';
  var CLOSE_CONSENT_FORM_ID = 'PiwikPROConsentForm-close-consent-form';
  var CONTAINER_ID = 'PiwikPROConsentForm-container';
  var SAVE_ID = 'PiwikPROConsentForm-save';
  var AGREE_TO_ALL_ID = 'PiwikPROConsentForm-agree-to-all';
  var REJECT_ALL_ID = 'PiwikPROConsentForm-reject-all';
  var TOGGLE_DETAILS_ID = 'PiwikPROConsentForm-toggle-details';
  var DETAILS_ID = 'PiwikPROConsentForm-consents';
  var CONSENT_TYPES_ID = 'PiwikPROConsentForm-consent-types';
  var HIDDEN_CLASS = 'PiwikPROConsentForm-hidden';
  var ID_PREFIX = 'PiwikPROConsentForm-';
  var CONSENT_ITEM_CLASS = 'PiwikPROConsentForm-consent-item';
  var CONSENT_ITEM_TYPE_CLASS = 'PiwikPROConsentForm-consent-item-type';
  var CONSENT_ITEM_DESCRIPTION_CLASS = 'PiwikPROConsentForm-consent-item-description';
  var CHECKBOX_CLASS = 'PiwikPROConsentForm-consent-checkbox';

  var OPEN_REASON_MANUAL = 'manual';
  var OPEN_REASON_AUTO = 'auto';

  /** Descriptions for consent types. */
  var consentTypesDescriptions = {
    analytics: {
      name: 'Analytics',
      description: 'We will store data in an aggregated form about visitors and their experiences on our website. We use this data to fix bugs and improve the experience for all visitors.'
    },
    ab_testing_and_personalization: {
      name: 'AB Testing',
      description: 'We will create a cookie in your browser to ensure consistency of our A/B tests. A/B tests are small changes displayed to different groups of visitors. We use the data to create a better experience for all visitors. We will also use this cookie to personalize content for you.'
    },
    conversion_tracking: {
      name: 'Conversion Tracking',
      description: 'We will store data about when you complete certain actions on our website to understand better how you use it. We use this data to improve your experience with our site.'
    },
    marketing_automation: {
      name: 'Marketing Automation',
      description: 'We will store data to create marketing campaigns for certain groups of visitors.'
    },
    remarketing: {
      name: 'Remarketing',
      description: 'We will store data to show you our advertisements (only ours) on other websites relevant to your interests.'
    },
    user_feedback: {
      name: 'User Feedback',
      description: 'We will store data in an aggregated form to analyze the performance of our website\'s user interface. We use this data to improve the site for all visitors.'
    },
    custom_consent: {
      name: 'Custom consent',
      description: 'Adjust this copy to your needs.'
    }
  };

  /** HTML of the consent form, used to recreate the form after it's closed */
  var initialConsentFormHTML;

  function makeConsentFormVisible() {
    document.getElementById(CONTAINER_ID).classList.remove(HIDDEN_CLASS);
  }

  function makeConsentFormHidden() {
    document.getElementById(CONTAINER_ID).classList.add(HIDDEN_CLASS);
  }

  function toggleDetails() {
    document.getElementById(DETAILS_ID).classList.toggle(HIDDEN_CLASS);
  }

  /** For given type, it adds checkbox, consent type and its description to the form */
  function addConsentItemElement(consentType, consentDetails) {
    var consentItemElement;
    var checkboxId = ID_PREFIX + 'consent-type-' + consentType;
    var input, label;

    input = document.createElement('input');
    input.type = 'checkbox';
    input.value = consentType;
    input.checked = consentDetails.status === 1;
    input.id = checkboxId;
    input.classList.add(CHECKBOX_CLASS);

    label = document.createElement('label');
    label.setAttribute('for', checkboxId);
    label.innerHTML = '' +
      '<div class="' + CONSENT_ITEM_TYPE_CLASS + '">' +
      consentTypesDescriptions[consentType].name +
      '</div>' +
      '<div class="' + CONSENT_ITEM_DESCRIPTION_CLASS + '">' +
      consentTypesDescriptions[consentType].description +
      '</div>';

    consentItemElement = document.createElement('div');
    consentItemElement.classList.add(CONSENT_ITEM_CLASS);
    consentItemElement.appendChild(input);
    consentItemElement.appendChild(label);

    document.getElementById(DETAILS_ID).appendChild(consentItemElement);
  }

  /** Adds consent items (checkboxes) to the form */
  function showDetailedConsentTypesList(consentTypes, consentDetails) {
    consentTypes.forEach(function (consentType) {
      addConsentItemElement(consentType, consentDetails[consentType]);
    });
  }

  /** Adds consent types list to the form */
  function showSimpleConsentTypesList(consentTypes) {
    var ul = document.createElement('ul');

    consentTypes.forEach(function (consentType) {
      var li = document.createElement('li');

      li.innerText = consentTypesDescriptions[consentType].name;
      ul.appendChild(li);
    });
    document.getElementById(CONSENT_TYPES_ID).appendChild(ul);
  }

  function closeConsentForm() {
    makeConsentFormHidden();
  }

  /** Saves consent decisions based on the checkboxes statuses and closes the form on success */
  function saveConsents() {
    var consents = {};
    var consentCheckboxes = document.getElementsByClassName(CHECKBOX_CLASS);
    var i, currentCheckbox, currentConsentType, currentChecked;

    for (i = 0; i < consentCheckboxes.length; i++) {
      currentCheckbox = consentCheckboxes[i];
      currentConsentType = currentCheckbox.value;
      currentChecked = currentCheckbox.checked ? 1 : 0;
      consents[currentConsentType] = { status: currentChecked };
    }

    ppms.cm.api('setComplianceSettings', { consents: consents }, closeConsentForm, console.error);
  }

  /** Sets status from the parameter for all checkboxes in the form */
  function setStatusForAll(status) {
    var consentCheckboxes = document.getElementsByClassName(CHECKBOX_CLASS);
    var i;

    for (i = 0; i < consentCheckboxes.length; i++) {
      consentCheckboxes[i].checked = status;
    }
  }

  function agreeToAll() {
    setStatusForAll(1);
    saveConsents();
  }

  function rejectAll() {
    setStatusForAll(0);
    saveConsents();
  }

  function storeInitialHMTL() {
    initialConsentFormHTML = document.getElementById(CONTAINER_ID).innerHTML;
  }

  function restoreInitialHMTL() {
    document.getElementById(CONTAINER_ID).innerHTML = initialConsentFormHTML;
  }

  /** Makes consent form buttons interactive */
  function addEventListenersToConsentFormButtons() {
    document.getElementById(SAVE_ID).addEventListener('click', function() {
      saveConsents();
      ppms.cm.api('trackSaveChoicesClick');
    });
    document.getElementById(AGREE_TO_ALL_ID).addEventListener('click', function() {
      agreeToAll();
      ppms.cm.api('trackAgreeToAllClick');
    });
    document.getElementById(REJECT_ALL_ID).addEventListener('click', function() {
      rejectAll();
      ppms.cm.api('trackRejectAllClick');
    });
    document.getElementById(TOGGLE_DETAILS_ID).addEventListener('click', function() {
      toggleDetails();
    });
    document.getElementById(CLOSE_CONSENT_FORM_ID).addEventListener('click', function() {
      closeConsentForm();
      ppms.cm.api('trackCloseButtonClick');
    });
  }

  /** Opens consent form, and shows consent types from the consentTypesToShow parameter. If consentTypesToShow is
   * undefined, then all available consents are displayed. */
  function openConsentForm(consentTypesToShow, reason) {
    ppms.cm.api('getComplianceSettings', function (settings) {
      var consentDetails = settings.consents;
      var allTypes = Object.keys(consentDetails);

      restoreInitialHMTL();
      addEventListenersToConsentFormButtons();
      showDetailedConsentTypesList(consentTypesToShow || allTypes, consentDetails);
      showSimpleConsentTypesList(consentTypesToShow || allTypes);
      makeConsentFormVisible();

      switch (reason) {
        case OPEN_REASON_MANUAL: ppms.cm.api('trackPrivacyPolicyLinkView'); break;
        default: ppms.cm.api('trackMainFormView');
      }
    }, console.error);
  }

  /** Makes "open consent form" button interactive if it's present on the website */
  function addOpenConsentFormButtonListener() {
    var openConsentFormElement = document.getElementById(OPEN_CONSENT_FORM_ID);

    if (openConsentFormElement) {
      openConsentFormElement.addEventListener('click', function () {
        openConsentForm(undefined, OPEN_REASON_MANUAL);
      });
    }
  }

  /** If there are any new (not decided) consent types, then cookie is saved and openConsentForm function is called. */
  function setNewConsentTypes (newTypes) {
    if (newTypes.length > 0) {
      ppms.cm.api('setInitialComplianceSettings', newTypes, function () { openConsentForm(newTypes, OPEN_REASON_AUTO); }, console.error);
    }
  }

  /** Checks for the consent types user never decided to agree/disagree. Passes those types to setNewConsentTypes function. */
  function setConsents() {
    ppms.cm.api('getNewComplianceTypes', function (newTypes) { setNewConsentTypes(newTypes); }, console.error);
  }

  function init() {
    storeInitialHMTL();
    setConsents();
    addOpenConsentFormButtonListener();
  }

  init();
})();
</script>
<style type="text/css">
.PiwikPROConsentForm-hidden {
    display: none;
}

.PiwikPROConsentForm-container {

}

.PiwikPROConsentForm-content {
    display: flex;
    flex-direction: column;
    border: 1px dashed #cccccc;
    position: fixed;
    left: 0;
    bottom: 0;
    padding: 10px;
    margin: 0;
    width: 100%;
    font-family: sans-serif;
    box-sizing: border-box;
    background-color: #FFFFFF;
    box-shadow: 0 0 10px;
    max-height: 80vh;
    overflow: auto;
}

.PiwikPROConsentForm-row-top {
    display: flex;
    margin-bottom: 10px;
}

.PiwikPROConsentForm-row-middle {
    flex-grow: 1;
    overflow: auto;
}

.PiwikPROConsentForm-row-bottom {

}

.PiwikPROConsentForm-header {
    font-weight: bold;
    font-size: 22px;
    flex-grow: 1;
}

.PiwikPROConsentForm-close-button {
    height: 100%;
    border: none;
    background-color: transparent;
    padding: 0;
    cursor: pointer;
    font-size: 0;
}

.PiwikPROConsentForm-close-button svg {
    width: 20px;
    height: 20px;
}

.PiwikPROConsentForm-intro {
    text-align: justify;
}

.PiwikPROConsentForm-consent-types {
    display: block;
}

.PiwikPROConsentForm-consent-types ul {
    margin: 10px 0 10px -10px;
    display: flex;
    flex-wrap: wrap;
}

.PiwikPROConsentForm-consent-types ul li {
    padding: 0;
    margin: 0 30px 0 0;
}

.PiwikPROConsentForm-toggle-details {
    color: #107EF1;
}

.PiwikPROConsentForm-consents {
    border: 1px solid #ccc;
    margin: 10px 0;
}

.PiwikPROConsentForm-consent-item {
    padding: 5px;
    display: flex;
    align-items: flex-start;
}

.PiwikPROConsentForm-consent-item-type {
    font-weight: bold;
}

.PiwikPROConsentForm-consent-item-description {

}

.PiwikPROConsentForm-consent-checkbox {
    margin: 5px;
}

.PiwikPROConsentForm-buttons {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -8px;
}

.PiwikPROConsentForm-buttons button {
    margin: 8px;
    cursor: pointer;
}

.PiwikPROConsentForm-save-button {
    color: #131313;
    border-radius: 2px;
    border: 1px solid #cccccc;
    background-color: #eeeeee;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}

.PiwikPROConsentForm-agree-to-all-button {
    color: #ffffff;
    border-radius: 2px;
    border: 1px solid #0B4BB3;
    background-color: #2775F2;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}

.PiwikPROConsentForm-reject-all-button {
    color: #ffffff;
    border-radius: 2px;
    border: 1px solid #0B4BB3;
    background-color: #2775F2;
    padding: 7px;
    font-size: 14px;
    line-height: 17px;
    min-width: 130px;
}
.PiwikPROConsentForm-container-inner {
    background-color: rgba(0,0,0,0.5);
    position: fixed;
    display: flex;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    justify-content: center;
    align-items: center;

}

.PiwikPROConsentForm-content {
    width: 50%;
    margin: auto;
    max-width: 500px;
    position: relative;
}
</style>

<div id="PiwikPROConsentForm-container" class="PiwikPROConsentForm-container PiwikPROConsentForm-hidden">
    <div id="PiwikPROConsentForm-container-inner" class="PiwikPROConsentForm-container-inner">
        <div id="PiwikPROConsentForm-content" class="PiwikPROConsentForm-content">
            <div class="PiwikPROConsentForm-row-top">
                <div class="PiwikPROConsentForm-header">Privacy settings</div>
                <button id="PiwikPROConsentForm-close-consent-form" class="PiwikPROConsentForm-close-button">
                    <svg viewbox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
                        <g>
                            <path d="M2.188 4.406l2.244-2.25 9.725 9.75-2.244 2.25-9.726-9.75"></path>
                            <path d="M11.913 2.156l2.244 2.25-9.725 9.75-2.245-2.25 9.726-9.75"></path>
                        </g>
                    </svg>
                </button>
            </div>
            <div class="PiwikPROConsentForm-row-middle">
                <p class="PiwikPROConsentForm-intro">
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus leo elit, pulvinar non sagittis ac,
                    iaculis id sapien. Phasellus eros ipsum, molestie in malesuada nec, eleifend non tellus. Aliquam
                    aliquam
                    placerat turpis, et commodo lorem tempus sit amet. Curabitur venenatis ex quis dolor pretium, nec
                    pharetra tellus cursus. More in <a href="#">privacy policy</a>.
                </p>
                <a href="#" id="PiwikPROConsentForm-toggle-details" class="PiwikPROConsentForm-toggle-details">
                    Toggle details
                </a>
                <div id="PiwikPROConsentForm-consent-types" class="PiwikPROConsentForm-consent-types"></div>
                <div id="PiwikPROConsentForm-consents"
                     class="PiwikPROConsentForm-consents PiwikPROConsentForm-hidden"></div>
            </div>
            <div class="PiwikPROConsentForm-row-bottom">
                <div class="PiwikPROConsentForm-buttons">
                    <button id="PiwikPROConsentForm-save" class="PiwikPROConsentForm-save-button">
                        Save
                    </button>
                    <button id="PiwikPROConsentForm-agree-to-all" class="PiwikPROConsentForm-agree-to-all-button">
                        Agree to all
                    </button>
                    <button id="PiwikPROConsentForm-reject-all" class="PiwikPROConsentForm-reject-all-button">
                        Reject all
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript">
(function () {
  'use strict';

  var OPEN_CONSENT_FORM_ID = 'PiwikPROConsentForm-open-consent-form';
  var CLOSE_CONSENT_FORM_ID = 'PiwikPROConsentForm-close-consent-form';
  var CONTAINER_ID = 'PiwikPROConsentForm-container';
  var SAVE_ID = 'PiwikPROConsentForm-save';
  var AGREE_TO_ALL_ID = 'PiwikPROConsentForm-agree-to-all';
  var REJECT_ALL_ID = 'PiwikPROConsentForm-reject-all';
  var TOGGLE_DETAILS_ID = 'PiwikPROConsentForm-toggle-details';
  var DETAILS_ID = 'PiwikPROConsentForm-consents';
  var CONSENT_TYPES_ID = 'PiwikPROConsentForm-consent-types';
  var HIDDEN_CLASS = 'PiwikPROConsentForm-hidden';
  var ID_PREFIX = 'PiwikPROConsentForm-';
  var CONSENT_ITEM_CLASS = 'PiwikPROConsentForm-consent-item';
  var CONSENT_ITEM_TYPE_CLASS = 'PiwikPROConsentForm-consent-item-type';
  var CONSENT_ITEM_DESCRIPTION_CLASS = 'PiwikPROConsentForm-consent-item-description';
  var CHECKBOX_CLASS = 'PiwikPROConsentForm-consent-checkbox';

  var OPEN_REASON_MANUAL = 'manual';
  var OPEN_REASON_AUTO = 'auto';

  /** Descriptions for consent types. */
  var consentTypesDescriptions = {
    analytics: {
      name: 'Analytics',
      description: 'We will store data in an aggregated form about visitors and their experiences on our website. We use this data to fix bugs and improve the experience for all visitors.'
    },
    ab_testing_and_personalization: {
      name: 'AB Testing',
      description: 'We will create a cookie in your browser to ensure consistency of our A/B tests. A/B tests are small changes displayed to different groups of visitors. We use the data to create a better experience for all visitors. We will also use this cookie to personalize content for you.'
    },
    conversion_tracking: {
      name: 'Conversion Tracking',
      description: 'We will store data about when you complete certain actions on our website to understand better how you use it. We use this data to improve your experience with our site.'
    },
    marketing_automation: {
      name: 'Marketing Automation',
      description: 'We will store data to create marketing campaigns for certain groups of visitors.'
    },
    remarketing: {
      name: 'Remarketing',
      description: 'We will store data to show you our advertisements (only ours) on other websites relevant to your interests.'
    },
    user_feedback: {
      name: 'User Feedback',
      description: 'We will store data in an aggregated form to analyze the performance of our website\'s user interface. We use this data to improve the site for all visitors.'
    },
    custom_consent: {
      name: 'Custom consent',
      description: 'Adjust this copy to your needs.'
    }
  };

  /** HTML of the consent form, used to recreate the form after it's closed */
  var initialConsentFormHTML;

  function makeConsentFormVisible() {
    document.getElementById(CONTAINER_ID).classList.remove(HIDDEN_CLASS);
  }

  function makeConsentFormHidden() {
    document.getElementById(CONTAINER_ID).classList.add(HIDDEN_CLASS);
  }

  function toggleDetails() {
    document.getElementById(DETAILS_ID).classList.toggle(HIDDEN_CLASS);
  }

  /** For given type, it adds checkbox, consent type and its description to the form */
  function addConsentItemElement(consentType, consentDetails) {
    var consentItemElement;
    var checkboxId = ID_PREFIX + 'consent-type-' + consentType;
    var input, label;

    input = document.createElement('input');
    input.type = 'checkbox';
    input.value = consentType;
    input.checked = consentDetails.status === 1;
    input.id = checkboxId;
    input.classList.add(CHECKBOX_CLASS);

    label = document.createElement('label');
    label.setAttribute('for', checkboxId);
    label.innerHTML = '' +
      '<div class="' + CONSENT_ITEM_TYPE_CLASS + '">' +
      consentTypesDescriptions[consentType].name +
      '</div>' +
      '<div class="' + CONSENT_ITEM_DESCRIPTION_CLASS + '">' +
      consentTypesDescriptions[consentType].description +
      '</div>';

    consentItemElement = document.createElement('div');
    consentItemElement.classList.add(CONSENT_ITEM_CLASS);
    consentItemElement.appendChild(input);
    consentItemElement.appendChild(label);

    document.getElementById(DETAILS_ID).appendChild(consentItemElement);
  }

  /** Adds consent items (checkboxes) to the form */
  function showDetailedConsentTypesList(consentTypes, consentDetails) {
    consentTypes.forEach(function (consentType) {
      addConsentItemElement(consentType, consentDetails[consentType]);
    });
  }

  /** Adds consent types list to the form */
  function showSimpleConsentTypesList(consentTypes) {
    var ul = document.createElement('ul');

    consentTypes.forEach(function (consentType) {
      var li = document.createElement('li');

      li.innerText = consentTypesDescriptions[consentType].name;
      ul.appendChild(li);
    });
    document.getElementById(CONSENT_TYPES_ID).appendChild(ul);
  }

  function closeConsentForm() {
    makeConsentFormHidden();
  }

  /** Saves consent decisions based on the checkboxes statuses and closes the form on success */
  function saveConsents() {
    var consents = {};
    var consentCheckboxes = document.getElementsByClassName(CHECKBOX_CLASS);
    var i, currentCheckbox, currentConsentType, currentChecked;

    for (i = 0; i < consentCheckboxes.length; i++) {
      currentCheckbox = consentCheckboxes[i];
      currentConsentType = currentCheckbox.value;
      currentChecked = currentCheckbox.checked ? 1 : 0;
      consents[currentConsentType] = { status: currentChecked };
    }

    ppms.cm.api('setComplianceSettings', { consents: consents }, closeConsentForm, console.error);
  }

  /** Sets status from the parameter for all checkboxes in the form */
  function setStatusForAll(status) {
    var consentCheckboxes = document.getElementsByClassName(CHECKBOX_CLASS);
    var i;

    for (i = 0; i < consentCheckboxes.length; i++) {
      consentCheckboxes[i].checked = status;
    }
  }

  function agreeToAll() {
    setStatusForAll(1);
    saveConsents();
  }

  function rejectAll() {
    setStatusForAll(0);
    saveConsents();
  }

  function storeInitialHMTL() {
    initialConsentFormHTML = document.getElementById(CONTAINER_ID).innerHTML;
  }

  function restoreInitialHMTL() {
    document.getElementById(CONTAINER_ID).innerHTML = initialConsentFormHTML;
  }

  /** Makes consent form buttons interactive */
  function addEventListenersToConsentFormButtons() {
    document.getElementById(SAVE_ID).addEventListener('click', function() {
      saveConsents();
      ppms.cm.api('trackSaveChoicesClick');
    });
    document.getElementById(AGREE_TO_ALL_ID).addEventListener('click', function() {
      agreeToAll();
      ppms.cm.api('trackAgreeToAllClick');
    });
    document.getElementById(REJECT_ALL_ID).addEventListener('click', function() {
      rejectAll();
      ppms.cm.api('trackRejectAllClick');
    });
    document.getElementById(TOGGLE_DETAILS_ID).addEventListener('click', function() {
      toggleDetails();
    });
    document.getElementById(CLOSE_CONSENT_FORM_ID).addEventListener('click', function() {
      closeConsentForm();
      ppms.cm.api('trackCloseButtonClick');
    });
  }

  /** Opens consent form, and shows consent types from the consentTypesToShow parameter. If consentTypesToShow is
   * undefined, then all available consents are displayed. */
  function openConsentForm(consentTypesToShow, reason) {
    ppms.cm.api('getComplianceSettings', function (settings) {
      var consentDetails = settings.consents;
      var allTypes = Object.keys(consentDetails);

      restoreInitialHMTL();
      addEventListenersToConsentFormButtons();
      showDetailedConsentTypesList(consentTypesToShow || allTypes, consentDetails);
      showSimpleConsentTypesList(consentTypesToShow || allTypes);
      makeConsentFormVisible();

      switch (reason) {
        case OPEN_REASON_MANUAL: ppms.cm.api('trackPrivacyPolicyLinkView'); break;
        default: ppms.cm.api('trackMainFormView');
      }
    }, console.error);
  }

  /** Makes "open consent form" button interactive if it's present on the website */
  function addOpenConsentFormButtonListener() {
    var openConsentFormElement = document.getElementById(OPEN_CONSENT_FORM_ID);

    if (openConsentFormElement) {
      openConsentFormElement.addEventListener('click', function () {
        openConsentForm(undefined, OPEN_REASON_MANUAL);
      });
    }
  }

  /** If there are any new (not decided) consent types, then cookie is saved and openConsentForm function is called. */
  function setNewConsentTypes (newTypes) {
    if (newTypes.length > 0) {
      ppms.cm.api('setInitialComplianceSettings', newTypes, function () { openConsentForm(newTypes, OPEN_REASON_AUTO); }, console.error);
    }
  }

  /** Checks for the consent types user never decided to agree/disagree. Passes those types to setNewConsentTypes function. */
  function setConsents() {
    ppms.cm.api('getNewComplianceTypes', function (newTypes) { setNewConsentTypes(newTypes); }, console.error);
  }

  function init() {
    storeInitialHMTL();
    setConsents();
    addOpenConsentFormButtonListener();
  }

  init();
})();
</script>

To allow your users to open consent form on demand, place this code fragment on your website:

<button id="PiwikPROConsentForm-open-consent-form">
  Open 
</button>

You can apply whatever styles you want. The only thing required for this button to work is to keep its id attribute.

Consent stats tracking implementation is included for both Bottom bar and Modal examples and it covers tracking:

Additional resources