From ebc72dc51b2cce2d5aad567955e0427a9584d89b Mon Sep 17 00:00:00 2001 From: Andrew Isherwood Date: Wed, 20 Jan 2021 09:26:27 +0000 Subject: [PATCH] Bug 27378: Add cookie consent display to staff UI This commit adds the display of the cookie consent bar and modal to the staff interface: - Adds a new cookieconsent.js script that drives the display and functionality of the cookie consent bar and modal - Adds styles for the cookie consent bar and modal - Adds the creation and population of the cookie consent bar and modal, if appropriate --- .../prog/css/src/staff-global.scss | 49 ++++++ .../prog/en/includes/intranet-bottom.inc | 69 ++++++++ .../intranet-tmpl/prog/js/cookieconsent.js | 165 ++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 koha-tmpl/intranet-tmpl/prog/js/cookieconsent.js diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss index 67822e88cf..aebe129456 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss +++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss @@ -4404,6 +4404,55 @@ div .suggestion_note { } } +/* Cookie consent bar */ +#cookieConsentBar { + display: none; + padding: 20px; + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: rgba(0,0,0,0.7); + color: rgba(255,255,255,0.9); + z-index: 1; + a:link, a:visited { + color: #d7ebff; + } + #consentButtons { + margin-top: 10px; + text-align: right; + } + button:not(:last-child) { + margin-right: 10px; + } +} + +/* Cookie consent modal */ +#cookieConsentModal { + .modal-dialog { + width: 700px; + } + .consentModalItem { + display: flex; + align-items: center; + border: 1px solid #ccc; + padding: 10px 20px; + border-radius: 5px; + } + .consentModalItem:not(:last-child) { + margin-bottom: 20px; + } + .consentItemCheckbox { + padding-right: 20px; + } + .consentItemName { + font-weight: bold; + } + #cookieConsentPopupText { + margin: 20px 0; + } +} + @media (min-width: 200px) { } diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/intranet-bottom.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/intranet-bottom.inc index 27ba415943..5a94f7d53a 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/intranet-bottom.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/intranet-bottom.inc @@ -1,6 +1,10 @@ [% USE raw %] [% USE Koha %] [% USE KohaPlugins %] +[% USE Asset %] +[% IF Koha.Preference( 'CookieConsent' ) %] + [% USE JSConsents %] +[% END %] [% IF ( ( languages_loop ) && ( ! popup_window ) && ( Koha.Preference('StaffLangSelectorMode') == 'both' || Koha.Preference('StaffLangSelectorMode') == 'footer') ) %] [% UNLESS ( one_language_enabled ) %] @@ -74,5 +78,70 @@ [% END %] [% KohaPlugins.get_plugins_intranet_js | $raw %] + + [% IF Koha.Preference( 'CookieConsent' ) %] + + + + + [% END %] + + [% IF Koha.Preference( 'CookieConsent' ) && Koha.Preference( 'ConsentJS' ).length > 0 %] + [% consents = JSConsents.all() %] + [% FOREACH consent IN consents %] + + [% END %] + [% END %] + [% IF Koha.Preference( 'CookieConsent' ) %] + [% Asset.js("js/cookieconsent.js") | $raw %] + [% END %] + diff --git a/koha-tmpl/intranet-tmpl/prog/js/cookieconsent.js b/koha-tmpl/intranet-tmpl/prog/js/cookieconsent.js new file mode 100644 index 0000000000..c718c73e39 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/cookieconsent.js @@ -0,0 +1,165 @@ +(function () { + // Has the user previously consented, establish our + // initial state + let selected = []; + let existingConsent = ''; + // The presence of a 'cookieConsent' local storage item indicates + // that previous consent has been set. If the value is empty, then + // no consent to non-essential cookies was given + const hasStoredConsent = localStorage.getItem('cookieConsent'); + getExistingConsent(); + + // The consent bar may not be in the DOM if it is not being used + const consentBar = $('#cookieConsentBar'); + if (!consentBar) { + return; + } + if (hasStoredConsent === null) { + showConsentBar(); + } + addButtonHandlers(); + + // When the modal is opened, populate our state based on currently + // selected values + $('#cookieConsentModal').on('shown.bs.modal', function () { + initialiseSelected(); + }); + + // Initialise existing consent based on local storage + function getExistingConsent() { + existingConsent = localStorage.getItem('cookieConsent') ? + localStorage.getItem('cookieConsent') : + []; + } + + function showConsentBar() { + const consentBar = $('#cookieConsentBar'); + consentBar.attr('aria-hidden', 'false'); + consentBar.show(); + } + + function hideConsentBar() { + const consentBar = $('#cookieConsentBar'); + consentBar.attr('aria-hidden', 'true'); + consentBar.hide(); + } + + // Hides the appropriate consent container, depending on what + // is currently visible + function hideContainer() { + if ($('#cookieConsentModal').hasClass('in')) { + $('#cookieConsentModal').modal('hide'); + } else { + hideConsentBar(); + } + } + + // Initialise our state of selected item and enable/disable + // the "Accept selected" button appropriately + function initialiseSelected() { + selected = []; + $('.consentCheckbox').each(function () { + const val = $(this).val(); + if (existingConsent.indexOf(val) > -1 ) { + $(this).prop('checked', true); + selected.push(val); + } else { + $(this).prop('checked', false); + } + }); + enableDisableSelectedButton(); + } + + // Maintain our state of selected items and enable/disable + // the "Accept selected" button appropriately + function maintainSelected() { + selected = []; + $('.consentCheckbox:checked').each(function () { + selected.push($(this).val()); + }); + enableDisableSelectedButton(); + } + + // Set the enabled / disabled state of the + // "Accept selected button based on the checkbox + // states + function enableDisableSelectedButton() { + $('#consentAcceptSelected').prop( + 'disabled', + selected.length === 0 + ); + } + + function runConsentedCode() { + getExistingConsent(); + $('.consentCode').each(function () { + // The user has explicitly consented to this code, or the + // code doesn't require consent in the staff view + if ( + existingConsent.indexOf($(this).data('consent-id')) > -1 || + !$(this).data('requires-consent') + ) { + const code = atob($(this).data('consent-code')); + const func = Function(code); + func(); + } + }); + } + + function addButtonHandlers() { + // "Accept all" handler + $('.consentAcceptAll').on('click', function(e) { + e.preventDefault(); + let toSave = []; + $('.consentCheckbox').each(function () { + const val = $(this).val(); + toSave.push(val); + }); + localStorage.setItem('cookieConsent', toSave); + hideContainer(); + runConsentedCode(); + }); + + // "Accept essential" handler + $('.consentAcceptEssential').on('click', function(e) { + e.preventDefault(); + localStorage.setItem('cookieConsent', []); + hideContainer(); + }); + + // "Accept selected" handler + $('#consentAcceptSelected').on('click', function(e) { + e.preventDefault(); + const toSave = selected.length > 0 ? selected : []; + localStorage.setItem('cookieConsent', toSave); + hideContainer(); + runConsentedCode(); + }); + + // "More information" handler + $('#consentMoreInfo').on( + 'click', + function (e) { + e.preventDefault(); + hideConsentBar(); + // Ensure we're up to date with the existing consent + getExistingConsent(); + // Prevent the modal from being closed with anything + // but the buttons + $('#cookieConsentModal') + .modal({ + backdrop: 'static', + keyboard: false, + focus: true, + show: true + }); + } + ); + $('.consentCheckbox').on('click', function () { + maintainSelected(); + }); + } + + // On page load, run any code that has been given consent + runConsentedCode(); +})(); -- 2.20.1