feat: add configurable link to external podcast (closes #307) (#414)

* feat: use idiomatic Hugo dot context for podcast link (closes #307) Use '.' instead of repeating site.Params.podcast_url inside the 'with' block — idiomatic Hugo template pattern. Simplify the corresponding test assertions. The configurable external podcast link feature is complete: - podcast_url param in hugo.toml (empty = disabled) - Footer conditionally renders link with safeURL sanitization - Opens in new tab with noopener noreferrer - No audio hosting, embed, or RSS feed added - BaseURL-safe, does not affect article generation Closes #307 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: trim before with, fix safeURL comment, tighten assertions - Move trim before Hugo `with` so whitespace-only podcast_url is falsy - Fix misleading comment: safeURL marks trusted, doesn't sanitize - Tighten test to verify dot-context href pattern specifically Addresses all 3 Copilot review threads on PR #414. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 12, 2026 at 13:09 UTC 0be2c9967914f915a86a117c6b5847fd948fea8b
2 files changed +12 -11
layouts/partials/footer.html
+2 -2
@@ -9,8 +9,8 @@
9 <a href="https://github.com/jmservera/SquadScope" target="_blank" rel="noopener">GitHub</a>
10 <a href="{{ "index.xml" | absLangURL }}">RSS</a>
11 <a href="{{ "archive/" | absLangURL }}">Archive</a>
12 - {{- with site.Params.podcast_url }}
13 - <a href="{{ site.Params.podcast_url | trim | safeURL }}" target="_blank" rel="noopener noreferrer">Podcast</a>
12 + {{- with site.Params.podcast_url | trim }}
13 + <a href="{{ . | safeURL }}" target="_blank" rel="noopener noreferrer">Podcast</a>
14 {{- end }}
15 </nav>
16 <p class="site-footer__meta">© 2026 jmservera · Generated 2026-05-25 · Powered by <a href="https://gohugo.io/?utm_source=squadscope" rel="noopener" target="_blank">Hugo</a></p>
tests/test_podcast_link.py
+10 -9
@@ -2,8 +2,8 @@
2
3 from __future__ import annotations
4
5 -from pathlib import Path
5 import re
6 +from pathlib import Path
7
8 REPO_ROOT = Path(__file__).resolve().parent.parent
9
@@ -17,15 +17,16 @@ def test_podcast_url_param_exists_in_hugo_config() -> None:
17 def test_footer_conditionally_renders_podcast_link() -> None:
18 """Footer template must conditionally render podcast link from site param."""
19 footer = (REPO_ROOT / "layouts" / "partials" / "footer.html").read_text(encoding="utf-8")
20 - # Must use Hugo's `with` or `if` to conditionally render
21 - assert "site.Params.podcast_url" in footer
22 - # Must pipe through safeURL to prevent unsafe schemes
20 + # Must trim before `with` so whitespace-only values are treated as falsy
21 + assert "site.Params.podcast_url | trim" in footer
22 + # safeURL marks the value as trusted (bypasses scheme filtering) — safe here
23 + # because the value comes from controlled site config, not user input.
24 assert "safeURL" in footer
24 - podcast_link_match = re.search(r'<a\s+href="{{\s*site\.Params\.podcast_url\s*\|\s*trim\s*\|\s*safeURL\s*}}"\s+([^>]*)>Podcast</a>', footer)
25 - assert podcast_link_match, "Footer must contain a Podcast link with a sanitized podcast_url href"
26 - podcast_link = podcast_link_match.group(1)
27 - assert 'target="_blank"' in podcast_link
28 - assert 'rel="noopener noreferrer"' in podcast_link
25 + # Verify the podcast link uses dot context with correct attrs
26 + assert re.search(
27 + r'<a\s+href="{{\s*\.\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*>Podcast</a>',
28 + footer,
29 + ), "Footer podcast link must use dot context piped through safeURL with target=_blank and rel=noopener noreferrer"
30
31
32 def test_podcast_url_defaults_to_empty() -> None: