markdown: sitegen keyword and run generation at startup

Also fixed bug for markdown files that don't contain front matter
This commit is contained in:
Matthew Holt 2015-05-09 21:12:28 -06:00
parent a96c4d707b
commit 4272536518
3 changed files with 110 additions and 40 deletions

View File

@ -1,9 +1,12 @@
package setup
import (
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/markdown"
@ -24,6 +27,57 @@ func Markdown(c *Controller) (middleware.Middleware, error) {
IndexFiles: []string{"index.md"},
}
// For any configs that enabled static site gen, sweep the whole path at startup
c.Startup = append(c.Startup, func() error {
for _, cfg := range mdconfigs {
if cfg.StaticDir == "" {
continue
}
// If generated site already exists, clear it out
_, err := os.Stat(cfg.StaticDir)
if err == nil {
err := os.RemoveAll(cfg.StaticDir)
if err != nil {
return err
}
}
fp := filepath.Join(md.Root, cfg.PathScope)
filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
for _, ext := range cfg.Extensions {
if !info.IsDir() && strings.HasSuffix(info.Name(), ext) {
// Load the file
body, err := ioutil.ReadFile(path)
if err != nil {
return err
}
// Get the relative path as if it were a HTTP request,
// then prepend with "/" (like a real HTTP request)
reqPath, err := filepath.Rel(md.Root, path)
if err != nil {
return err
}
reqPath = "/" + reqPath
// Generate the static file
_, err = md.Process(cfg, reqPath, body)
if err != nil {
return err
}
break // don't try other file extensions
}
}
return nil
})
}
return nil
})
return func(next middleware.Handler) middleware.Handler {
md.Next = next
return md
@ -38,7 +92,6 @@ func markdownParse(c *Controller) ([]markdown.Config, error) {
Renderer: blackfriday.HtmlRenderer(0, "", ""),
Templates: make(map[string]string),
StaticFiles: make(map[string]string),
StaticDir: path.Join(c.Root, markdown.StaticDir),
}
// Get the path scope
@ -83,6 +136,16 @@ func markdownParse(c *Controller) ([]markdown.Config, error) {
default:
return mdconfigs, c.ArgErr()
}
case "sitegen":
if c.NextArg() {
md.StaticDir = path.Join(c.Root, c.Val())
} else {
md.StaticDir = path.Join(c.Root, markdown.DefaultStaticDir)
}
if c.NextArg() {
// only 1 argument allowed
return mdconfigs, c.ArgErr()
}
default:
return mdconfigs, c.Err("Expected valid markdown configuration property")
}

View File

@ -105,6 +105,11 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
if html, err := ioutil.ReadFile(filepath); err == nil {
w.Write(html)
return http.StatusOK, nil
} else {
if os.IsPermission(err) {
return http.StatusForbidden, err
}
return http.StatusNotFound, nil
}
}
}
@ -115,7 +120,7 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
return http.StatusInternalServerError, err
}
html, err := md.process(m, fpath, body)
html, err := md.Process(m, fpath, body)
if err != nil {
return http.StatusInternalServerError, err
}

View File

@ -2,7 +2,6 @@ package markdown
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
@ -14,13 +13,13 @@ import (
)
const (
DefaultTemplate = "defaultTemplate"
StaticDir = ".caddy_static"
DefaultTemplate = "defaultTemplate"
DefaultStaticDir = "generated_site"
)
// process processes the contents of a page.
// It parses the metadata (if any) and uses the template (if found)
func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, error) {
// Process processes the contents of a page in b. It parses the metadata
// (if any) and uses the template (if found).
func (md Markdown) Process(c Config, requestPath string, b []byte) ([]byte, error) {
var metadata = Metadata{Variables: make(map[string]interface{})}
var markdown []byte
var err error
@ -28,8 +27,11 @@ func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, erro
// find parser compatible with page contents
parser := findParser(b)
// if found, assume metadata present and parse.
if parser != nil {
if parser == nil {
// if not found, assume whole file is markdown (no front matter)
markdown = b
} else {
// if found, assume metadata present and parse.
markdown, err = parser.Parse(b)
if err != nil {
return nil, err
@ -74,7 +76,7 @@ func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, me
}
// process the template
b := &bytes.Buffer{}
b := new(bytes.Buffer)
t, err := template.New("").Parse(string(tmpl))
if err != nil {
return nil, err
@ -87,6 +89,7 @@ func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, me
if err = md.generatePage(c, requestPath, b.Bytes()); err != nil {
// if static page generation fails,
// nothing fatal, only log the error.
// TODO: Report this non-fatal error, but don't log it here
log.Println(err)
}
@ -94,42 +97,41 @@ func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, me
}
// generatePage generates a static html page from the markdown in content.
// generatePage generates a static html page from the markdown in content if c.StaticDir
// is a non-empty value, meaning that the user enabled static site generation.
func (md Markdown) generatePage(c Config, requestPath string, content []byte) error {
// should not happen,
// must be set on Markdown init.
if c.StaticDir == "" {
return fmt.Errorf("Static directory not set")
}
// Only generate the page if static site generation is enabled
if c.StaticDir != "" {
// if static directory is not existing, create it
if _, err := os.Stat(c.StaticDir); err != nil {
err := os.MkdirAll(c.StaticDir, os.FileMode(0755))
if err != nil {
return err
}
}
// if static directory is not existing, create it
if _, err := os.Stat(c.StaticDir); err != nil {
err := os.MkdirAll(c.StaticDir, os.FileMode(0755))
filePath := filepath.Join(c.StaticDir, requestPath)
// If it is index file, use the directory instead
if md.IsIndexFile(filepath.Base(requestPath)) {
filePath, _ = filepath.Split(filePath)
}
// Create the directory in case it is not existing
if err := os.MkdirAll(filePath, os.FileMode(0744)); err != nil {
return err
}
// generate index.html file in the directory
filePath = filepath.Join(filePath, "index.html")
err := ioutil.WriteFile(filePath, content, os.FileMode(0664))
if err != nil {
return err
}
c.StaticFiles[requestPath] = filePath
}
filePath := filepath.Join(c.StaticDir, requestPath)
// If it is index file, use the directory instead
if md.IsIndexFile(filepath.Base(requestPath)) {
filePath, _ = filepath.Split(filePath)
}
// Create the directory in case it is not existing
if err := os.MkdirAll(filePath, os.FileMode(0755)); err != nil {
return err
}
// generate index.html file in the directory
filePath = filepath.Join(filePath, "index.html")
err := ioutil.WriteFile(filePath, content, os.FileMode(0755))
if err != nil {
return err
}
c.StaticFiles[requestPath] = filePath
return nil
}