Commit 2bccc146 authored by Tobias Weingartner's avatar Tobias Weingartner

Fixup and address most of the feedback.

parent a3af232d
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
package markdown package markdown
import ( import (
"io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path"
"strings"
"text/template" "text/template"
"time" "time"
...@@ -83,17 +83,22 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -83,17 +83,22 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
// URL points to, and pass that in to any possible template invocations, // URL points to, and pass that in to any possible template invocations,
// so that templates can customize the look and feel of a directory. // so that templates can customize the look and feel of a directory.
fdp, err := md.FileSys.Open(fpath) fdp, err := md.FileSys.Open(fpath)
if err != nil { switch {
if os.IsPermission(err) { case err == nil: // nop
return http.StatusForbidden, err case os.IsPermission(err):
} return http.StatusForbidden, err
case os.IsExist(err):
return http.StatusNotFound, nil
default: // did we run out of FD?
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
defer fdp.Close()
// Grab a possible set of directory entries. Note, we do not check // Grab a possible set of directory entries. Note, we do not check
// for errors here (unreadable directory, for example). It may // for errors here (unreadable directory, for example). It may
// still be useful to have a directory template file, without the // still be useful to have a directory template file, without the
// directory contents being present. // directory contents being present. Note, the directory's last
// modification is also present here (entry ".").
dirents, _ = fdp.Readdir(-1) dirents, _ = fdp.Readdir(-1)
for _, d := range dirents { for _, d := range dirents {
lastModTime = latest(lastModTime, d.ModTime()) lastModTime = latest(lastModTime, d.ModTime())
...@@ -103,53 +108,45 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -103,53 +108,45 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
fpath = idx fpath = idx
} }
// If supported extension, process it // If not supported extension, pass on it
if _, ok := cfg.Extensions[path.Ext(fpath)]; ok { if _, ok := cfg.Extensions[path.Ext(fpath)]; !ok {
f, err := md.FileSys.Open(fpath) return md.Next.ServeHTTP(w, r)
if err != nil { }
if os.IsPermission(err) {
return http.StatusForbidden, err
}
return http.StatusNotFound, nil
}
fs, err := f.Stat()
if err != nil {
return http.StatusNotFound, nil
}
lastModTime = latest(lastModTime, fs.ModTime())
body, err := ioutil.ReadAll(f)
if err != nil {
return http.StatusInternalServerError, err
}
ctx := middleware.Context{ // At this point we have a supported extension/markdown
Root: md.FileSys, f, err := md.FileSys.Open(fpath)
Req: r, switch {
URL: r.URL, case err == nil: // nop
} case os.IsPermission(err):
html, err := cfg.Markdown(fpath, body, dirents, ctx) return http.StatusForbidden, err
if err != nil { case os.IsExist(err):
return http.StatusInternalServerError, err return http.StatusNotFound, nil
} default: // did we run out of FD?
return http.StatusInternalServerError, err
}
defer f.Close()
// TODO(weingart): move template execution here, something like: if fs, err := f.Stat(); err != nil {
// return http.StatusGone, nil
// html, err = md.execTemplate(cfg, html, ctx) } else {
// if err != nil { lastModTime = latest(lastModTime, fs.ModTime())
// return http.StatusInternalServerError, err }
// }
middleware.SetLastModifiedHeader(w, lastModTime) ctx := middleware.Context{
if r.Method == http.MethodGet { Root: md.FileSys,
w.Write(html) Req: r,
} URL: r.URL,
return http.StatusOK, nil }
html, err := cfg.Markdown(title(fpath), f, dirents, ctx)
if err != nil {
return http.StatusInternalServerError, err
} }
// Didn't qualify to serve as markdown; pass-thru middleware.SetLastModifiedHeader(w, lastModTime)
return md.Next.ServeHTTP(w, r) if r.Method == http.MethodGet {
w.Write(html)
}
return http.StatusOK, nil
} }
// latest returns the latest time.Time // latest returns the latest time.Time
...@@ -164,3 +161,8 @@ func latest(t ...time.Time) time.Time { ...@@ -164,3 +161,8 @@ func latest(t ...time.Time) time.Time {
return last return last
} }
// title gives a backup generated title for a page
func title(p string) string {
return strings.TrimRight(path.Base(p), path.Ext(p))
}
...@@ -2,12 +2,14 @@ package markdown ...@@ -2,12 +2,14 @@ package markdown
import ( import (
"bufio" "bufio"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"text/template"
"time" "time"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
...@@ -217,3 +219,12 @@ func equalStrings(s1, s2 string) bool { ...@@ -217,3 +219,12 @@ func equalStrings(s1, s2 string) bool {
} }
return true return true
} }
func setDefaultTemplate(filename string) *template.Template {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return nil
}
return template.Must(GetDefaultTemplate().Parse(string(buf)))
}
package markdown package markdown
import ( import (
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" // "path/filepath"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/markdown/metadata" "github.com/mholt/caddy/middleware/markdown/metadata"
...@@ -33,8 +34,13 @@ func (f FileInfo) Summarize(wordcount int) (string, error) { ...@@ -33,8 +34,13 @@ func (f FileInfo) Summarize(wordcount int) (string, error) {
// Markdown 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). // (if any) and uses the template (if found).
func (c *Config) Markdown(requestPath string, b []byte, dirents []os.FileInfo, ctx middleware.Context) ([]byte, error) { func (c *Config) Markdown(title string, r io.Reader, dirents []os.FileInfo, ctx middleware.Context) ([]byte, error) {
parser := metadata.GetParser(b) body, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
parser := metadata.GetParser(body)
markdown := parser.Markdown() markdown := parser.Markdown()
mdata := parser.Metadata() mdata := parser.Metadata()
...@@ -44,19 +50,16 @@ func (c *Config) Markdown(requestPath string, b []byte, dirents []os.FileInfo, c ...@@ -44,19 +50,16 @@ func (c *Config) Markdown(requestPath string, b []byte, dirents []os.FileInfo, c
extns |= blackfriday.EXTENSION_FENCED_CODE extns |= blackfriday.EXTENSION_FENCED_CODE
extns |= blackfriday.EXTENSION_STRIKETHROUGH extns |= blackfriday.EXTENSION_STRIKETHROUGH
extns |= blackfriday.EXTENSION_DEFINITION_LISTS extns |= blackfriday.EXTENSION_DEFINITION_LISTS
markdown = blackfriday.Markdown(markdown, c.Renderer, extns) html := blackfriday.Markdown(markdown, c.Renderer, extns)
// set it as body for template // set it as body for template
mdata.Variables["body"] = string(markdown) mdata.Variables["body"] = string(html)
// fixup title // fixup title
title := mdata.Title mdata.Variables["title"] = mdata.Title
if title == "" { if mdata.Variables["title"] == "" {
title = filepath.Base(requestPath) mdata.Variables["title"] = title
var extension = filepath.Ext(requestPath)
title = title[0 : len(title)-len(extension)]
} }
mdata.Variables["title"] = title
// massage possible files // massage possible files
files := []FileInfo{} files := []FileInfo{}
......
...@@ -9,23 +9,26 @@ import ( ...@@ -9,23 +9,26 @@ import (
// Ensure we implement the Blackfriday Markdown Renderer interface // Ensure we implement the Blackfriday Markdown Renderer interface
var _ blackfriday.Renderer = (*Renderer)(nil) var _ blackfriday.Renderer = (*Renderer)(nil)
// Renderer is a plain-text Markdown renderer that implements the
// blackfriday.Renderer interface. Many of the required methods are
// stubs with no output.
type Renderer struct{} type Renderer struct{}
// Blocklevel callbacks // Blocklevel callbacks
// BlockCode is the code tag callback. // Stub BlockCode is the code tag callback.
func (r Renderer) BlockCode(out *bytes.Buffer, text []byte, land string) {} func (r Renderer) BlockCode(out *bytes.Buffer, text []byte, land string) {}
// BlockQuote is teh quote tag callback. // Stub BlockQuote is teh quote tag callback.
func (r Renderer) BlockQuote(out *bytes.Buffer, text []byte) {} func (r Renderer) BlockQuote(out *bytes.Buffer, text []byte) {}
// BlockHtml is the HTML tag callback. // Stub BlockHtml is the HTML tag callback.
func (r Renderer) BlockHtml(out *bytes.Buffer, text []byte) {} func (r Renderer) BlockHtml(out *bytes.Buffer, text []byte) {}
// Header is the header tag callback. // Stub Header is the header tag callback.
func (r Renderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {} func (r Renderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {}
// HRule is the horizontal rule tag callback. // Stub HRule is the horizontal rule tag callback.
func (r Renderer) HRule(out *bytes.Buffer) {} func (r Renderer) HRule(out *bytes.Buffer) {}
// List is the list tag callback. // List is the list tag callback.
...@@ -39,10 +42,11 @@ func (r Renderer) List(out *bytes.Buffer, text func() bool, flags int) { ...@@ -39,10 +42,11 @@ func (r Renderer) List(out *bytes.Buffer, text func() bool, flags int) {
out.Write([]byte{' '}) out.Write([]byte{' '})
} }
// ListItem is the list item tag callback. // Stub ListItem is the list item tag callback.
func (r Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {} func (r Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {}
// Paragraph is the paragraph tag callback. // Paragraph is the paragraph tag callback. This renders simple paragraph text
// into plain text, such that summaries can be easily generated.
func (r Renderer) Paragraph(out *bytes.Buffer, text func() bool) { func (r Renderer) Paragraph(out *bytes.Buffer, text func() bool) {
marker := out.Len() marker := out.Len()
if !text() { if !text() {
...@@ -51,93 +55,98 @@ func (r Renderer) Paragraph(out *bytes.Buffer, text func() bool) { ...@@ -51,93 +55,98 @@ func (r Renderer) Paragraph(out *bytes.Buffer, text func() bool) {
out.Write([]byte{' '}) out.Write([]byte{' '})
} }
// Table is the table tag callback. // Stub Table is the table tag callback.
func (r Renderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {} func (r Renderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {}
// TableRow is the table row tag callback. // Stub TableRow is the table row tag callback.
func (r Renderer) TableRow(out *bytes.Buffer, text []byte) {} func (r Renderer) TableRow(out *bytes.Buffer, text []byte) {}
// TableHeaderCell is the table header cell tag callback. // Stub TableHeaderCell is the table header cell tag callback.
func (r Renderer) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {} func (r Renderer) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {}
// TableCell is the table cell tag callback. // Stub TableCell is the table cell tag callback.
func (r Renderer) TableCell(out *bytes.Buffer, text []byte, flags int) {} func (r Renderer) TableCell(out *bytes.Buffer, text []byte, flags int) {}
// Footnotes is the foot notes tag callback. // Stub Footnotes is the foot notes tag callback.
func (r Renderer) Footnotes(out *bytes.Buffer, text func() bool) {} func (r Renderer) Footnotes(out *bytes.Buffer, text func() bool) {}
// FootnoteItem is the footnote item tag callback. // Stub FootnoteItem is the footnote item tag callback.
func (r Renderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {} func (r Renderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {}
// TitleBlock is the title tag callback. // Stub TitleBlock is the title tag callback.
func (r Renderer) TitleBlock(out *bytes.Buffer, text []byte) {} func (r Renderer) TitleBlock(out *bytes.Buffer, text []byte) {}
// Spanlevel callbacks // Spanlevel callbacks
// AutoLink is the autolink tag callback. // Stub AutoLink is the autolink tag callback.
func (r Renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {} func (r Renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {}
// CodeSpan is the code span tag callback. // CodeSpan is the code span tag callback. Outputs a simple Markdown version
// of the code span.
func (r Renderer) CodeSpan(out *bytes.Buffer, text []byte) { func (r Renderer) CodeSpan(out *bytes.Buffer, text []byte) {
out.Write([]byte("`")) out.Write([]byte("`"))
out.Write(text) out.Write(text)
out.Write([]byte("`")) out.Write([]byte("`"))
} }
// DoubleEmphasis is the double emphasis tag callback. // DoubleEmphasis is the double emphasis tag callback. Outputs a simple
// plain-text version of the input.
func (r Renderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { func (r Renderer) DoubleEmphasis(out *bytes.Buffer, text []byte) {
out.Write(text) out.Write(text)
} }
// Emphasis is the emphasis tag callback. // Emphasis is the emphasis tag callback. Outputs a simple plain-text
// version of the input.
func (r Renderer) Emphasis(out *bytes.Buffer, text []byte) { func (r Renderer) Emphasis(out *bytes.Buffer, text []byte) {
out.Write(text) out.Write(text)
} }
// Image is the image tag callback. // Stub Image is the image tag callback.
func (r Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {} func (r Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {}
// LineBreak is the line break tag callback. // Stub LineBreak is the line break tag callback.
func (r Renderer) LineBreak(out *bytes.Buffer) {} func (r Renderer) LineBreak(out *bytes.Buffer) {}
// Link is the link tag callback. // Link is the link tag callback. Outputs a sipmle plain-text version
// of the input.
func (r Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { func (r Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
out.Write(content) out.Write(content)
} }
// RawHtmlTag is the raw HTML tag callback. // Stub RawHtmlTag is the raw HTML tag callback.
func (r Renderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {} func (r Renderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {}
// TripleEmphasis is the triple emphasis tag callback. // TripleEmphasis is the triple emphasis tag callback. Outputs a simple plain-text
// version of the input.
func (r Renderer) TripleEmphasis(out *bytes.Buffer, text []byte) { func (r Renderer) TripleEmphasis(out *bytes.Buffer, text []byte) {
out.Write(text) out.Write(text)
} }
// StrikeThrough is the strikethrough tag callback. // Stub StrikeThrough is the strikethrough tag callback.
func (r Renderer) StrikeThrough(out *bytes.Buffer, text []byte) {} func (r Renderer) StrikeThrough(out *bytes.Buffer, text []byte) {}
// FootnoteRef is the footnote ref tag callback. // Stub FootnoteRef is the footnote ref tag callback.
func (r Renderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {} func (r Renderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {}
// Lowlevel callbacks // Lowlevel callbacks
// Entity callback. // Entity callback. Outputs a simple plain-text version of the input.
func (r Renderer) Entity(out *bytes.Buffer, entity []byte) { func (r Renderer) Entity(out *bytes.Buffer, entity []byte) {
out.Write(entity) out.Write(entity)
} }
// NormalText callback. // NormalText callback. Outputs a simple plain-text version of the input.
func (r Renderer) NormalText(out *bytes.Buffer, text []byte) { func (r Renderer) NormalText(out *bytes.Buffer, text []byte) {
out.Write(text) out.Write(text)
} }
// Header and footer // Header and footer
// DocumentHeader callback. // Stub DocumentHeader callback.
func (r Renderer) DocumentHeader(out *bytes.Buffer) {} func (r Renderer) DocumentHeader(out *bytes.Buffer) {}
// DocumentFooter callback. // Stub DocumentFooter callback.
func (r Renderer) DocumentFooter(out *bytes.Buffer) {} func (r Renderer) DocumentFooter(out *bytes.Buffer) {}
// GetFlags returns zero. // Stub GetFlags returns zero.
func (r Renderer) GetFlags() int { return 0 } func (r Renderer) GetFlags() int { return 0 }
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
) )
// Markdown formats input using a plain-text renderer, and
// then returns up to the first `wordcount` words as a summary.
func Markdown(input []byte, wordcount int) []byte { func Markdown(input []byte, wordcount int) []byte {
words := bytes.Fields(blackfriday.Markdown(input, Renderer{}, 0)) words := bytes.Fields(blackfriday.Markdown(input, Renderer{}, 0))
if wordcount > len(words) { if wordcount > len(words) {
......
...@@ -3,7 +3,6 @@ package markdown ...@@ -3,7 +3,6 @@ package markdown
import ( import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
// "os"
"text/template" "text/template"
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
...@@ -46,15 +45,6 @@ func execTemplate(c *Config, mdata metadata.Metadata, files []FileInfo, ctx midd ...@@ -46,15 +45,6 @@ func execTemplate(c *Config, mdata metadata.Metadata, files []FileInfo, ctx midd
return b.Bytes(), nil return b.Bytes(), nil
} }
func setDefaultTemplate(filename string) *template.Template {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return nil
}
return template.Must(GetDefaultTemplate().Parse(string(buf)))
}
func SetTemplate(t *template.Template, name, filename string) error { func SetTemplate(t *template.Template, name, filename string) error {
// Read template // Read template
......
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