diff --git a/mkdocs.yml b/mkdocs.yml index b7cb28c4..448fc1fd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -62,7 +62,11 @@ theme: icon: annotation: material/information plugins: - - privacy + - privacy: + assets_exclude: + # The tracker has to be loaded from the Plausible instance, so that it stays + # current instead of being frozen into a self-hosted copy at build time. + - plausible.openhomefoundation.org/* - macros: on_undefined: strict include_dir: source/includes @@ -76,8 +80,12 @@ plugins: - tags hooks: - source/hooks/html_tag_modifier.py + - source/hooks/plausible.py - source/hooks/shortcodes.py extra: + plausible: + script: https://plausible.openhomefoundation.org/js/pa-yXO_VcjbsD8Bs4w6PgY5_.js + dashboard: https://plausible.openhomefoundation.org/hacs.xyz resources: - link: https://github.com/hacs/.github/blob/master/CODE_OF_CONDUCT.md title: Code of Conduct diff --git a/requirements.txt b/requirements.txt index 70d27a99..2e7aa6f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ mkdocs-material[imaging]==9.7.5 mkdocs-macros-plugin==1.5.0 +requests==2.34.2 diff --git a/source/assets/stylesheets/extra.css b/source/assets/stylesheets/extra.css index 24778b20..3886356c 100644 --- a/source/assets/stylesheets/extra.css +++ b/source/assets/stylesheets/extra.css @@ -172,7 +172,6 @@ code { text-decoration: underline; } - ol:not(.no-styling) { list-style: none; counter-reset: markdown-ordered-list; diff --git a/source/hooks/plausible.py b/source/hooks/plausible.py new file mode 100644 index 00000000..cc1370f6 --- /dev/null +++ b/source/hooks/plausible.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +import json +import logging +import time +from pathlib import Path + +import requests +from mkdocs.config.defaults import MkDocsConfig + +ALLOWLIST_URL = "https://www.openhomefoundation.org/allowed-referrers.json" +ALLOWLIST_FILE = Path(".cache/plausible/allowed-referrers.json") +ALLOWLIST_MAX_AGE = 3600 + +# `strict: true` aborts the build on anything logged at WARNING or above under the +# "mkdocs" logger, and an unreachable allow list must never break the site, so the +# fallbacks below report at INFO instead. +log = logging.getLogger("mkdocs.hooks.plausible") + + +def normalize_referrers(payload: object) -> list[str]: + """Reduce the allow list payload to bare, lowercase domains.""" + if not isinstance(payload, list) or not all(isinstance(entry, str) for entry in payload): + raise ValueError("payload is not an array of strings") + return [domain for entry in payload if (domain := entry.strip().lower().removesuffix("."))] + + +def cached_referrers() -> list[str] | None: + """The allow list left behind by an earlier build, if it is still usable.""" + try: + return normalize_referrers(json.loads(ALLOWLIST_FILE.read_text())) + except FileNotFoundError: + return None + except (OSError, ValueError) as exception: + log.info("Discarding unusable allow list at %s: %s", ALLOWLIST_FILE, exception) + return None + + +def download_referrers() -> list[str]: + """Download the allow list and cache it for subsequent builds.""" + response = requests.get(ALLOWLIST_URL, timeout=30) + response.raise_for_status() + referrers = normalize_referrers(response.json()) + ALLOWLIST_FILE.parent.mkdir(parents=True, exist_ok=True) + ALLOWLIST_FILE.write_text(json.dumps(referrers, indent=4, sort_keys=True) + "\n") + log.info("Fetched %s allowed referrers", len(referrers)) + return referrers + + +def allowed_referrers() -> list[str]: + """The allow list, downloaded at most once per build. + + An empty result means no referrer can be checked, which leaves Plausible out of + the build entirely rather than reporting every visit as unlisted. + """ + if ( + ALLOWLIST_FILE.exists() + and time.time() - ALLOWLIST_FILE.stat().st_mtime < ALLOWLIST_MAX_AGE + and (cached := cached_referrers()) is not None + ): + return cached + + try: + return download_referrers() + except (OSError, ValueError, requests.RequestException) as exception: + if (cached := cached_referrers()) is not None: + log.info("Could not refresh the allow list, reusing %s: %s", ALLOWLIST_FILE, exception) + return cached + log.info("Could not fetch the allow list (%s), Plausible is left out of this build", exception) + return [] + + +def on_config(config: MkDocsConfig, **kwargs): + config.extra.setdefault("plausible", {})["allowed_referrers"] = allowed_referrers() + return config diff --git a/source/overrides/404.html b/source/overrides/404.html index db0d5c35..5ce1c38a 100644 --- a/source/overrides/404.html +++ b/source/overrides/404.html @@ -1,6 +1,12 @@ -{% extends "main.html" %} - -{% block content %} -