diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go index d7bdcc39..7600780c 100644 --- a/middleware/markdown/markdown.go +++ b/middleware/markdown/markdown.go @@ -61,10 +61,10 @@ type Config struct { // Map of registered templates Templates map[string]string - // Static files + // Map of request URL to static files generated StaticFiles map[string]string - // Static files directory + // Directory to store static files StaticDir string } diff --git a/middleware/markdown/metadata.go b/middleware/markdown/metadata.go index bdaf6c06..38660586 100644 --- a/middleware/markdown/metadata.go +++ b/middleware/markdown/metadata.go @@ -17,12 +17,17 @@ var ( // Metadata stores a page's metadata type Metadata struct { - Title string - Template string + // Page title + Title string + + // Page template + Template string + + // Variables to be used with Template Variables map[string]interface{} } -// Load loads parsed metadata into m +// load loads parsed values in parsedMap into Metadata func (m *Metadata) load(parsedMap map[string]interface{}) { if template, ok := parsedMap["title"]; ok { m.Title, _ = template.(string) @@ -35,14 +40,19 @@ func (m *Metadata) load(parsedMap map[string]interface{}) { } } -// MetadataParser parses the page metadata -// into Metadata +// MetadataParser is a an interface that must be satisfied by each parser type MetadataParser interface { - // Identifiers + // Opening identifier Opening() []byte + + // Closing identifier Closing() []byte + // Parse the metadata Parse([]byte) error + + // Parsed metadata. + // Should be called after a call to Parse returns no error Metadata() Metadata } @@ -51,7 +61,7 @@ type JSONMetadataParser struct { metadata Metadata } -// Parse parses b into metadata +// Parse the metadata func (j *JSONMetadataParser) Parse(b []byte) error { m := make(map[string]interface{}) if err := json.Unmarshal(b, &m); err != nil { @@ -61,7 +71,8 @@ func (j *JSONMetadataParser) Parse(b []byte) error { return nil } -// Metadata returns the metadata parsed by this parser +// Parsed metadata. +// Should be called after a call to Parse returns no error func (j *JSONMetadataParser) Metadata() Metadata { return j.metadata } @@ -81,7 +92,7 @@ type TOMLMetadataParser struct { metadata Metadata } -// Parse parses b into metadata +// Parse the metadata func (t *TOMLMetadataParser) Parse(b []byte) error { m := make(map[string]interface{}) if err := toml.Unmarshal(b, &m); err != nil { @@ -91,7 +102,8 @@ func (t *TOMLMetadataParser) Parse(b []byte) error { return nil } -// Metadata returns the metadata parsed by this parser +// Parsed metadata. +// Should be called after a call to Parse returns no error func (t *TOMLMetadataParser) Metadata() Metadata { return t.metadata } @@ -106,12 +118,12 @@ func (t *TOMLMetadataParser) Closing() []byte { return []byte("+++") } -// YAMLMetadataParser is the MetdataParser for YAML +// YAMLMetadataParser is the MetadataParser for YAML type YAMLMetadataParser struct { metadata Metadata } -// Parse parses b into metadata +// Parse the metadata func (y *YAMLMetadataParser) Parse(b []byte) error { m := make(map[string]interface{}) if err := yaml.Unmarshal(b, &m); err != nil { @@ -121,7 +133,8 @@ func (y *YAMLMetadataParser) Parse(b []byte) error { return nil } -// Metadata returns the metadata parsed by this parser +// Parsed metadata. +// Should be called after a call to Parse returns no error func (y *YAMLMetadataParser) Metadata() Metadata { return y.metadata } diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go index a9fa702a..d775bf8b 100644 --- a/middleware/markdown/process.go +++ b/middleware/markdown/process.go @@ -19,9 +19,9 @@ const ( StaticDir = ".caddy_static" ) -// process the contents of a page. -// It parses the metadata if any and uses the template if found -func (md Markdown) process(c Config, fpath string, b []byte) ([]byte, error) { +// 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) { metadata, markdown, err := extractMetadata(b) if err != nil { return nil, err @@ -46,10 +46,11 @@ func (md Markdown) process(c Config, fpath string, b []byte) ([]byte, error) { // process markdown markdown = blackfriday.Markdown(markdown, c.Renderer, 0) + // set it as body for template metadata.Variables["body"] = string(markdown) - return md.processTemplate(c, fpath, tmpl, metadata) + return md.processTemplate(c, requestPath, tmpl, metadata) } // extractMetadata extracts metadata content from a page. @@ -60,12 +61,12 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { reader := bytes.NewBuffer(b) scanner := bufio.NewScanner(reader) var parser MetadataParser - // if scanner.Scan() && + // Read first line if scanner.Scan() { line := scanner.Bytes() parser = findParser(line) - // if no parser found + // if no parser found, // assume metadata not present if parser == nil { return metadata, b, nil @@ -78,7 +79,8 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { // Read remaining lines until closing identifier is found for scanner.Scan() { line := scanner.Bytes() - // closing identifier found + + // if closing identifier found if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) { // parse the metadata err := parser.Parse(buf.Bytes()) @@ -97,10 +99,12 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { buf.Write(line) buf.WriteString("\r\n") } + + // closing identifier not found return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing())) } -// findParser locates the parser for an opening identifier +// findParser finds the parser using line that contains opening identifier func findParser(line []byte) MetadataParser { line = bytes.TrimSpace(line) for _, parser := range parsers { @@ -111,13 +115,16 @@ func findParser(line []byte) MetadataParser { return nil } -func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata Metadata) ([]byte, error) { - // if template is specified - // replace parse the template - if tmpl != nil { - tmpl = defaultTemplate(c, metadata, fpath) +// processTemplate processes a template given a requestPath, +// template (tmpl) and metadata +func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, metadata Metadata) ([]byte, error) { + // if template is not specified, + // use the default template + if tmpl == nil { + tmpl = defaultTemplate(c, metadata, requestPath) } + // process the template b := &bytes.Buffer{} t, err := template.New("").Parse(string(tmpl)) if err != nil { @@ -128,7 +135,7 @@ func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata } // generate static page - if err = md.generatePage(c, fpath, b.Bytes()); err != nil { + if err = md.generatePage(c, requestPath, b.Bytes()); err != nil { // if static page generation fails, // nothing fatal, only log the error. log.Println(err) @@ -138,8 +145,47 @@ func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata } -func defaultTemplate(c Config, metadata Metadata, fpath string) []byte { - // else, use default template +// generatePage generates a static html page from the markdown in content. +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") + } + + // 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 + } + } + + 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 +} + +// defaultTemplate constructs a default template. +func defaultTemplate(c Config, metadata Metadata, requestPath string) []byte { var scripts, styles bytes.Buffer for _, style := range c.Styles { styles.WriteString(strings.Replace(cssTemplate, "{{url}}", style, 1)) @@ -153,7 +199,7 @@ func defaultTemplate(c Config, metadata Metadata, fpath string) []byte { // Title is first line (length-limited), otherwise filename title := metadata.Title if title == "" { - title = filepath.Base(fpath) + title = filepath.Base(requestPath) if body, _ := metadata.Variables["body"].([]byte); len(body) > 128 { title = string(body[:128]) } else if len(body) > 0 { @@ -169,42 +215,6 @@ func defaultTemplate(c Config, metadata Metadata, fpath string) []byte { return html } -func (md Markdown) generatePage(c Config, fpath string, content []byte) error { - // should not happen - // must be set on init - if c.StaticDir == "" { - return fmt.Errorf("Static directory not set") - } - - // 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 - } - } - - filePath := filepath.Join(c.StaticDir, fpath) - - // If it is index file, use the directory instead - if md.IsIndexFile(filepath.Base(fpath)) { - filePath, _ = filepath.Split(filePath) - } - 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[fpath] = filePath - return nil -} - const ( htmlTemplate = `