Link every page to its own source at the revision it was built fromUnverified
Borrowed from fzakaria.com's site-foot: an "Improve this page" link in the footer, pointing at the page's own content file on the forge. It goes to /blob/<rev>/<path> at the exact revision the site was built from, not to main, so a reader who follows it from a deployed page reads the source that produced what they were just looking at rather than whatever main has moved on to since. The revision comes from the flake: self.rev exists only for a clean tree, so `hugo server` and a `nix build` over uncommitted changes both fall back to the branch, which is also the default sitting in hugo.toml. Two small things the template has to be careful about. Hugo's path.Join cleans its result, which would turn https:// into https:/, so only the in-repo path is joined with it and the URL is printf'd. And pages Hugo generates without a content file - the home page, the post list - have no .File at all, so the link is wrapped in a `with` and those pages simply do not get one. No hx-boost="false" needed, unlike the RSS and language links next to it: htmx only boosts same-origin anchors and the forge is another host.
a823897 parent: f158010 modified
blog/hugo.toml +12 -0 | @@ -84,6 +84,18 @@ weight = 5 | ||
| 84 | 84 | # flip to false (or delete the line) once the site is ready to be found. |
| 85 | 85 | noindex = true |
| 86 | 86 | |
| 87 | +# Footer "Improve this page" link: <repoURL>/blob/<repoCommit>/<repoContentPath>/<the | |
| 88 | +# page's own source file>. The forge serves a file at a given revision under | |
| 89 | +# /blob/<ref>/<path>, and .File.Path is relative to blog/content/, which is what | |
| 90 | +# repoContentPath prepends. | |
| 91 | +repoURL = "https://rickub.com/mykiwi/mykiwi.blog" | |
| 92 | +repoContentPath = "blog/content" | |
| 93 | +# Overridden by the Nix build (HUGO_PARAMS_REPOCOMMIT, see flake.nix) with the | |
| 94 | +# revision the site was built from, so a deployed page links to the source it was | |
| 95 | +# actually rendered from rather than to wherever main has moved since. `hugo | |
| 96 | +# server` and builds from a dirty tree have no revision and keep the branch below. | |
| 97 | +repoCommit = "main" | |
| 98 | + | |
| 87 | 99 | [params.profile] |
| 88 | 100 | # Drop a square photo at blog/assets/images/avatar.jpg (or change this path) |
| 89 | 101 | # to have it show up; the colophon quietly skips itself if the file is missing. |
| @@ -84,6 +84,18 @@ weight = 5 | |||
| 84 | # flip to false (or delete the line) once the site is ready to be found. | 84 | # flip to false (or delete the line) once the site is ready to be found. |
| 85 | noindex = true | 85 | noindex = true |
| 86 | 86 | ||
| 87 | +# Footer "Improve this page" link: <repoURL>/blob/<repoCommit>/<repoContentPath>/<the | ||
| 88 | +# page's own source file>. The forge serves a file at a given revision under | ||
| 89 | +# /blob/<ref>/<path>, and .File.Path is relative to blog/content/, which is what | ||
| 90 | +# repoContentPath prepends. | ||
| 91 | +repoURL = "https://rickub.com/mykiwi/mykiwi.blog" | ||
| 92 | +repoContentPath = "blog/content" | ||
| 93 | +# Overridden by the Nix build (HUGO_PARAMS_REPOCOMMIT, see flake.nix) with the | ||
| 94 | +# revision the site was built from, so a deployed page links to the source it was | ||
| 95 | +# actually rendered from rather than to wherever main has moved since. `hugo | ||
| 96 | +# server` and builds from a dirty tree have no revision and keep the branch below. | ||
| 97 | +repoCommit = "main" | ||
| 98 | + | ||
| 87 | [params.profile] | 99 | [params.profile] |
| 88 | # Drop a square photo at blog/assets/images/avatar.jpg (or change this path) | 100 | # Drop a square photo at blog/assets/images/avatar.jpg (or change this path) |
| 89 | # to have it show up; the colophon quietly skips itself if the file is missing. | 101 | # to have it show up; the colophon quietly skips itself if the file is missing. |
modified
blog/i18n/en.toml +3 -0 | @@ -11,6 +11,9 @@ other = "{{ .Count }} min read" | ||
| 11 | 11 | one = "{{ .Count }} post" |
| 12 | 12 | other = "{{ .Count }} posts" |
| 13 | 13 | |
| 14 | +[improvePage] | |
| 15 | +other = "Improve this page" | |
| 16 | + | |
| 14 | 17 | [licenseText] |
| 15 | 18 | other = "Content is" |
| 16 | 19 | |
| @@ -11,6 +11,9 @@ other = "{{ .Count }} min read" | |||
| 11 | one = "{{ .Count }} post" | 11 | one = "{{ .Count }} post" |
| 12 | other = "{{ .Count }} posts" | 12 | other = "{{ .Count }} posts" |
| 13 | 13 | ||
| 14 | +[improvePage] | ||
| 15 | +other = "Improve this page" | ||
| 16 | + | ||
| 14 | [licenseText] | 17 | [licenseText] |
| 15 | other = "Content is" | 18 | other = "Content is" |
| 16 | 19 | ||
modified
blog/i18n/es.toml +3 -0 | @@ -11,6 +11,9 @@ other = "{{ .Count }} min de lectura" | ||
| 11 | 11 | one = "{{ .Count }} artículo" |
| 12 | 12 | other = "{{ .Count }} artículos" |
| 13 | 13 | |
| 14 | +[improvePage] | |
| 15 | +other = "Mejorar esta página" | |
| 16 | + | |
| 14 | 17 | [licenseText] |
| 15 | 18 | other = "Contenido bajo licencia" |
| 16 | 19 | |
| @@ -11,6 +11,9 @@ other = "{{ .Count }} min de lectura" | |||
| 11 | one = "{{ .Count }} artículo" | 11 | one = "{{ .Count }} artículo" |
| 12 | other = "{{ .Count }} artículos" | 12 | other = "{{ .Count }} artículos" |
| 13 | 13 | ||
| 14 | +[improvePage] | ||
| 15 | +other = "Mejorar esta página" | ||
| 16 | + | ||
| 14 | [licenseText] | 17 | [licenseText] |
| 15 | other = "Contenido bajo licencia" | 18 | other = "Contenido bajo licencia" |
| 16 | 19 | ||
modified
blog/i18n/fr.toml +3 -0 | @@ -11,6 +11,9 @@ other = "{{ .Count }} min de lecture" | ||
| 11 | 11 | one = "{{ .Count }} article" |
| 12 | 12 | other = "{{ .Count }} articles" |
| 13 | 13 | |
| 14 | +[improvePage] | |
| 15 | +other = "Améliorer cette page" | |
| 16 | + | |
| 14 | 17 | [licenseText] |
| 15 | 18 | other = "Contenu sous licence" |
| 16 | 19 | |
| @@ -11,6 +11,9 @@ other = "{{ .Count }} min de lecture" | |||
| 11 | one = "{{ .Count }} article" | 11 | one = "{{ .Count }} article" |
| 12 | other = "{{ .Count }} articles" | 12 | other = "{{ .Count }} articles" |
| 13 | 13 | ||
| 14 | +[improvePage] | ||
| 15 | +other = "Améliorer cette page" | ||
| 16 | + | ||
| 14 | [licenseText] | 17 | [licenseText] |
| 15 | other = "Contenu sous licence" | 18 | other = "Contenu sous licence" |
| 16 | 19 | ||
modified
blog/i18n/ja.toml +3 -0 | @@ -10,6 +10,9 @@ other = "{{ .Count }}分で読了" | ||
| 10 | 10 | [postsCount] |
| 11 | 11 | other = "{{ .Count }}件の投稿" |
| 12 | 12 | |
| 13 | +[improvePage] | |
| 14 | +other = "このページを改善する" | |
| 15 | + | |
| 13 | 16 | [licenseText] |
| 14 | 17 | other = "コンテンツは" |
| 15 | 18 | |
| @@ -10,6 +10,9 @@ other = "{{ .Count }}分で読了" | |||
| 10 | [postsCount] | 10 | [postsCount] |
| 11 | other = "{{ .Count }}件の投稿" | 11 | other = "{{ .Count }}件の投稿" |
| 12 | 12 | ||
| 13 | +[improvePage] | ||
| 14 | +other = "このページを改善する" | ||
| 15 | + | ||
| 13 | [licenseText] | 16 | [licenseText] |
| 14 | other = "コンテンツは" | 17 | other = "コンテンツは" |
| 15 | 18 | ||
modified
blog/i18n/pt.toml +3 -0 | @@ -11,6 +11,9 @@ other = "{{ .Count }} min de leitura" | ||
| 11 | 11 | one = "{{ .Count }} post" |
| 12 | 12 | other = "{{ .Count }} posts" |
| 13 | 13 | |
| 14 | +[improvePage] | |
| 15 | +other = "Melhorar esta página" | |
| 16 | + | |
| 14 | 17 | [licenseText] |
| 15 | 18 | other = "Conteúdo sob licença" |
| 16 | 19 | |
| @@ -11,6 +11,9 @@ other = "{{ .Count }} min de leitura" | |||
| 11 | one = "{{ .Count }} post" | 11 | one = "{{ .Count }} post" |
| 12 | other = "{{ .Count }} posts" | 12 | other = "{{ .Count }} posts" |
| 13 | 13 | ||
| 14 | +[improvePage] | ||
| 15 | +other = "Melhorar esta página" | ||
| 16 | + | ||
| 14 | [licenseText] | 17 | [licenseText] |
| 15 | other = "Conteúdo sob licença" | 18 | other = "Conteúdo sob licença" |
| 16 | 19 | ||
modified
blog/layouts/partials/site-foot.html +13 -0 | @@ -1,4 +1,17 @@ | ||
| 1 | 1 | <footer class="site-foot column"> |
| 2 | + {{- /* | |
| 3 | + Source link for the page you are reading, pointing at the very revision the | |
| 4 | + site was built from (site.Params.repoCommit, see hugo.toml). Pages Hugo | |
| 5 | + generates without a content file - the home page, the post list - have no | |
| 6 | + .File, and get no link. | |
| 7 | + */}} | |
| 8 | + {{- with .File }} | |
| 9 | + {{- $source := path.Join site.Params.repoContentPath .Path }} | |
| 10 | + <span> | |
| 11 | + <a href="{{ printf "%s/blob/%s/%s" site.Params.repoURL site.Params.repoCommit $source }}">{{ i18n "improvePage" }}</a> | |
| 12 | + </span> | |
| 13 | + <span class="sep" aria-hidden="true">·</span> | |
| 14 | + {{- end }} | |
| 2 | 15 | <span>{{ i18n "licenseText" }} <a href="{{ i18n "licenseURL" }}">CC BY-SA</a></span> |
| 3 | 16 | <span class="sep" aria-hidden="true">·</span> |
| 4 | 17 | <span> |
| @@ -1,4 +1,17 @@ | |||
| 1 | <footer class="site-foot column"> | 1 | <footer class="site-foot column"> |
| 2 | + {{- /* | ||
| 3 | + Source link for the page you are reading, pointing at the very revision the | ||
| 4 | + site was built from (site.Params.repoCommit, see hugo.toml). Pages Hugo | ||
| 5 | + generates without a content file - the home page, the post list - have no | ||
| 6 | + .File, and get no link. | ||
| 7 | + */}} | ||
| 8 | + {{- with .File }} | ||
| 9 | + {{- $source := path.Join site.Params.repoContentPath .Path }} | ||
| 10 | + <span> | ||
| 11 | + <a href="{{ printf "%s/blob/%s/%s" site.Params.repoURL site.Params.repoCommit $source }}">{{ i18n "improvePage" }}</a> | ||
| 12 | + </span> | ||
| 13 | + <span class="sep" aria-hidden="true">·</span> | ||
| 14 | + {{- end }} | ||
| 2 | <span>{{ i18n "licenseText" }} <a href="{{ i18n "licenseURL" }}">CC BY-SA</a></span> | 15 | <span>{{ i18n "licenseText" }} <a href="{{ i18n "licenseURL" }}">CC BY-SA</a></span> |
| 3 | <span class="sep" aria-hidden="true">·</span> | 16 | <span class="sep" aria-hidden="true">·</span> |
| 4 | <span> | 17 | <span> |
modified
flake.nix +6 -1 | @@ -19,7 +19,12 @@ | ||
| 19 | 19 | src = ./blog; |
| 20 | 20 | nativeBuildInputs = [ hugo dart-sass ]; |
| 21 | 21 | buildPhase = '' |
| 22 | - HUGO_PARAMS_NIXSTOREPATH="$out" hugo --minify --destination "$out" | |
| 22 | + # self.rev is the revision this flake was built from - it only exists | |
| 23 | + # when the tree is clean, so a local `nix build` with uncommitted | |
| 24 | + # changes falls back to the branch, same as `hugo server` does. | |
| 25 | + HUGO_PARAMS_NIXSTOREPATH="$out" \ | |
| 26 | + HUGO_PARAMS_REPOCOMMIT="${self.rev or "main"}" \ | |
| 27 | + hugo --minify --destination "$out" | |
| 23 | 28 | ''; |
| 24 | 29 | dontInstall = true; |
| 25 | 30 | }; |
| @@ -19,7 +19,12 @@ | |||
| 19 | src = ./blog; | 19 | src = ./blog; |
| 20 | nativeBuildInputs = [ hugo dart-sass ]; | 20 | nativeBuildInputs = [ hugo dart-sass ]; |
| 21 | buildPhase = '' | 21 | buildPhase = '' |
| 22 | - HUGO_PARAMS_NIXSTOREPATH="$out" hugo --minify --destination "$out" | 22 | + # self.rev is the revision this flake was built from - it only exists |
| 23 | + # when the tree is clean, so a local `nix build` with uncommitted | ||
| 24 | + # changes falls back to the branch, same as `hugo server` does. | ||
| 25 | + HUGO_PARAMS_NIXSTOREPATH="$out" \ | ||
| 26 | + HUGO_PARAMS_REPOCOMMIT="${self.rev or "main"}" \ | ||
| 27 | + hugo --minify --destination "$out" | ||
| 23 | ''; | 28 | ''; |
| 24 | dontInstall = true; | 29 | dontInstall = true; |
| 25 | }; | 30 | }; |