internal/httpd/readme.go
72 lines · 2139 bytes
1 symbol in this file
1package httpd
2
3import (
4 "html/template"
5 "path"
6 "strings"
7
8 "golang.org/x/net/html"
9 "golang.org/x/net/html/atom"
10
11 "gitbay.org/gitbay/internal/gitutil"
12)
13
14// rewriteRelativeLinks makes relative hrefs and srcs in rendered repo
15// content resolve on the forge: links go to blob pages, images to raw.
16// go-org exports .org links as .html, so an .html target whose .org (or
17// .md) source exists in the tree maps back to the source file.
18func rewriteRelativeLinks(rendered template.HTML, p repoPage, baseDir string) template.HTML {
19 ctx := &html.Node{Type: html.ElementNode, Data: "div", DataAtom: atom.Div}
20 nodes, err := html.ParseFragment(strings.NewReader(string(rendered)), ctx)
21 if err != nil {
22 return rendered
23 }
24 var walk func(*html.Node)
25 walk = func(n *html.Node) {
26 if n.Type == html.ElementNode {
27 for i, a := range n.Attr {
28 isHref := a.Key == "href" && n.Data == "a"
29 isSrc := a.Key == "src" && (n.Data == "img" || n.Data == "video" || n.Data == "source")
30 if !isHref && !isSrc {
31 continue
32 }
33 v := a.Val
34 if v == "" || strings.Contains(v, "://") || strings.HasPrefix(v, "/") ||
35 strings.HasPrefix(v, "#") || strings.HasPrefix(v, "mailto:") ||
36 strings.HasPrefix(v, "data:") {
37 continue
38 }
39 target := path.Clean(path.Join(baseDir, v))
40 if strings.HasPrefix(target, "..") {
41 continue
42 }
43 if isSrc {
44 n.Attr[i].Val = "/" + p.Repo.Path() + "/raw/" + p.Ref + "/" + target
45 continue
46 }
47 // .html from org/markdown exports maps back to the source.
48 if strings.HasSuffix(target, ".html") {
49 stem := strings.TrimSuffix(target, ".html")
50 for _, ext := range []string{".org", ".md"} {
51 if _, err := gitutil.ReadBlob(p.Dir, p.Ref, stem+ext, 1); err == nil {
52 target = stem + ext
53 break
54 }
55 }
56 }
57 n.Attr[i].Val = "/" + p.Repo.Path() + "/blob/" + p.Ref + "/" + target
58 }
59 }
60 for c := n.FirstChild; c != nil; c = c.NextSibling {
61 walk(c)
62 }
63 }
64 var out strings.Builder
65 for _, n := range nodes {
66 walk(n)
67 if err := html.Render(&out, n); err != nil {
68 return rendered
69 }
70 }
71 return template.HTML(out.String())
72}