Compare commits

...

1 Commits

Author SHA1 Message Date
Jorijn Schrijvershof
a3de6cce20 feat: add configurable custom HTML head injection
Allow deployers to inject custom HTML into the <head> of every page
via the CUSTOM_HEAD_HTML config option, useful for analytics scripts
(Plausible, Matomo, etc.) without modifying source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 11:14:15 +01:00
5 changed files with 42 additions and 0 deletions

View File

@@ -148,6 +148,14 @@ RADIO_CODING_RATE=CR8
# TELEMETRY_RETRY_ATTEMPTS=2
# TELEMETRY_RETRY_BACKOFF_S=4
# =============================================================================
# Custom HTML (Analytics, etc.)
# =============================================================================
# Inject custom HTML into the <head> of every page.
# Useful for analytics scripts (Plausible, Matomo, etc.) without modifying source.
# Example for Plausible:
# CUSTOM_HEAD_HTML=<script defer data-domain="stats.example.com" src="https://plausible.io/js/script.js"></script>
# =============================================================================
# Paths (Native installation only)
# =============================================================================

View File

@@ -273,6 +273,9 @@ class Config:
self.html_path = get_str("HTML_PATH", "") or ""
# Custom HTML injected into <head> (e.g. analytics scripts)
self.custom_head_html = get_str("CUSTOM_HEAD_HTML", "") or ""
# Global config instance
_config: Config | None = None

View File

@@ -727,6 +727,9 @@ def build_page_context(
"page_title": page_title,
"page_subtitle": page_subtitle,
"chart_groups": chart_groups,
# Custom HTML
"custom_head_html": cfg.custom_head_html,
}
@@ -1304,6 +1307,7 @@ def render_report_page(
"monthly_links": monthly_links,
"prev_report": prev_report,
"next_report": next_report,
"custom_head_html": cfg.custom_head_html,
}
template = env.get_template("report.html")
@@ -1341,6 +1345,7 @@ def render_reports_index(report_sections: list[dict]) -> str:
"css_path": "../",
"report_sections": report_sections,
"month_abbrs": month_abbrs,
"custom_head_html": cfg.custom_head_html,
}
template = env.get_template("report_index.html")

View File

@@ -19,6 +19,7 @@
<meta name="twitter:description" content="{{ meta_description }}">
<link rel="stylesheet" href="{{ css_path }}styles.css">
{% if custom_head_html %}{{ custom_head_html | safe }}{% endif %}
</head>
<body>
{% block body %}{% endblock %}

View File

@@ -147,3 +147,28 @@ class TestTemplateRendering:
assert "<html" in html
assert "<head>" in html
assert "<body>" in html
def test_custom_head_html_rendered_when_set(self):
"""Custom head HTML appears in rendered output when provided."""
env = get_jinja_env()
template = env.get_template("base.html")
snippet = '<script defer data-domain="example.com" src="https://plausible.io/js/script.js"></script>'
html = template.render(
title="Test",
custom_head_html=snippet,
)
assert snippet in html
assert html.index(snippet) < html.index("</head>")
def test_custom_head_html_absent_when_empty(self):
"""No extra content in head when custom_head_html is empty."""
env = get_jinja_env()
template = env.get_template("base.html")
html_with = template.render(title="Test", custom_head_html="")
html_without = template.render(title="Test")
# Both should produce identical output (no extra content)
assert html_with == html_without