/
markdown.go
132 lines (114 loc) · 2.76 KB
/
markdown.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package uos
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/ast"
"github.com/gomarkdown/markdown/html"
)
// MarkdownHandler returns a handler for the "/markdown/" route providing HTML for the documents
// in the configured markdown directory.
// The handler can be activated using RegisterAppRequestHandlers.
func MarkdownHandler() AppRequestHandlerMapping {
return AppRequestHandlerMapping{
Route: "/markdown/",
Handler: markdownWebHandler,
Options: AppRequestHandlerOptions{
NoSitemap: true,
},
}
}
func markdownWebHandler(w http.ResponseWriter, r *http.Request) {
// determine markdown element name
name := getElementName("markdown", r.URL.Path)
if name == "" {
RespondNotFound(w)
return
}
if status := markdownHandler(w, r, name); status != http.StatusOK {
respondWithStatusText(w, status)
}
}
func markdownHandler(w io.Writer, r *http.Request, name string) int {
mdFilePath := filepath.Join(Config.Assets.Markdown, name)
// append ".md" file extension (of not already present)
if !strings.HasSuffix(mdFilePath, ".md") {
mdFilePath = mdFilePath + ".md"
}
// markdown file not found?
info, err := os.Stat(mdFilePath)
if err != nil {
if os.IsNotExist(err) {
Log.WarnContextR(
r, "file not found",
LogContext{"file": mdFilePath},
)
return http.StatusNotFound
}
Log.ErrorContextR(
r, "could not os.Stat markdown file",
LogContext{
"file": mdFilePath,
"error": err,
},
)
return http.StatusInternalServerError
}
// requestes a directory? (trailing slash)
if info.IsDir() {
return http.StatusNotFound
}
// read markdown file
md, err := ReadFile(mdFilePath)
if err != nil {
Log.ErrorContextR(
r, "could not read markdown file",
LogContext{
"file": mdFilePath,
"error": err,
},
)
return http.StatusInternalServerError
}
var (
opts = html.RendererOptions{
Flags: html.FlagsNone,
RenderNodeHook: renderHook,
}
renderer = html.NewRenderer(opts)
)
// render document and wrap in a content-div (Bulma CSS)
result := fmt.Sprintf(
`<div class="content">%s</div>`,
string(markdown.ToHTML(md, nil, renderer)),
)
w.Write([]byte(result))
return http.StatusOK
}
func renderHook(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
switch astObj := node.(type) {
case *ast.Heading:
// customized rendering (Bulma CSS)
level := astObj.Level
if entering {
switch level {
case 1:
w.Write([]byte(`<h1 class="title">`))
default:
w.Write([]byte(
fmt.Sprintf(`<h%d class="subtitle is-%d">`, level, level+2),
))
}
} else {
w.Write([]byte(
fmt.Sprintf("</h%d>", level),
))
}
return ast.GoToNext, true
}
return ast.GoToNext, false
}