From 7c9867917aacf3755c24bfe3bcb8ccbdb04201ec Mon Sep 17 00:00:00 2001 From: Tobias Weingartner Date: Sat, 16 Apr 2016 14:45:32 -0700 Subject: [PATCH] Fixup tests and move metadata into own package --- middleware/markdown/markdown.go | 24 +++++- .../markdown/{ => metadata}/metadata.go | 84 ++++++++++++++++--- .../markdown/{ => metadata}/metadata_json.go | 39 ++++++++- middleware/markdown/metadata/metadata_none.go | 50 +++++++++++ .../markdown/{ => metadata}/metadata_test.go | 68 ++++++++------- .../markdown/{ => metadata}/metadata_toml.go | 32 ++++++- .../markdown/{ => metadata}/metadata_yaml.go | 30 ++++++- middleware/markdown/process.go | 69 +++------------ middleware/markdown/template.go | 40 +++++++++ 9 files changed, 330 insertions(+), 106 deletions(-) rename middleware/markdown/{ => metadata}/metadata.go (65%) rename middleware/markdown/{ => metadata}/metadata_json.go (57%) create mode 100644 middleware/markdown/metadata/metadata_none.go rename middleware/markdown/{ => metadata}/metadata_test.go (77%) rename middleware/markdown/{ => metadata}/metadata_toml.go (61%) rename middleware/markdown/{ => metadata}/metadata_yaml.go (62%) diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go index 5c2ac5fcd..56c735f90 100644 --- a/middleware/markdown/markdown.go +++ b/middleware/markdown/markdown.go @@ -70,9 +70,26 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error continue } + var dirents []os.FileInfo fpath := r.URL.Path if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { + // We're serving a directory index file, which may be a markdown + // file with a template. Let's grab a list of files this directory + // URL points to, and pass that in to any possible template invocations, + // so that templates can customize the look and feel of a directory. + + fdp, err := md.FileSys.Open(fpath) + if err != nil { + return http.StatusInternalServerError, err + } + dirents, err = fdp.Readdir(-1) + if err != nil { + return http.StatusInternalServerError, err + } + + // Set path to found index file fpath = idx + _ = dirents } // If supported extension, process it @@ -100,11 +117,16 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error Req: r, URL: r.URL, } - html, err := md.Process(cfg, fpath, body, ctx) + html, err := cfg.Markdown(fpath, body, ctx) if err != nil { return http.StatusInternalServerError, err } + // html, err = md.doTemplate(cfg, html, ctx) + // if err != nil { + // return http.StatusInternalServerError, err + // } + middleware.SetLastModifiedHeader(w, fs.ModTime()) w.Write(html) return http.StatusOK, nil diff --git a/middleware/markdown/metadata.go b/middleware/markdown/metadata/metadata.go similarity index 65% rename from middleware/markdown/metadata.go rename to middleware/markdown/metadata/metadata.go index 20a30e18a..c5ccaaaab 100644 --- a/middleware/markdown/metadata.go +++ b/middleware/markdown/metadata/metadata.go @@ -1,6 +1,7 @@ -package markdown +package metadata import ( + "bufio" "bytes" "fmt" "time" @@ -64,20 +65,23 @@ func (m *Metadata) load(parsedMap map[string]interface{}) { // MetadataParser is a an interface that must be satisfied by each parser type MetadataParser interface { + // Initialize a parser + Init(b *bytes.Buffer) bool + + // Type of metadata + Type() string + // Opening identifier Opening() []byte // Closing identifier Closing() []byte - // Parse the metadata. - // Returns the remaining page contents (Markdown) - // after extracting metadata - Parse([]byte) ([]byte, error) - // Parsed metadata. - // Should be called after a call to Parse returns no error Metadata() Metadata + + // Raw markdown. + Markdown() []byte } // extractMetadata separates metadata content from from markdown content in b. @@ -109,8 +113,19 @@ func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown return metadata, markdown, nil } +func GetParser(buf []byte) MetadataParser { + for _, p := range parsers() { + b := bytes.NewBuffer(buf) + if p.Init(b) { + return p + } + } + + return nil +} + // findParser finds the parser using line that contains opening identifier -func findParser(b []byte) MetadataParser { +func FindParser(b []byte) MetadataParser { var line []byte // Read first line if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil { @@ -125,7 +140,7 @@ func findParser(b []byte) MetadataParser { return nil } -func newMetadata() Metadata { +func NewMetadata() Metadata { return Metadata{ Variables: make(map[string]string), Flags: make(map[string]bool), @@ -135,8 +150,53 @@ func newMetadata() Metadata { // parsers returns all available parsers func parsers() []MetadataParser { return []MetadataParser{ - &JSONMetadataParser{metadata: newMetadata()}, - &TOMLMetadataParser{metadata: newMetadata()}, - &YAMLMetadataParser{metadata: newMetadata()}, + &TOMLMetadataParser{}, + &YAMLMetadataParser{metadata: NewMetadata()}, + &JSONMetadataParser{}, + &NoneMetadataParser{}, } } + +// Split out "normal" metadata with given delimiter +func splitBuffer(b *bytes.Buffer, delim string) (*bytes.Buffer, *bytes.Buffer) { + scanner := bufio.NewScanner(b) + + // Read and check first line + if !scanner.Scan() { + return nil, nil + } + if string(bytes.TrimSpace(scanner.Bytes())) != delim { + return nil, nil + } + + // Accumulate metadata, until delimiter + meta := bytes.NewBuffer(nil) + for scanner.Scan() { + if string(bytes.TrimSpace(scanner.Bytes())) == delim { + break + } + if _, err := meta.Write(scanner.Bytes()); err != nil { + return nil, nil + } + if _, err := meta.WriteRune('\n'); err != nil { + return nil, nil + } + } + // Make sure we saw closing delimiter + if string(bytes.TrimSpace(scanner.Bytes())) != delim { + return nil, nil + } + + // The rest is markdown + markdown := new(bytes.Buffer) + for scanner.Scan() { + if _, err := markdown.Write(scanner.Bytes()); err != nil { + return nil, nil + } + if _, err := markdown.WriteRune('\n'); err != nil { + return nil, nil + } + } + + return meta, markdown +} diff --git a/middleware/markdown/metadata_json.go b/middleware/markdown/metadata/metadata_json.go similarity index 57% rename from middleware/markdown/metadata_json.go rename to middleware/markdown/metadata/metadata_json.go index 53dbea19f..bfc8c13d7 100644 --- a/middleware/markdown/metadata_json.go +++ b/middleware/markdown/metadata/metadata_json.go @@ -1,4 +1,4 @@ -package markdown +package metadata import ( "bytes" @@ -8,6 +8,39 @@ import ( // JSONMetadataParser is the MetadataParser for JSON type JSONMetadataParser struct { metadata Metadata + markdown *bytes.Buffer +} + +func (j *JSONMetadataParser) Type() string { + return "JSON" +} + +// Parse metadata/markdown file +func (j *JSONMetadataParser) Init(b *bytes.Buffer) bool { + m := make(map[string]interface{}) + + err := json.Unmarshal(b.Bytes(), &m) + if err != nil { + var offset int + + if jerr, ok := err.(*json.SyntaxError); !ok { + return false + } else { + offset = int(jerr.Offset) + } + + m = make(map[string]interface{}) + err = json.Unmarshal(b.Next(offset-1), &m) + if err != nil { + return false + } + } + + j.metadata = NewMetadata() + j.metadata.load(m) + j.markdown = bytes.NewBuffer(b.Bytes()) + + return true } // Parse the metadata @@ -34,6 +67,10 @@ func (j *JSONMetadataParser) Metadata() Metadata { return j.metadata } +func (j *JSONMetadataParser) Markdown() []byte { + return j.markdown.Bytes() +} + // Opening returns the opening identifier JSON metadata func (j *JSONMetadataParser) Opening() []byte { return []byte("{") diff --git a/middleware/markdown/metadata/metadata_none.go b/middleware/markdown/metadata/metadata_none.go new file mode 100644 index 000000000..3dc22c076 --- /dev/null +++ b/middleware/markdown/metadata/metadata_none.go @@ -0,0 +1,50 @@ +package metadata + +import ( + "bytes" +) + +// TOMLMetadataParser is the MetadataParser for TOML +type NoneMetadataParser struct { + metadata Metadata + markdown *bytes.Buffer +} + +func (n *NoneMetadataParser) Type() string { + return "None" +} + +// Parse metadata/markdown file +func (n *NoneMetadataParser) Init(b *bytes.Buffer) bool { + m := make(map[string]interface{}) + n.metadata = NewMetadata() + n.metadata.load(m) + n.markdown = bytes.NewBuffer(b.Bytes()) + + return true +} + +// Parse the metadata +func (n *NoneMetadataParser) Parse(b []byte) ([]byte, error) { + return nil, nil +} + +// Metadata returns parsed metadata. It should be called +// only after a call to Parse returns without error. +func (n *NoneMetadataParser) Metadata() Metadata { + return n.metadata +} + +func (n *NoneMetadataParser) Markdown() []byte { + return n.markdown.Bytes() +} + +// Opening returns the opening identifier TOML metadata +func (n *NoneMetadataParser) Opening() []byte { + return []byte("...") +} + +// Closing returns the closing identifier TOML metadata +func (n *NoneMetadataParser) Closing() []byte { + return []byte("...") +} diff --git a/middleware/markdown/metadata_test.go b/middleware/markdown/metadata/metadata_test.go similarity index 77% rename from middleware/markdown/metadata_test.go rename to middleware/markdown/metadata/metadata_test.go index f219d9237..0c155d37c 100644 --- a/middleware/markdown/metadata_test.go +++ b/middleware/markdown/metadata/metadata_test.go @@ -1,9 +1,7 @@ -package markdown +package metadata import ( "bytes" - "fmt" - "reflect" "strings" "testing" ) @@ -164,56 +162,52 @@ func TestParsers(t *testing.T) { testData [5]string name string }{ - {&JSONMetadataParser{metadata: newMetadata()}, JSON, "json"}, - {&YAMLMetadataParser{metadata: newMetadata()}, YAML, "yaml"}, - {&TOMLMetadataParser{metadata: newMetadata()}, TOML, "toml"}, + {&JSONMetadataParser{}, JSON, "JSON"}, + {&YAMLMetadataParser{}, YAML, "YAML"}, + {&TOMLMetadataParser{}, TOML, "TOML"}, } for _, v := range data { // metadata without identifiers - if _, err := v.parser.Parse([]byte(v.testData[0])); err == nil { + if v.parser.Init(bytes.NewBufferString(v.testData[0])) { t.Fatalf("Expected error for invalid metadata for %v", v.name) } // metadata with identifiers - md, err := v.parser.Parse([]byte(v.testData[1])) - check(t, err) + if !v.parser.Init(bytes.NewBufferString(v.testData[1])) { + t.Fatalf("Metadata failed to initialize, type %v", v.parser.Type()) + } + md := v.parser.Markdown() if !compare(v.parser.Metadata()) { t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata(), v.name) } if "Page content" != strings.TrimSpace(string(md)) { t.Fatalf("Expected %v, found %v for %v", "Page content", string(md), v.name) } - - var line []byte - fmt.Fscanln(bytes.NewReader([]byte(v.testData[1])), &line) - if parser := findParser(line); parser == nil { - t.Fatalf("Parser must be found for %v", v.name) - } else { - if reflect.TypeOf(parser) != reflect.TypeOf(v.parser) { - t.Fatalf("parsers not equal. %v != %v", reflect.TypeOf(parser), reflect.TypeOf(v.parser)) - } + // Check that we find the correct metadata parser type + if p := GetParser([]byte(v.testData[1])); p.Type() != v.name { + t.Fatalf("Wrong parser found, expected %v, found %v", v.name, p.Type()) } // metadata without closing identifier - if _, err := v.parser.Parse([]byte(v.testData[2])); err == nil { - t.Fatalf("Expected error for missing closing identifier for %v", v.name) + if v.parser.Init(bytes.NewBufferString(v.testData[2])) { + t.Fatalf("Expected error for missing closing identifier for %v parser", v.name) } // invalid metadata - if _, err = v.parser.Parse([]byte(v.testData[3])); err == nil { + if v.parser.Init(bytes.NewBufferString(v.testData[3])) { t.Fatalf("Expected error for invalid metadata for %v", v.name) } // front matter but no body - if _, err = v.parser.Parse([]byte(v.testData[4])); err != nil { + if !v.parser.Init(bytes.NewBufferString(v.testData[4])) { t.Fatalf("Unexpected error for valid metadata but no body for %v", v.name) } } - } func TestLargeBody(t *testing.T) { + var JSON = `{ "template": "chapter" } @@ -232,24 +226,36 @@ Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, template : chapter --- +Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman. + + ` + var NONE = ` + Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman. ` var expectedBody = `Mycket olika byggnader har man i de nordiska rikena: pyramidformiga, kilformiga, välvda, runda och fyrkantiga. De pyramidformiga består helt enkelt av träribbor, som upptill löper samman och nedtill bildar en vidare krets; de är avsedda att användas av hantverkarna under sommaren, för att de inte ska plågas av solen, på samma gång som de besväras av rök och eld. De kilformiga husen är i regel försedda med höga tak, för att de täta och tunga snömassorna fortare ska kunna blåsa av och inte tynga ned taken. Dessa är täckta av björknäver, tegel eller kluvet spån av furu - för kådans skull -, gran, ek eller bok; taken på de förmögnas hus däremot med plåtar av koppar eller bly, i likhet med kyrktaken. Valvbyggnaderna uppförs ganska konstnärligt till skydd mot våldsamma vindar och snöfall, görs av sten eller trä, och är avsedda för olika alldagliga viktiga ändamål. Liknande byggnader kan finnas i stormännens gårdar där de används som förvaringsrum för husgeråd och jordbruksredskap. De runda byggnaderna - som för övrigt är de högst sällsynta - används av konstnärer, som vid sitt arbete behöver ett jämnt fördelat ljus från taket. Vanligast är de fyrkantiga husen, vars grova bjälkar är synnerligen väl hopfogade i hörnen - ett sant mästerverk av byggnadskonst; även dessa har fönster högt uppe i taken, för att dagsljuset skall kunna strömma in och ge alla därinne full belysning. Stenhusen har dörröppningar i förhållande till byggnadens storlek, men smala fönstergluggar, som skydd mot den stränga kölden, frosten och snön. Vore de större och vidare, såsom fönstren i Italien, skulle husen i följd av den fint yrande snön, som röres upp av den starka blåsten, precis som dammet av virvelvinden, snart nog fyllas med massor av snö och inte kunna stå emot dess tryck, utan störta samman. ` + data := []struct { - parser MetadataParser + pType string testData string - name string }{ - {&JSONMetadataParser{metadata: newMetadata()}, JSON, "json"}, - {&YAMLMetadataParser{metadata: newMetadata()}, YAML, "yaml"}, - {&TOMLMetadataParser{metadata: newMetadata()}, TOML, "toml"}, + {"JSON", JSON}, + {"TOML", TOML}, + {"YAML", YAML}, + {"None", NONE}, } for _, v := range data { - // metadata without identifiers - if md, err := v.parser.Parse([]byte(v.testData)); err != nil || strings.TrimSpace(string(md)) != strings.TrimSpace(expectedBody) { - t.Fatalf("Error not expected and/or markdown not equal for %v", v.name) + p := GetParser([]byte(v.testData)) + if v.pType != p.Type() { + t.Fatalf("Wrong parser type, expected %v, got %v", v.pType, p.Type()) + } + md := p.Markdown() + if strings.TrimSpace(string(md)) != strings.TrimSpace(expectedBody) { + t.Log("Provided:", v.testData) + t.Log("Returned:", p.Markdown()) + t.Fatalf("Error, mismatched body in expected type %v, matched type %v", v.pType, p.Type()) } } } diff --git a/middleware/markdown/metadata_toml.go b/middleware/markdown/metadata/metadata_toml.go similarity index 61% rename from middleware/markdown/metadata_toml.go rename to middleware/markdown/metadata/metadata_toml.go index fe4068d7d..cfd3a9f7f 100644 --- a/middleware/markdown/metadata_toml.go +++ b/middleware/markdown/metadata/metadata_toml.go @@ -1,12 +1,37 @@ -package markdown +package metadata import ( + "bytes" + "github.com/BurntSushi/toml" ) // TOMLMetadataParser is the MetadataParser for TOML type TOMLMetadataParser struct { metadata Metadata + markdown *bytes.Buffer +} + +func (t *TOMLMetadataParser) Type() string { + return "TOML" +} + +// Parse metadata/markdown file +func (t *TOMLMetadataParser) Init(b *bytes.Buffer) bool { + meta, data := splitBuffer(b, "+++") + if meta == nil || data == nil { + return false + } + t.markdown = data + + m := make(map[string]interface{}) + if err := toml.Unmarshal(meta.Bytes(), &m); err != nil { + return false + } + t.metadata = NewMetadata() + t.metadata.load(m) + + return true } // Parse the metadata @@ -15,6 +40,7 @@ func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) { if err != nil { return markdown, err } + m := make(map[string]interface{}) if err := toml.Unmarshal(b, &m); err != nil { return markdown, err @@ -29,6 +55,10 @@ func (t *TOMLMetadataParser) Metadata() Metadata { return t.metadata } +func (t *TOMLMetadataParser) Markdown() []byte { + return t.markdown.Bytes() +} + // Opening returns the opening identifier TOML metadata func (t *TOMLMetadataParser) Opening() []byte { return []byte("+++") diff --git a/middleware/markdown/metadata_yaml.go b/middleware/markdown/metadata/metadata_yaml.go similarity index 62% rename from middleware/markdown/metadata_yaml.go rename to middleware/markdown/metadata/metadata_yaml.go index 41103e219..e78774459 100644 --- a/middleware/markdown/metadata_yaml.go +++ b/middleware/markdown/metadata/metadata_yaml.go @@ -1,12 +1,36 @@ -package markdown +package metadata import ( + "bytes" + "gopkg.in/yaml.v2" ) // YAMLMetadataParser is the MetadataParser for YAML type YAMLMetadataParser struct { metadata Metadata + markdown *bytes.Buffer +} + +func (y *YAMLMetadataParser) Type() string { + return "YAML" +} + +func (y *YAMLMetadataParser) Init(b *bytes.Buffer) bool { + meta, data := splitBuffer(b, "---") + if meta == nil || data == nil { + return false + } + y.markdown = data + + m := make(map[string]interface{}) + if err := yaml.Unmarshal(meta.Bytes(), &m); err != nil { + return false + } + y.metadata = NewMetadata() + y.metadata.load(m) + + return true } // Parse the metadata @@ -30,6 +54,10 @@ func (y *YAMLMetadataParser) Metadata() Metadata { return y.metadata } +func (y *YAMLMetadataParser) Markdown() []byte { + return y.markdown.Bytes() +} + // Opening returns the opening identifier YAML metadata func (y *YAMLMetadataParser) Opening() []byte { return []byte("---") diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go index e661e6978..27f337dd7 100644 --- a/middleware/markdown/process.go +++ b/middleware/markdown/process.go @@ -1,49 +1,19 @@ package markdown import ( - "bytes" "path/filepath" "github.com/mholt/caddy/middleware" + "github.com/mholt/caddy/middleware/markdown/metadata" "github.com/russross/blackfriday" ) -// Data represents a markdown document. -type Data struct { - middleware.Context - Doc map[string]string - DocFlags map[string]bool - Styles []string - Scripts []string -} - -// Include "overrides" the embedded middleware.Context's Include() -// method so that included files have access to d's fields. -func (d Data) Include(filename string) (string, error) { - return middleware.ContextInclude(filename, d, d.Root) -} - -// Process processes the contents of a page in b. It parses the metadata +// Markdown 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, ctx middleware.Context) ([]byte, error) { - var metadata = newMetadata() - var markdown []byte - var err error - - // find parser compatible with page contents - parser := findParser(b) - - 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 - } - metadata = parser.Metadata() - } +func (c *Config) Markdown(requestPath string, b []byte, ctx middleware.Context) ([]byte, error) { + parser := metadata.GetParser(b) + markdown := parser.Markdown() + mdata := parser.Metadata() // process markdown extns := 0 @@ -54,33 +24,14 @@ func (md Markdown) Process(c *Config, requestPath string, b []byte, ctx middlewa markdown = blackfriday.Markdown(markdown, c.Renderer, extns) // set it as body for template - metadata.Variables["body"] = string(markdown) - title := metadata.Title + mdata.Variables["body"] = string(markdown) + title := mdata.Title if title == "" { title = filepath.Base(requestPath) var extension = filepath.Ext(requestPath) title = title[0 : len(title)-len(extension)] } - metadata.Variables["title"] = title + mdata.Variables["title"] = title - // return md.processTemplate(c, requestPath, metadata, ctx) - return md.doTemplate(c, requestPath, metadata, ctx) -} - -// doTemplate executes a template given a requestPath, template, and metadata -func (md Markdown) doTemplate(c *Config, reqPath string, metadata Metadata, ctx middleware.Context) ([]byte, error) { - mdData := Data{ - Context: ctx, - Doc: metadata.Variables, - DocFlags: metadata.Flags, - Styles: c.Styles, - Scripts: c.Scripts, - } - - b := new(bytes.Buffer) - if err := c.Template.ExecuteTemplate(b, metadata.Template, mdData); err != nil { - return nil, err - } - - return b.Bytes(), nil + return execTemplate(c, mdata, ctx) } diff --git a/middleware/markdown/template.go b/middleware/markdown/template.go index 245ad9953..18d486a1d 100644 --- a/middleware/markdown/template.go +++ b/middleware/markdown/template.go @@ -1,10 +1,50 @@ package markdown import ( + "bytes" "io/ioutil" + "os" "text/template" + + "github.com/mholt/caddy/middleware" + "github.com/mholt/caddy/middleware/markdown/metadata" ) +// Data represents a markdown document. +type Data struct { + middleware.Context + Doc map[string]string + DocFlags map[string]bool + Styles []string + Scripts []string + Files []os.FileInfo +} + +// Include "overrides" the embedded middleware.Context's Include() +// method so that included files have access to d's fields. +// Note: using {{template 'template-name' .}} instead might be better. +func (d Data) Include(filename string) (string, error) { + return middleware.ContextInclude(filename, d, d.Root) +} + +// execTemplate executes a template given a requestPath, template, and metadata +func execTemplate(c *Config, mdata metadata.Metadata, ctx middleware.Context) ([]byte, error) { + mdData := Data{ + Context: ctx, + Doc: mdata.Variables, + DocFlags: mdata.Flags, + Styles: c.Styles, + Scripts: c.Scripts, + } + + b := new(bytes.Buffer) + if err := c.Template.ExecuteTemplate(b, mdata.Template, mdData); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + func setDefaultTemplate(filename string) *template.Template { buf, err := ioutil.ReadFile(filename) if err != nil {