First draft.
This commit is contained in:
@@ -1,3 +1,85 @@
|
|||||||
# hugo-cookie-consent
|
# hugo-cookie-consent
|
||||||
|
|
||||||
Hugo module that implements a cookie-consent message and functionality to change the cookie-consent decision.
|
**Hugo module that implements a cookie-consent message and functionality to change
|
||||||
|
the cookie-consent decision.**
|
||||||
|
|
||||||
|
The purpose of this module is to help make a site [GDPR][] compliant. It can
|
||||||
|
optionally insert tracking code that calls a [Matomo][] instance. This will only
|
||||||
|
happen after visitors have accepted the use of a cookie -- no logging will take
|
||||||
|
place before visitors have interacted with the cookie information message. Their
|
||||||
|
decision is stored in the browser's Local Storage, and it is _not_ personal data
|
||||||
|
as defined by the GDPR.
|
||||||
|
|
||||||
|
This Hugo Module requires Hugo [v0.146.0][]+ which uses the new layout directory
|
||||||
|
structure with `_partials` and `_shortcode` directories with leading underscores.
|
||||||
|
|
||||||
|
[Anthropic Claude][] helped in creating this module (paid subscription).
|
||||||
|
|
||||||
|
## Installation in your Hugo site
|
||||||
|
|
||||||
|
1. Turn the Hugo site into a Hugo module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hugo mod init <identifier>
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `<identifier>` may be the path to the site's Git repository or anything
|
||||||
|
else; see [Hugo's docs][mod-doc].
|
||||||
|
|
||||||
|
2. Add the hugo-cookie-consent module by adding this `module.imports.path` to your
|
||||||
|
site configuration file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# config/_default/hugo.yaml
|
||||||
|
module:
|
||||||
|
imports:
|
||||||
|
- path: git.bovender.de/daniel/hugo-cookie-consent
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Add configuration keys (see [below](#configuration)).
|
||||||
|
|
||||||
|
4. If your site's language is neither English nor German, add a few terms in the
|
||||||
|
desired language to the language tables.
|
||||||
|
|
||||||
|
5. Make your pages load the JavaScript code, e. g. by adding this to a `script.html`
|
||||||
|
partial:
|
||||||
|
|
||||||
|
```html
|
||||||
|
{{- $js := resources.Get "js/cookie-consent.js" | minify -}}
|
||||||
|
<script src="{{ $js.RelPermalink }}" defer></script>
|
||||||
|
{{- $js := resources.Get "js/tracker-code.js" | minify -}}
|
||||||
|
<script src="{{ $js.RelPermalink }}" defer></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Optionally, insert a `cookie-settings` partial somewhere on your site to enable
|
||||||
|
visitors to review and revise their choice. The privacy statement page might
|
||||||
|
be a good place for this.
|
||||||
|
|
||||||
|
```md
|
||||||
|
{{< cookie-settings >}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You can configure the module by defining site parameters in your
|
||||||
|
`hugo.yaml` or `hugo.toml` configuration file. The following
|
||||||
|
snippet shows the default values.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# config/_default/params.yaml
|
||||||
|
hugo_cookie_consent:
|
||||||
|
privacy_policy_url:
|
||||||
|
enable_matomo: false
|
||||||
|
matomo_host:
|
||||||
|
matomo_site_id:
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
|-----------|-------------|
|
||||||
|
| `privacy_policy_url` | URL of the privacy policy page
|
||||||
|
|
||||||
|
[anthropic claude]: https://claude.ai
|
||||||
|
[gdpr]: https://en.wikipedia.org/wiki/GDPR
|
||||||
|
[matomo]: https://matomo.org
|
||||||
|
[mod-doc]: https://gohugo.io/hugo-modules/use-modules/#import
|
||||||
|
[v0.146.0]: https://github.com/gohugoio/hugo/releases/tag/v0.146.0
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
(function () {
|
||||||
|
var KEY = 'cookie_consent';
|
||||||
|
var banner = document.getElementById('cookie-banner');
|
||||||
|
if (!banner) return;
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
var consent = localStorage.getItem(KEY);
|
||||||
|
if (!consent) banner.style.display = 'flex';
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
banner.classList.add('fade-out');
|
||||||
|
setTimeout(function () { banner.style.display = 'none'; }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStatus() {
|
||||||
|
var el = document.getElementById('cookie-status');
|
||||||
|
if (!el) return;
|
||||||
|
var consent = localStorage.getItem(KEY);
|
||||||
|
el.textContent = consent === 'accepted' ? el.dataset.accepted
|
||||||
|
: consent === 'declined' ? el.dataset.declined
|
||||||
|
: el.dataset.none;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('cookie-accept').onclick = function () {
|
||||||
|
localStorage.setItem(KEY, 'accepted');
|
||||||
|
updateStatus();
|
||||||
|
dismiss();
|
||||||
|
window.dispatchEvent(new Event('cookieAccepted'));
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('cookie-decline').onclick = function () {
|
||||||
|
localStorage.setItem(KEY, 'declined');
|
||||||
|
updateStatus();
|
||||||
|
dismiss();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.reopenCookieBanner = function () {
|
||||||
|
localStorage.removeItem(KEY);
|
||||||
|
updateStatus();
|
||||||
|
banner.classList.remove('fade-out');
|
||||||
|
banner.style.display = 'flex';
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('cookieAccepted', function () {
|
||||||
|
if (typeof window.trackVisit === 'function') window.trackVisit();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (localStorage.getItem(KEY) === 'accepted') {
|
||||||
|
if (typeof window.trackVisit === 'function') window.trackVisit();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
(function () {
|
||||||
|
function trackVisit() {
|
||||||
|
var banner = document.getElementById('cookie_banner');
|
||||||
|
if (!banner) return;
|
||||||
|
var matomoUrl = banner.dataset.matomoUrl;
|
||||||
|
var siteId = banner.dataset.matomoSiteId;
|
||||||
|
if (!matomoUrl || !siteId) return;
|
||||||
|
|
||||||
|
var _paq = window._paq = window._paq || [];
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
_paq.push(['setTrackerUrl', matomoUrl + 'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', siteId]);
|
||||||
|
var d = document, g = d.createElement('script'),
|
||||||
|
s = d.getElementsByTagName('script')[0];
|
||||||
|
g.async = true; g.src = matomoUrl + 'matomo.js';
|
||||||
|
s.parentNode.insertBefore(g, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('cookieAccepted', trackVisit);
|
||||||
|
if (localStorage.getItem('cookie_consent') === 'accepted') {
|
||||||
|
trackVisit();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#cookie-banner {
|
||||||
|
position: fixed;
|
||||||
|
top: 2.5rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: min(480px, calc(100vw - 2rem));
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e2e2e2;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
box-shadow: 0 4px 24px rgba(0,0,0,.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cookie-banner.fade-out {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cookie-banner p { margin: 0; font-size: 14px; flex: 1; }
|
||||||
|
|
||||||
|
#cookie-banner button {
|
||||||
|
margin: 0.1rem 0;
|
||||||
|
padding: .45rem 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cookie-accept {
|
||||||
|
background: #0b913a; color: #fff; border-color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
hugo_cookie_consent:
|
||||||
|
privacy_policy_url:
|
||||||
|
enable_matomo: false
|
||||||
|
matomo_host:
|
||||||
|
matomo_site_id:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
site_wants_cookie:
|
||||||
|
other: Diese Website möchte ein Cookie speichern.
|
||||||
|
cookie_more_info:
|
||||||
|
other: Mehr darüber.
|
||||||
|
cookie_button_accept:
|
||||||
|
other: Akzeptieren
|
||||||
|
cookie_button_decline:
|
||||||
|
other: Ablehnen
|
||||||
|
cookie_status:
|
||||||
|
other: Aktueller Status
|
||||||
|
cookie_accepted:
|
||||||
|
other: Akzeptiert
|
||||||
|
cookie_declined:
|
||||||
|
other: Abgelehnt
|
||||||
|
cookie_none:
|
||||||
|
other: Noch keine Entscheidung
|
||||||
|
cookie_change:
|
||||||
|
other: Cookie-Einstellungen ändern
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
site_wants_cookie:
|
||||||
|
other: This site would like to store a cookie.
|
||||||
|
cookie_more_info:
|
||||||
|
other: More about this.
|
||||||
|
cookie_button_accept:
|
||||||
|
other: Accept
|
||||||
|
cookie_button_decline:
|
||||||
|
other: Decline
|
||||||
|
cookie_status:
|
||||||
|
other: Current status
|
||||||
|
cookie_accepted:
|
||||||
|
other: Accepted
|
||||||
|
cookie_declined:
|
||||||
|
other: Declined
|
||||||
|
cookie_none:
|
||||||
|
other: No decision yet
|
||||||
|
cookie_change:
|
||||||
|
other: Change cookie preferences
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<div id="cookie-banner" style="display:none" role="dialog"
|
||||||
|
aria-label="Cookie consent" aria-live="polite"
|
||||||
|
data-matomo-url="{{ .Site.Params.hugo_cookie_consent.matomo_host }}"
|
||||||
|
data-matomo-site-id="{{ .Site.Params.hugo_cookie_consent_matomo_site_id }}" >
|
||||||
|
<p>
|
||||||
|
{{ i18n "site_wants_cookie" }}
|
||||||
|
<a href="{{ .Site.Params.hugo_cookie_consent.privacy_policy_url }}">
|
||||||
|
{{ i18n "cookie_more_info" }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button id="cookie-accept">
|
||||||
|
{{ i18n "cookie_button_accept" }}
|
||||||
|
</button>
|
||||||
|
<button id="cookie-decline">
|
||||||
|
{{ i18n "cookie_button_decline" }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<p>
|
||||||
|
{{ i18n "cookie_status" }}:
|
||||||
|
<strong id="cookie_status"
|
||||||
|
data-accepted="{{ i18n "cookie_accepted" }}"
|
||||||
|
data-declined="{{ i18n "cookie_declined" }}"
|
||||||
|
data-none="{{ i18n "cookie_none" }}">
|
||||||
|
—
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
<button onclick="reopenCookieBanner()">
|
||||||
|
{{ i18n "cookie_change" }}
|
||||||
|
</button>
|
||||||
Reference in New Issue
Block a user