You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mdserver/context.go

161 lines
2.8 KiB
Go

package mdserver
import (
"html/template"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"sort"
"strings"
"github.com/russross/blackfriday/v2"
)
type Breadcrumb struct {
Name string
URL string
}
type DirItem struct {
Name string
URL string
IsDir bool
IsMarkdown bool
}
type Context struct {
// Full paths in filesystem.
fullPath string
IsDir bool
URL string
Breadcrumbs []Breadcrumb
Children []DirItem
MarkdownCSS template.CSS
MDContent template.HTML
}
func NewContext(root, fullPath string, fi os.FileInfo) (ctx Context) {
ctx.fullPath = filepath.Clean(fullPath)
if fi.IsDir() {
if _, err := os.Stat(filepath.Join(ctx.fullPath, "index.md")); err == nil {
ctx.fullPath = filepath.Join(ctx.fullPath, "index.md")
} else {
ctx.IsDir = true
}
}
ctx.URL, _ = filepath.Rel(root, ctx.fullPath)
ctx.URL = strings.TrimPrefix(ctx.URL, ".")
ctx.URL = filepath.ToSlash("/" + ctx.URL)
ctx.Breadcrumbs = ctx.breadcrumbs()
ctx.Children = ctx.listDir()
ctx.MarkdownCSS = template.CSS(markdownCSS)
ctx.MDContent = ctx.renderMD()
return ctx
}
func (ctx Context) breadcrumbs() []Breadcrumb {
prev := Breadcrumb{Name: "root", URL: "/"}
bc := []Breadcrumb{}
url := ctx.URL
if len(url) > 0 && (url[0] == '/' || url[0] == '.') {
url = url[1:]
}
for len(url) > 0 {
parts := strings.SplitN(url, "/", 2)
name := parts[0]
prev = Breadcrumb{
Name: name,
URL: filepath.Join(prev.URL, name),
}
bc = append(bc, prev)
if len(parts) == 1 {
url = ""
} else {
url = parts[1]
}
}
return bc
}
func (ctx Context) listDir() (l []DirItem) {
dir := ctx.fullPath
dirURL := ctx.URL
if !ctx.IsDir {
dir = filepath.Dir(dir)
dirURL = path.Dir(dirURL)
}
dis, err := os.ReadDir(dir)
if err != nil {
log.Printf("Failed to list dir: %v", err)
return l
}
for _, di := range dis {
if strings.HasPrefix(di.Name(), ".") {
continue
}
l = append(l, DirItem{
Name: di.Name(),
URL: path.Join(dirURL, di.Name()),
IsDir: di.IsDir(),
IsMarkdown: strings.HasSuffix(di.Name(), ".md") && !di.IsDir(),
})
}
sort.Slice(l, func(i, j int) bool {
ii := l[i]
ij := l[j]
if ii.IsDir {
if ij.IsDir {
return ii.Name < ij.Name
}
return true
}
if ii.IsMarkdown {
if ij.IsDir {
return false
}
if ij.IsMarkdown {
return ii.Name < ij.Name
}
return true
}
if ij.IsDir || ij.IsMarkdown {
return false
}
return ii.Name < ij.Name
})
return l
}
func (ctx Context) renderMD() template.HTML {
if ctx.IsDir {
return ""
}
raw, err := ioutil.ReadFile(ctx.fullPath)
if err != nil {
log.Printf("Failed to read markdown file: %v", err)
return ""
}
return template.HTML(
blackfriday.Run(
raw,
blackfriday.WithRenderer(&CustomRenderer{})))
}