diff --git a/config/setup/browse.go b/config/setup/browse.go
index 7db8b40ac..a98ec313d 100644
--- a/config/setup/browse.go
+++ b/config/setup/browse.go
@@ -2,8 +2,8 @@ package setup
import (
"fmt"
- "html/template"
"io/ioutil"
+ "text/template"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/browse"
diff --git a/config/setup/markdown.go b/config/setup/markdown.go
index fd92f20ad..bbccf9d5f 100644
--- a/config/setup/markdown.go
+++ b/config/setup/markdown.go
@@ -62,7 +62,8 @@ func Markdown(c *Controller) (middleware.Middleware, error) {
reqPath = "/" + reqPath
// Generate the static file
- _, err = md.Process(cfg, reqPath, body)
+ ctx := middleware.Context{Root: md.FileSys}
+ _, err = md.Process(cfg, reqPath, body, ctx)
if err != nil {
return err
}
diff --git a/middleware/browse/browse.go b/middleware/browse/browse.go
index d11fe6f88..756ca8c83 100644
--- a/middleware/browse/browse.go
+++ b/middleware/browse/browse.go
@@ -5,13 +5,13 @@ package browse
import (
"bytes"
"errors"
- "html/template"
"net/http"
"net/url"
"os"
"path"
"sort"
"strings"
+ "text/template"
"time"
"github.com/dustin/go-humanize"
@@ -51,6 +51,8 @@ type Listing struct {
// And which order
Order string
+
+ middleware.Context
}
// FileInfo is the info about a particular file or directory
@@ -137,8 +139,9 @@ var IndexPages = []string{
"default.txt",
}
-func directoryListing(files []os.FileInfo, urlPath string, canGoUp bool) (Listing, error) {
+func directoryListing(files []os.FileInfo, r *http.Request, canGoUp bool, root string) (Listing, error) {
var fileinfos []FileInfo
+ var urlPath = r.URL.Path
for _, f := range files {
name := f.Name()
@@ -170,6 +173,11 @@ func directoryListing(files []os.FileInfo, urlPath string, canGoUp bool) (Listin
Path: urlPath,
CanGoUp: canGoUp,
Items: fileinfos,
+ Context: middleware.Context{
+ Root: http.Dir(root),
+ Req: r,
+ URL: r.URL,
+ },
}, nil
}
@@ -224,7 +232,7 @@ func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
}
}
// Assemble listing of directory contents
- listing, err := directoryListing(files, r.URL.Path, canGoUp)
+ listing, err := directoryListing(files, r, canGoUp, b.Root)
if err != nil { // directory isn't browsable
continue
}
diff --git a/middleware/browse/browse_test.go b/middleware/browse/browse_test.go
index 33218a195..b0ff28db6 100644
--- a/middleware/browse/browse_test.go
+++ b/middleware/browse/browse_test.go
@@ -1,9 +1,14 @@
package browse
import (
+ "net/http"
+ "net/http/httptest"
"sort"
"testing"
+ "text/template"
"time"
+
+ "github.com/mholt/caddy/middleware"
)
// "sort" package has "IsSorted" function, but no "IsReversed";
@@ -94,3 +99,59 @@ func TestSort(t *testing.T) {
t.Errorf("The listing isn't reversed by time: %v", listing.Items)
}
}
+
+func TestBrowseTemplate(t *testing.T) {
+ tmpl, err := template.ParseFiles("testdata/photos.tpl")
+ if err != nil {
+ t.Fatalf("An error occured while parsing the template: %v", err)
+ }
+
+ b := Browse{
+ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
+ t.Fatalf("Next shouldn't be called")
+ return 0, nil
+ }),
+ Root: "./testdata",
+ Configs: []Config{
+ Config{
+ PathScope: "/photos",
+ Template: tmpl,
+ },
+ },
+ }
+
+ req, err := http.NewRequest("GET", "/photos/", nil)
+ if err != nil {
+ t.Fatalf("Test: Could not create HTTP request: %v", err)
+ }
+
+ rec := httptest.NewRecorder()
+
+ b.ServeHTTP(rec, req)
+ if rec.Code != http.StatusOK {
+ t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code)
+ }
+
+ respBody := rec.Body.String()
+ expectedBody := `
+
+
+Template
+
+
+Header
+
+/photos/
+
+test.html
+
+test2.html
+
+
+
+`
+
+ if respBody != expectedBody {
+ t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
+ }
+}
diff --git a/middleware/browse/testdata/header.html b/middleware/browse/testdata/header.html
new file mode 100644
index 000000000..78e5a6a48
--- /dev/null
+++ b/middleware/browse/testdata/header.html
@@ -0,0 +1 @@
+Header
diff --git a/middleware/browse/testdata/photos.tpl b/middleware/browse/testdata/photos.tpl
new file mode 100644
index 000000000..5163ca008
--- /dev/null
+++ b/middleware/browse/testdata/photos.tpl
@@ -0,0 +1,13 @@
+
+
+
+Template
+
+
+{{.Include "header.html"}}
+{{.Path}}
+{{range .Items}}
+{{.Name}}
+{{end}}
+
+
diff --git a/middleware/browse/testdata/photos/test.html b/middleware/browse/testdata/photos/test.html
new file mode 100644
index 000000000..40535a223
--- /dev/null
+++ b/middleware/browse/testdata/photos/test.html
@@ -0,0 +1,8 @@
+
+
+
+Test
+
+
+
+
diff --git a/middleware/browse/testdata/photos/test2.html b/middleware/browse/testdata/photos/test2.html
new file mode 100644
index 000000000..8e10c5780
--- /dev/null
+++ b/middleware/browse/testdata/photos/test2.html
@@ -0,0 +1,8 @@
+
+
+
+Test 2
+
+
+
+
diff --git a/middleware/templates/context.go b/middleware/context.go
similarity index 63%
rename from middleware/templates/context.go
rename to middleware/context.go
index 4f125c991..508ec6042 100644
--- a/middleware/templates/context.go
+++ b/middleware/context.go
@@ -1,4 +1,4 @@
-package templates
+package middleware
import (
"bytes"
@@ -8,23 +8,22 @@ import (
"net/url"
"text/template"
"time"
-
- "github.com/mholt/caddy/middleware"
)
// This file contains the context and functions available for
// use in the templates.
// context is the context with which templates are executed.
-type context struct {
- root http.FileSystem
- req *http.Request
- URL *url.URL
+type Context struct {
+ Root http.FileSystem
+ Req *http.Request
+ // This is used to access information about the URL.
+ URL *url.URL
}
// Include returns the contents of filename relative to the site root
-func (c context) Include(filename string) (string, error) {
- file, err := c.root.Open(filename)
+func (c Context) Include(filename string) (string, error) {
+ file, err := c.Root.Open(filename)
if err != nil {
return "", err
}
@@ -50,13 +49,13 @@ func (c context) Include(filename string) (string, error) {
}
// Date returns the current timestamp in the specified format
-func (c context) Date(format string) string {
+func (c Context) Date(format string) string {
return time.Now().Format(format)
}
// Cookie gets the value of a cookie with name name.
-func (c context) Cookie(name string) string {
- cookies := c.req.Cookies()
+func (c Context) Cookie(name string) string {
+ cookies := c.Req.Cookies()
for _, cookie := range cookies {
if cookie.Name == name {
return cookie.Value
@@ -66,15 +65,15 @@ func (c context) Cookie(name string) string {
}
// Header gets the value of a request header with field name.
-func (c context) Header(name string) string {
- return c.req.Header.Get(name)
+func (c Context) Header(name string) string {
+ return c.Req.Header.Get(name)
}
// IP gets the (remote) IP address of the client making the request.
-func (c context) IP() string {
- ip, _, err := net.SplitHostPort(c.req.RemoteAddr)
+func (c Context) IP() string {
+ ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
if err != nil {
- return c.req.RemoteAddr
+ return c.Req.RemoteAddr
}
return ip
}
@@ -82,14 +81,14 @@ func (c context) IP() string {
// URI returns the raw, unprocessed request URI (including query
// string and hash) obtained directly from the Request-Line of
// the HTTP request.
-func (c context) URI() string {
- return c.req.RequestURI
+func (c Context) URI() string {
+ return c.Req.RequestURI
}
// Host returns the hostname portion of the Host header
// from the HTTP request.
-func (c context) Host() (string, error) {
- host, _, err := net.SplitHostPort(c.req.Host)
+func (c Context) Host() (string, error) {
+ host, _, err := net.SplitHostPort(c.Req.Host)
if err != nil {
return "", err
}
@@ -97,8 +96,8 @@ func (c context) Host() (string, error) {
}
// Port returns the port portion of the Host header if specified.
-func (c context) Port() (string, error) {
- _, port, err := net.SplitHostPort(c.req.Host)
+func (c Context) Port() (string, error) {
+ _, port, err := net.SplitHostPort(c.Req.Host)
if err != nil {
return "", err
}
@@ -106,12 +105,12 @@ func (c context) Port() (string, error) {
}
// Method returns the method (GET, POST, etc.) of the request.
-func (c context) Method() string {
- return c.req.Method
+func (c Context) Method() string {
+ return c.Req.Method
}
// PathMatches returns true if the path portion of the request
// URL matches pattern.
-func (c context) PathMatches(pattern string) bool {
- return middleware.Path(c.req.URL.Path).Matches(pattern)
+func (c Context) PathMatches(pattern string) bool {
+ return Path(c.Req.URL.Path).Matches(pattern)
}
diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go
index 6b7f35563..71b189068 100644
--- a/middleware/markdown/markdown.go
+++ b/middleware/markdown/markdown.go
@@ -119,7 +119,12 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
return http.StatusInternalServerError, err
}
- html, err := md.Process(m, fpath, body)
+ ctx := middleware.Context{
+ Root: md.FileSys,
+ Req: r,
+ URL: r.URL,
+ }
+ html, err := md.Process(m, fpath, body, ctx)
if err != nil {
return http.StatusInternalServerError, err
}
diff --git a/middleware/markdown/markdown_test.go b/middleware/markdown/markdown_test.go
new file mode 100644
index 000000000..e01d88e8e
--- /dev/null
+++ b/middleware/markdown/markdown_test.go
@@ -0,0 +1,126 @@
+package markdown
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/mholt/caddy/middleware"
+ "github.com/russross/blackfriday"
+)
+
+func TestMarkdown(t *testing.T) {
+ templates := make(map[string]string)
+ templates[DefaultTemplate] = "testdata/markdown_tpl.html"
+ md := Markdown{
+ Root: "./testdata",
+ FileSys: http.Dir("./testdata"),
+ Configs: []Config{
+ Config{
+ Renderer: blackfriday.HtmlRenderer(0, "", ""),
+ PathScope: "/blog",
+ Extensions: []string{".md"},
+ Styles: []string{},
+ Scripts: []string{},
+ Templates: templates,
+ },
+ Config{
+ Renderer: blackfriday.HtmlRenderer(0, "", ""),
+ PathScope: "/log",
+ Extensions: []string{".md"},
+ Styles: []string{"/resources/css/log.css", "/resources/css/default.css"},
+ Scripts: []string{"/resources/js/log.js", "/resources/js/default.js"},
+ Templates: make(map[string]string),
+ },
+ },
+ IndexFiles: []string{"index.html"},
+ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
+ t.Fatalf("Next shouldn't be called")
+ return 0, nil
+ }),
+ }
+
+ req, err := http.NewRequest("GET", "/blog/test.md", nil)
+ if err != nil {
+ t.Fatalf("Could not create HTTP request: %v", err)
+ }
+
+ rec := httptest.NewRecorder()
+
+ md.ServeHTTP(rec, req)
+ if rec.Code != http.StatusOK {
+ t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, rec.Code)
+ }
+
+ respBody := rec.Body.String()
+ expectedBody := `
+
+
+Markdown test
+
+
+Header
+
+Welcome to A Caddy website!
+Welcome on the blog
+
+Body
+
+go
+func getTrue() bool {
+ return true
+}
+
+
+
+
+`
+ if respBody != expectedBody {
+ t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
+ }
+
+ req, err = http.NewRequest("GET", "/log/test.md", nil)
+ if err != nil {
+ t.Fatalf("Could not create HTTP request: %v", err)
+ }
+ rec = httptest.NewRecorder()
+
+ md.ServeHTTP(rec, req)
+ if rec.Code != http.StatusOK {
+ t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, rec.Code)
+ }
+ respBody = rec.Body.String()
+ expectedBody = `
+
+
+ Markdown test
+
+
+
+
+
+
+
+
+
+ Welcome on the blog
+
+Body
+
+go
+func getTrue() bool {
+ return true
+}
+
+
+
+`
+
+ replacer := strings.NewReplacer("\r", "", "\n", "")
+ respBody = replacer.Replace(respBody)
+ expectedBody = replacer.Replace(expectedBody)
+ if respBody != expectedBody {
+ t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
+ }
+}
diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go
index 95f7e51b3..81e2f4ec8 100644
--- a/middleware/markdown/process.go
+++ b/middleware/markdown/process.go
@@ -9,6 +9,7 @@ import (
"strings"
"text/template"
+ "github.com/mholt/caddy/middleware"
"github.com/russross/blackfriday"
)
@@ -17,9 +18,14 @@ const (
DefaultStaticDir = "generated_site"
)
+type MarkdownData struct {
+ middleware.Context
+ Doc map[string]interface{}
+}
+
// 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) {
+func (md Markdown) Process(c Config, requestPath string, b []byte, ctx middleware.Context) ([]byte, error) {
var metadata = Metadata{Variables: make(map[string]interface{})}
var markdown []byte
var err error
@@ -61,14 +67,21 @@ func (md Markdown) Process(c Config, requestPath string, b []byte) ([]byte, erro
markdown = blackfriday.Markdown(markdown, c.Renderer, 0)
// set it as body for template
- metadata.Variables["markdown"] = string(markdown)
+ metadata.Variables["body"] = string(markdown)
+ title := metadata.Title
+ if title == "" {
+ title = filepath.Base(requestPath)
+ var extension = filepath.Ext(requestPath)
+ title = title[0 : len(title)-len(extension)]
+ }
+ metadata.Variables["title"] = title
- return md.processTemplate(c, requestPath, tmpl, metadata)
+ return md.processTemplate(c, requestPath, tmpl, metadata, ctx)
}
// 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) {
+func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, metadata Metadata, ctx middleware.Context) ([]byte, error) {
// if template is not specified,
// use the default template
if tmpl == nil {
@@ -81,7 +94,12 @@ func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, me
if err != nil {
return nil, err
}
- if err = t.Execute(b, metadata.Variables); err != nil {
+ mdData := MarkdownData{
+ Context: ctx,
+ Doc: metadata.Variables,
+ }
+
+ if err = t.Execute(b, mdData); err != nil {
return nil, err
}
@@ -148,15 +166,7 @@ func defaultTemplate(c Config, metadata Metadata, requestPath string) []byte {
}
// Title is first line (length-limited), otherwise filename
- title := metadata.Title
- if title == "" {
- title = filepath.Base(requestPath)
- if body, _ := metadata.Variables["markdown"].([]byte); len(body) > 128 {
- title = string(body[:128])
- } else if len(body) > 0 {
- title = string(body)
- }
- }
+ title, _ := metadata.Variables["title"].(string)
html := []byte(htmlTemplate)
html = bytes.Replace(html, []byte("{{title}}"), []byte(title), 1)
@@ -176,7 +186,7 @@ const (
{{js}}
- {{.markdown}}
+ {{.Doc.body}}