blob: 06e2c6e264628e125300134b58133d3511569b3b [file] [log] [blame]
package main
import (
"bytes"
"html/template"
"net/url"
"strings"
"code.hackerspace.pl/hscloud/devtools/hackdoc/config"
"github.com/gabriel-vasile/mimetype"
"github.com/golang/glog"
"gopkg.in/russross/blackfriday.v2"
)
// renderMarkdown renders markdown to HTML, replacing all relative (intra-hackdoc) links with version that have ref set.
func renderMarkdown(input []byte, ref string) []byte {
r := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
Flags: blackfriday.CommonHTMLFlags | blackfriday.TOC,
})
parser := blackfriday.New(blackfriday.WithRenderer(r), blackfriday.WithExtensions(blackfriday.CommonExtensions))
ast := parser.Parse(input)
var buf bytes.Buffer
buf.Write([]byte(`<div class="toc"><h1>Page Contents</h1>`))
r.RenderHeader(&buf, ast)
buf.Write([]byte(`</div><div class="content">`))
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
if ref != "" && entering && node.Type == blackfriday.Link {
dest := string(node.Destination)
u, err := url.Parse(dest)
if err == nil && !u.IsAbs() {
q := u.Query()
q["ref"] = []string{ref}
u.RawQuery = q.Encode()
node.Destination = []byte(u.String())
glog.Infof("link fix %q -> %q", dest, u.String())
}
}
return r.RenderNode(&buf, node, entering)
})
buf.Write([]byte(`</div>`))
r.RenderFooter(&buf, ast)
return buf.Bytes()
}
type pathPart struct {
Label string
Path string
}
func (r *request) handleFile(path string, cfg *config.Config) {
data, err := r.source.ReadFile(r.ctx, path)
if err != nil {
glog.Errorf("ReadFile(%q): %w", err)
r.handle500()
return
}
// TODO(q3k): do MIME detection instead.
if strings.HasSuffix(path, ".md") {
rendered := renderMarkdown([]byte(data), r.ref)
r.logRequest("serving markdown at %s, cfg %+v", path, cfg)
// TODO(q3k): allow markdown files to override which template to load
tmpl, ok := cfg.Templates["default"]
if !ok {
glog.Errorf("No default template found for %s", path)
// TODO(q3k): implement fallback template
r.w.Write(rendered)
return
}
pathInDepot := strings.TrimPrefix(path, "//")
pathParts := []pathPart{
{Label: "//", Path: "/"},
}
parts := strings.Split(pathInDepot, "/")
fullPath := ""
for i, p := range parts {
label := p
if i != len(parts)-1 {
label = label + "/"
}
fullPath += "/" + p
pathParts = append(pathParts, pathPart{Label: label, Path: fullPath})
}
vars := map[string]interface{}{
"Rendered": template.HTML(rendered),
"Title": path,
"Path": path,
"PathInDepot": pathInDepot,
"PathParts": pathParts,
"HackdocURL": flagHackdocURL,
"WebLinks": r.source.WebLinks(pathInDepot),
}
err = tmpl.Execute(r.w, vars)
if err != nil {
glog.Errorf("Could not execute template for %s: %v", err)
}
return
}
// Just serve the file.
mime := mimetype.Detect(data)
r.w.Header().Set("Content-Type", mime.String())
r.w.Write(data)
}