Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mkdocs-material[imaging]==9.7.5
mkdocs-macros-plugin==1.5.0
requests==2.33.1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not the current version?

1 change: 0 additions & 1 deletion source/assets/stylesheets/extra.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ code {
text-decoration: underline;
}


ol:not(.no-styling) {
list-style: none;
counter-reset: markdown-ordered-list;
Expand Down
74 changes: 74 additions & 0 deletions source/hooks/plausible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from __future__ import annotations

import json
import logging
import time
from pathlib import Path

import requests
from mkdocs.config.defaults import MkDocsConfig
Comment thread
mrdarrengriffin marked this conversation as resolved.

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(f"Discarding unusable allow list at {ALLOWLIST_FILE}: {exception}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.info(f"Discarding unusable allow list at {ALLOWLIST_FILE}: {exception}")
log.info("Discarding unusable allow list at %s: %s", ALLOWLIST_FILE, exception)

The same goes for your other loggers as well

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(f"Fetched {len(referrers)} allowed referrers")
return referrers


def allowed_referrers() -> list[str]:
"""The allow list, downloaded at most once per build."""
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(f"Could not refresh the allow list, reusing {ALLOWLIST_FILE}: {exception}")
return cached
log.info(
f"Could not fetch the allow list ({exception}), "
"every referrer will be reported to Plausible as unlisted"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have any value, should this not:
a) Fail the build
b) Disable Plausible

)
return []


def on_config(config: MkDocsConfig, **kwargs):
config.extra.setdefault("plausible", {})["allowed_referrers"] = allowed_referrers()
return config
16 changes: 10 additions & 6 deletions source/overrides/404.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{% extends "main.html" %}

{% block content %}
<h1>404 - Not found</h1>
<script>document.addEventListener("DOMContentLoaded", function () { plausible("404"); });</script>
{% endblock %}
{% extends "main.html" %}

{% block content %}
<h1>404 - Not found</h1>
<script>
document.addEventListener("DOMContentLoaded", function () {
if (typeof window.plausible === "function") window.plausible("404");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (typeof window.plausible === "function") window.plausible("404");
if (typeof window.plausible === "function") {
window.plausible("404")
};

});
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion source/overrides/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

{% block extrahead %}
{{ super() }}
<script async src="https://plausible.openhomefoundation.org/js/pa-yXO_VcjbsD8Bs4w6PgY5_.js"></script><script> window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; plausible.init()</script>
{% include "partials/plausible.html" %}
{% endblock %}
3 changes: 1 addition & 2 deletions source/overrides/partials/footer.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<footer class="md-footer md-typeset">
<p class="plausible-attribution">
This website uses <a href="https://www.openhomefoundation.org/blog/making-our-web-analytics-open-source-with-plausible/" target="_blank" rel="noopener">privacy-first analytics</a> to help us improve the site. You can view all data in our <a href="https://plausible.openhomefoundation.org/hacs.xyz" target="_blank" rel="noopener">public dashboard</a>.
This website uses <a href="https://www.openhomefoundation.org/blog/making-our-web-analytics-open-source-with-plausible/" target="_blank" rel="noopener">privacy-first analytics</a> to help us improve the site. You can view all data in our <a href="{{ config.extra.plausible.dashboard }}" target="_blank" rel="noopener">public dashboard</a>.
</p>
</footer>

50 changes: 50 additions & 0 deletions source/overrides/partials/plausible.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{#-
Plausible analytics.

Visitors arriving from their own Home Assistant or ESPHome instance send that

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESPHome has HACS now?

private URL as the referrer, so the tracker is initialized with a transformRequest
that replaces every referrer outside the Open Home Foundation allow list with a
single aggregate bucket. source/hooks/plausible.py fetches the allow list once per
build and the loop below is the only thing that ever sees the real referrer.
-#}
<script async src="{{ config.extra.plausible.script }}"></script>
<script>
(function () {
var allowedReferrers = {{ config.extra.plausible.allowed_referrers | tojson }};

window.plausible = window.plausible || function () {
(plausible.q = plausible.q || []).push(arguments);
};
plausible.init = plausible.init || function (options) {
plausible.o = options || {};
};

plausible.init({
transformRequest: function (payload) {
if (!payload.r) {
return payload;
}

var host = "";
try {
host = new URL(payload.r).hostname.replace(/\.$/, "");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also be lowercase?

} catch (error) {
// A referrer we cannot parse falls through and gets replaced.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably return early here then.

}

for (var index = 0; index < allowedReferrers.length; index++) {
var domain = allowedReferrers[index];
if (host === domain || (host.length > domain.length && host.slice(-(domain.length + 1)) === "." + domain)) {
return payload;
}
}
Comment on lines +35 to +40

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to do this loop if the try/catch above failed as host will be ""


// One aggregate bucket, so we can see how much we filter without learning
// anything about individual visitors. RFC 2606 reserves .invalid, so this
// can never collide with a real domain.
payload.r = "https://unlisted.invalid/";
return payload;
},
});
})();
</script>
Loading