From 427253651823d359fc0d3d36a40b90a664d38285 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sat, 9 May 2015 21:12:28 -0600 Subject: [PATCH] markdown: sitegen keyword and run generation at startup Also fixed bug for markdown files that don't contain front matter --- config/setup/markdown.go | 65 ++++++++++++++++++++++++++- middleware/markdown/markdown.go | 7 ++- middleware/markdown/process.go | 78 +++++++++++++++++---------------- 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/config/setup/markdown.go b/config/setup/markdown.go index bfe96300b..fd92f20ad 100644 --- a/config/setup/markdown.go +++ b/config/setup/markdown.go @@ -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") } diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go index 7600780c7..f3cf75f98 100644 --- a/middleware/markdown/markdown.go +++ b/middleware/markdown/markdown.go @@ -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 } diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go index 9dc5778fb..95f7e51b3 100644 --- a/middleware/markdown/process.go +++ b/middleware/markdown/process.go @@ -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 }