Commit d7ae9fb4 authored by Matthew Holt's avatar Matthew Holt

Added markdown middleware

parent fb785924
...@@ -105,10 +105,10 @@ func (d *dispenser) Val() string { ...@@ -105,10 +105,10 @@ func (d *dispenser) Val() string {
} }
} }
// ArgErr generates an argument error, meaning that another // ArgErr returns an argument error, meaning that another
// argument was expected but not found. The error is saved // argument was expected but not found. In other words,
// within the dispenser, but this function returns nil for // a line break or open curly brace was encountered instead of
// convenience in practice. // an argument.
func (d *dispenser) ArgErr() error { func (d *dispenser) ArgErr() error {
if d.Val() == "{" { if d.Val() == "{" {
return d.Err("Unexpected token '{', expecting argument") return d.Err("Unexpected token '{', expecting argument")
...@@ -117,12 +117,6 @@ func (d *dispenser) ArgErr() error { ...@@ -117,12 +117,6 @@ func (d *dispenser) ArgErr() error {
} }
// Err generates a custom parse error with a message of msg. // Err generates a custom parse error with a message of msg.
// TODO: Update the docs of all these Err methods!
// This function returns nil for convenience, but loads the
// error into the dispenser so it can be reported. The caller
// of the middleware preparator is responsible for checking
// the error in the dispenser after the middleware preparator
// is finished.
func (d *dispenser) Err(msg string) error { func (d *dispenser) Err(msg string) error {
msg = fmt.Sprintf("%s:%d - Parse error: %s", d.filename, d.tokens[d.cursor].line, msg) msg = fmt.Sprintf("%s:%d - Parse error: %s", d.filename, d.tokens[d.cursor].line, msg)
return errors.New(msg) return errors.New(msg)
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/mholt/caddy/middleware/gzip" "github.com/mholt/caddy/middleware/gzip"
"github.com/mholt/caddy/middleware/headers" "github.com/mholt/caddy/middleware/headers"
"github.com/mholt/caddy/middleware/log" "github.com/mholt/caddy/middleware/log"
"github.com/mholt/caddy/middleware/markdown"
"github.com/mholt/caddy/middleware/proxy" "github.com/mholt/caddy/middleware/proxy"
"github.com/mholt/caddy/middleware/redirect" "github.com/mholt/caddy/middleware/redirect"
"github.com/mholt/caddy/middleware/rewrite" "github.com/mholt/caddy/middleware/rewrite"
...@@ -26,6 +27,7 @@ func init() { ...@@ -26,6 +27,7 @@ func init() {
register("proxy", proxy.New) register("proxy", proxy.New)
register("fastcgi", fastcgi.New) register("fastcgi", fastcgi.New)
register("websocket", websockets.New) register("websocket", websockets.New)
register("markdown", markdown.New)
} }
var ( var (
......
// Package markdown is middleware to render markdown files as HTML
// on-the-fly.
package markdown
import (
"io/ioutil"
"net/http"
"path"
"strings"
"github.com/mholt/caddy/middleware"
"github.com/russross/blackfriday"
)
// New creates a new instance of Markdown middleware that
// renders markdown to HTML on-the-fly.
func New(c middleware.Controller) (middleware.Middleware, error) {
md, err := parse(c)
if err != nil {
return nil, err
}
md.Root = c.Root()
md.Renderer = blackfriday.HtmlRenderer(0, "", "")
return func(next http.HandlerFunc) http.HandlerFunc {
md.Next = next
return md.ServeHTTP
}, nil
}
// Markdown stores the configuration necessary to serve the Markdown middleware.
type Markdown struct {
// Server root
Root string
// Next HTTP handler in the chain
Next http.HandlerFunc
// Markdown renderer
Renderer blackfriday.Renderer
// Base path to match
PathScope string
// List of extensions to consider as markdown files
Extensions []string
// List of style sheets to load for each markdown file
Styles []string
// List of JavaScript files to load for each markdown file
Scripts []string
}
// ServeHTTP implements the http.Handler interface.
func (m Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if middleware.Path(r.URL.Path).Matches(m.PathScope) {
for _, ext := range m.Extensions {
if strings.HasSuffix(r.URL.Path, ext) {
fpath := m.Root + r.URL.Path
body, err := ioutil.ReadFile(fpath)
if err != nil {
panic(err) // TODO
}
content := blackfriday.Markdown(body, m.Renderer, 0)
var scripts, styles string
for _, style := range m.Styles {
styles += strings.Replace(cssTemplate, "{{url}}", style, 1) + "\r\n"
}
for _, script := range m.Scripts {
scripts += strings.Replace(jsTemplate, "{{url}}", script, 1) + "\r\n"
}
html := htmlTemplate
html = strings.Replace(html, "{{title}}", path.Base(fpath), 1)
html = strings.Replace(html, "{{css}}", styles, 1)
html = strings.Replace(html, "{{js}}", scripts, 1)
html = strings.Replace(html, "{{body}}", string(content), 1)
w.Write([]byte(html))
return
}
}
}
m.Next(w, r)
}
// parse fills up a new instance of Markdown middleware.
func parse(c middleware.Controller) (Markdown, error) {
var md Markdown
for c.Next() {
// Get the path scope
if !c.NextArg() {
return md, c.ArgErr()
}
md.PathScope = c.Val()
// Load any other configuration parameters
for c.NextBlock() {
switch c.Val() {
case "ext":
exts := c.RemainingArgs()
if len(exts) == 0 {
return md, c.ArgErr()
}
md.Extensions = append(md.Extensions, exts...)
case "css":
if !c.NextArg() {
return md, c.ArgErr()
}
md.Styles = append(md.Styles, c.Val())
case "js":
if !c.NextArg() {
return md, c.ArgErr()
}
md.Scripts = append(md.Scripts, c.Val())
default:
return md, c.Err("Expected valid markdown configuration property")
}
}
}
return md, nil
}
const (
htmlTemplate = `<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<meta charset="utf-8">
{{css}}
{{js}}
</head>
<body>
{{body}}
</body>
</html>`
cssTemplate = `<link rel="stylesheet" href="{{url}}">`
jsTemplate = `<script src="{{url}}"></script>`
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment