diff --git a/docs/_overrides/main.html b/docs/_overrides/main.html index 5490c824..5f7a8b75 100644 --- a/docs/_overrides/main.html +++ b/docs/_overrides/main.html @@ -33,13 +33,19 @@ : document.querySelector("[data-aaasm-outdated]"); if (!host) return; - // Derive the current version directory and the versions.json URL from the - // page location: mike serves each version under ///, with - // versions.json one level up at //versions.json. - var path = window.location.pathname.replace(/\/+$/, ""); - var segments = path.split("/"); - var current = segments.pop() || ""; - var versionsUrl = segments.join("/") + "/versions.json"; + // mike serves each version under ///, with versions.json + // one level up at //versions.json. mkdocs renders `base_url` so it + // points at the deploy root (the current version directory) from *any* + // page depth, so the deploy-root versions.json is reliably one level + // above it. Resolving the bare relative `base_url` against `location` + // yields an absolute URL; popping only the page's last path segment (the + // previous approach) 404s on deep pages. + var versionRoot = new URL("{{ base_url }}/", window.location); + var versionsUrl = new URL("../versions.json", versionRoot).toString(); + + // The current version id is the last path segment of the deploy root. + var current = + versionRoot.pathname.replace(/\/+$/, "").split("/").pop() || ""; fetch(versionsUrl, { cache: "no-store" }) .then(function (r) { return r.ok ? r.json() : []; }) diff --git a/docs/_overrides/partials/integrations/analytics.html b/docs/_overrides/partials/integrations/analytics.html new file mode 100644 index 00000000..f9760b86 --- /dev/null +++ b/docs/_overrides/partials/integrations/analytics.html @@ -0,0 +1,27 @@ +{#- + Override of Material's partials/integrations/analytics.html (AAASM-3558). + + Material's generated partial only calls `__md_analytics()` once the analytics + consent cookie is granted (`__md_get("__consent").analytics`). With our opt-in + consent (cookies.analytics.checked: false, off by default) that call never + fires for ordinary visitors, so gtag.js is never loaded and GA never reports. + + We switch python to *advanced* Consent Mode v2 (matching the other four doc + sites): the overridden google.html partial declares `analytics_storage: + 'denied'` as the default before loading gtag, so calling `__md_analytics()` + unconditionally is GDPR-correct — GA sends cookieless, modelled pings until + the visitor grants analytics consent, then full hits. We therefore drop + Material's consent gate and always run the loader. + + The native `extra.consent` banner UI is kept; google.html reads the stored + consent on each load and updates Consent Mode to 'granted' when the analytics + cookie is enabled (the consent form reloads the page on submit, so reading + stored consent at load time is sufficient — no live form hook needed). +-#} +{% if config.extra.analytics %} + {% set provider = config.extra.analytics.provider %} +{% endif %} +{% if provider %} + {% include "partials/integrations/analytics/" ~ provider ~ ".html" %} + +{% endif %} diff --git a/docs/_overrides/partials/integrations/analytics/google.html b/docs/_overrides/partials/integrations/analytics/google.html new file mode 100644 index 00000000..239206b2 --- /dev/null +++ b/docs/_overrides/partials/integrations/analytics/google.html @@ -0,0 +1,102 @@ +{#- + Override of Material's partials/integrations/analytics/google.html (AAASM-3558). + + Material's generated partial defines `__md_analytics()` (the gtag.js loader + + `config` + search/feedback wiring) but loads GA with *basic* Consent Mode — it + only runs once the analytics consent cookie is granted. With our opt-in consent + GA never loaded for ordinary visitors (window.gtag stayed undefined, zero + /g/collect hits). + + This override keeps Material's `__md_analytics()` body intact (so the feedback + widget is revealed + wired and its single GA `feedback` event still flows + through Material's own handler — no custom handler, no double-count) but turns + it into *advanced* Consent Mode v2, matching the other four doc sites: + + 1. Before `gtag('config', …)`, declare Consent Mode defaults as DENIED for + analytics_storage / ad_storage / ad_user_data / ad_personalization, so + gtag.js sends cookieless, modelled pings until the visitor consents. + 2. Read Material's stored consent (`__md_get('__consent')`, namespaced to the + deploy root) and, if the analytics cookie is enabled, immediately update + analytics_storage to 'granted' — upgrading from cookieless to full hits. + The consent banner reloads the page on submit, so reading stored consent + at load time covers accept/reject without a live form hook. + + `partials/integrations/analytics.html` (also overridden) now calls + `__md_analytics()` unconditionally, so this runs on every page load. The + Measurement ID + feedback markup still come from `extra.analytics` in + mkdocs.yml. Mirrors mkdocs-material 9.7.6; only the Consent Mode lines are added. +-#} +{% if config.extra.analytics %} + {% set property = config.extra.analytics.property | d("", true) %} +{% endif %} + diff --git a/docs/_overrides/partials/javascripts/outdated.html b/docs/_overrides/partials/javascripts/outdated.html new file mode 100644 index 00000000..a3062a0d --- /dev/null +++ b/docs/_overrides/partials/javascripts/outdated.html @@ -0,0 +1,14 @@ +{#- + Override of Material's partials/javascripts/outdated.html (AAASM-3558, carried + over from the closed PR #169 / AAASM-3557). + + Material's generated script calls `new URL("{{ base_url }}")` with no base + argument. On many pages `base_url` is a bare relative value such as `.` or + `..`, which is not an absolute URL, so `new URL("..")` throws + `TypeError: Failed to construct 'URL': Invalid URL` on every page load. + + The only change here is passing `location` as the base so the relative + `base_url` resolves against the current page. Behaviour is otherwise identical + to Material's generated script. +-#} + diff --git a/scripts/ci/deploy-release-version-documentation.sh b/scripts/ci/deploy-release-version-documentation.sh index 2eb5428e..b86cf141 100755 --- a/scripts/ci/deploy-release-version-documentation.sh +++ b/scripts/ci/deploy-release-version-documentation.sh @@ -153,8 +153,13 @@ fi # Set the default (root-redirect target) to the most authoritative channel that # exists, as decided by the resolver: stable, else pre-release, else latest. +# Use a custom redirect template that carries the GA snippet (advanced Consent +# Mode v2, cookieless-until-consent) so Google's "Test your website" detects the +# tag on the bare-root URL too (AAASM-3558). echo "🎯 Setting default channel (root redirect) to '${DEFAULT_CHANNEL}'" -mike set-default --push "${DEFAULT_CHANNEL}" +mike set-default --push \ + --template "${PROJECT_ROOT}/scripts/ci/templates/mike-redirect-with-analytics.html" \ + "${DEFAULT_CHANNEL}" echo "🍻 Release documentation deployed for ${RELEASE_TAG}: stable=${STABLE_VERSION:-(none)}," \ "pre-release=${PRERELEASE_VERSION:-(hidden)}, default=${DEFAULT_CHANNEL}." diff --git a/scripts/ci/templates/mike-redirect-with-analytics.html b/scripts/ci/templates/mike-redirect-with-analytics.html new file mode 100644 index 00000000..7d6d4a8d --- /dev/null +++ b/scripts/ci/templates/mike-redirect-with-analytics.html @@ -0,0 +1,42 @@ + + + + + Redirecting + + + + + + + + Redirecting to {{href}}... + +