Commit 9df917e73a

9df917e73a67d15adecc3f45976690f6fcd4e47a

parent: 145dec111d

Verified · cmc ci/build: success ci/test: success

cmc <hello@cleberg.net> · 2026-09-28 22:34 UTC

wiki: a link to an existing non-page file resolves to _raw, not the page route

Closes #283

Layout: unified · split

CHANGELOG.org +4
@@ -6,6 +6,10 @@ anything beyond "replace the binary and restart" is needed.
6 6
7* Unreleased 7* Unreleased
8 8
9- A wiki link to an existing non-page file (an .svg, .txt, .pdf) now
10 resolves to the raw route instead of 404ing against the page route
11 (#283).
12
9- The new-issue form takes labels, milestone and assignee in one step 13- The new-issue form takes labels, milestone and assignee in one step
10 for writers; the watch button names watching, muted and default; a 14 for writers; the watch button names watching, muted and default; a
11 Discussion heading sits before comment threads; the build page's 15 Discussion heading sits before comment threads; the build page's
internal/httpd/wiki.go +9 −1
@@ -248,8 +248,16 @@ func rewriteWikiLinks(rendered template.HTML, p repoPage, page string, isPage, i
248 } 248 }
249 continue 249 continue
250 } 250 }
251 // A plain link is usually to another page, but a link to
252 // an existing non-page file (an .svg, .txt, .pdf) must
253 // go to _raw the same as an image src, or it 404s
254 // against the page route (#283).
251 if target, ok := wikiResolve(page, trimPageExt(v), isPage); ok { 255 if target, ok := wikiResolve(page, trimPageExt(v), isPage); ok {
252 n.Attr[i].Val = base + "/" + target 256 if raw, rok := wikiResolve(page, v, isFile); rok && isFile(raw) && !isPage(target) {
257 n.Attr[i].Val = base + "/_raw/" + raw
258 } else {
259 n.Attr[i].Val = base + "/" + target
260 }
253 } 261 }
254 } 262 }
255 } 263 }
internal/httpd/wiki_test.go +2
@@ -41,12 +41,14 @@ func TestRewriteWikiLinksInSubfolder(t *testing.T) {
41 pages := map[string]bool{"Admin": true, "Architecture/Identity": true} 41 pages := map[string]bool{"Admin": true, "Architecture/Identity": true}
42 files := map[string]bool{"diagrams/a.svg": true, "Architecture/b.svg": true} 42 files := map[string]bool{"diagrams/a.svg": true, "Architecture/b.svg": true}
43 in := template.HTML(`<a href="Identity.org">i</a><a href="Admin.org">a</a>` + 43 in := template.HTML(`<a href="Identity.org">i</a><a href="Admin.org">a</a>` +
44 `<a href="b.svg">diagram</a>` +
44 `<img src="b.svg"><img src="diagrams/a.svg"><a href="https://x.test/">x</a>`) 45 `<img src="b.svg"><img src="diagrams/a.svg"><a href="https://x.test/">x</a>`)
45 out := string(rewriteWikiLinks(in, p, "Architecture/Trust", 46 out := string(rewriteWikiLinks(in, p, "Architecture/Trust",
46 func(s string) bool { return pages[s] }, func(s string) bool { return files[s] })) 47 func(s string) bool { return pages[s] }, func(s string) bool { return files[s] }))
47 for _, want := range []string{ 48 for _, want := range []string{
48 `href="/krz/gitbay/wiki/Architecture/Identity"`, 49 `href="/krz/gitbay/wiki/Architecture/Identity"`,
49 `href="/krz/gitbay/wiki/Admin"`, 50 `href="/krz/gitbay/wiki/Admin"`,
51 `href="/krz/gitbay/wiki/_raw/Architecture/b.svg"`,
50 `src="/krz/gitbay/wiki/_raw/Architecture/b.svg"`, 52 `src="/krz/gitbay/wiki/_raw/Architecture/b.svg"`,
51 `src="/krz/gitbay/wiki/_raw/diagrams/a.svg"`, 53 `src="/krz/gitbay/wiki/_raw/diagrams/a.svg"`,
52 `href="https://x.test/"`, 54 `href="https://x.test/"`,