mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-29 09:19:57 +00:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
192
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go
generated
vendored
Normal file
192
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package markdown is middleware to render markdown files as HTML
|
||||
// on-the-fly.
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// Markdown implements a layer of middleware that serves
|
||||
// markdown as HTML.
|
||||
type Markdown struct {
|
||||
// Server root
|
||||
Root string
|
||||
|
||||
// Jail the requests to site root with a mock file system
|
||||
FileSys http.FileSystem
|
||||
|
||||
// Next HTTP handler in the chain
|
||||
Next httpserver.Handler
|
||||
|
||||
// The list of markdown configurations
|
||||
Configs []*Config
|
||||
}
|
||||
|
||||
// Config stores markdown middleware configurations.
|
||||
type Config struct {
|
||||
// Markdown renderer
|
||||
Renderer blackfriday.Renderer
|
||||
|
||||
// Base path to match
|
||||
PathScope string
|
||||
|
||||
// List of extensions to consider as markdown files
|
||||
Extensions map[string]struct{}
|
||||
|
||||
// List of style sheets to load for each markdown file
|
||||
Styles []string
|
||||
|
||||
// List of JavaScript files to load for each markdown file
|
||||
Scripts []string
|
||||
|
||||
// The list of index files to try
|
||||
IndexFiles []string
|
||||
|
||||
// Template(s) to render with
|
||||
Template *template.Template
|
||||
|
||||
// a pair of template's name and its underlying file information
|
||||
TemplateFiles map[string]*cachedFileInfo
|
||||
}
|
||||
|
||||
type cachedFileInfo struct {
|
||||
path string
|
||||
fi os.FileInfo
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface.
|
||||
func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
var cfg *Config
|
||||
for _, c := range md.Configs {
|
||||
if httpserver.Path(r.URL.Path).Matches(c.PathScope) { // not negated
|
||||
cfg = c
|
||||
break // or goto
|
||||
}
|
||||
}
|
||||
if cfg == nil {
|
||||
return md.Next.ServeHTTP(w, r) // exit early
|
||||
}
|
||||
|
||||
// We only deal with HEAD/GET
|
||||
switch r.Method {
|
||||
case http.MethodGet, http.MethodHead:
|
||||
default:
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
||||
var dirents []os.FileInfo
|
||||
var lastModTime time.Time
|
||||
fpath := r.URL.Path
|
||||
if idx, ok := httpserver.IndexFile(md.FileSys, fpath, cfg.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)
|
||||
switch {
|
||||
case err == nil: // nop
|
||||
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
|
||||
}
|
||||
defer fdp.Close()
|
||||
|
||||
// Grab a possible set of directory entries. Note, we do not check
|
||||
// for errors here (unreadable directory, for example). It may
|
||||
// still be useful to have a directory template file, without the
|
||||
// directory contents being present. Note, the directory's last
|
||||
// modification is also present here (entry ".").
|
||||
dirents, _ = fdp.Readdir(-1)
|
||||
for _, d := range dirents {
|
||||
lastModTime = latest(lastModTime, d.ModTime())
|
||||
}
|
||||
|
||||
// Set path to found index file
|
||||
fpath = idx
|
||||
}
|
||||
|
||||
// If not supported extension, pass on it
|
||||
if _, ok := cfg.Extensions[path.Ext(fpath)]; !ok {
|
||||
return md.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// At this point we have a supported extension/markdown
|
||||
f, err := md.FileSys.Open(fpath)
|
||||
switch {
|
||||
case err == nil: // nop
|
||||
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
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fs, err := f.Stat()
|
||||
if err != nil {
|
||||
return http.StatusGone, nil
|
||||
}
|
||||
lastModTime = latest(lastModTime, fs.ModTime())
|
||||
|
||||
ctx := httpserver.NewContextWithHeader(w.Header())
|
||||
ctx.Root = md.FileSys
|
||||
ctx.Req = r
|
||||
ctx.URL = r.URL
|
||||
html, err := cfg.Markdown(title(fpath), f, dirents, ctx)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(html)))
|
||||
httpserver.SetLastModifiedHeader(w, lastModTime)
|
||||
if r.Method == http.MethodGet {
|
||||
w.Write(html)
|
||||
}
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// latest returns the latest time.Time
|
||||
func latest(t ...time.Time) time.Time {
|
||||
var last time.Time
|
||||
|
||||
for _, tt := range t {
|
||||
if tt.After(last) {
|
||||
last = tt
|
||||
}
|
||||
}
|
||||
|
||||
return last
|
||||
}
|
||||
|
||||
// title gives a backup generated title for a page
|
||||
func title(p string) string {
|
||||
return strings.TrimSuffix(path.Base(p), path.Ext(p))
|
||||
}
|
265
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown_test.go
generated
vendored
Normal file
265
vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown_test.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
func TestMarkdown(t *testing.T) {
|
||||
rootDir := "./testdata"
|
||||
|
||||
f := func(filename string) string {
|
||||
return filepath.ToSlash(rootDir + string(filepath.Separator) + filename)
|
||||
}
|
||||
|
||||
md := Markdown{
|
||||
Root: rootDir,
|
||||
FileSys: http.Dir(rootDir),
|
||||
Configs: []*Config{
|
||||
{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "", ""),
|
||||
PathScope: "/blog",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
},
|
||||
IndexFiles: []string{"index.md"},
|
||||
Styles: []string{},
|
||||
Scripts: []string{},
|
||||
Template: setDefaultTemplate(f("markdown_tpl.html")),
|
||||
},
|
||||
{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "", ""),
|
||||
PathScope: "/docflags",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
},
|
||||
IndexFiles: []string{"index.md"},
|
||||
Styles: []string{},
|
||||
Scripts: []string{},
|
||||
Template: setDefaultTemplate(f("docflags/template.txt")),
|
||||
},
|
||||
{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "", ""),
|
||||
PathScope: "/log",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
},
|
||||
IndexFiles: []string{"index.md"},
|
||||
Styles: []string{"/resources/css/log.css", "/resources/css/default.css"},
|
||||
Scripts: []string{"/resources/js/log.js", "/resources/js/default.js"},
|
||||
Template: GetDefaultTemplate(),
|
||||
},
|
||||
{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "", ""),
|
||||
PathScope: "/og",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
},
|
||||
IndexFiles: []string{"index.md"},
|
||||
Styles: []string{},
|
||||
Scripts: []string{},
|
||||
Template: setDefaultTemplate(f("markdown_tpl.html")),
|
||||
},
|
||||
},
|
||||
|
||||
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
t.Fatalf("Next shouldn't be called")
|
||||
return 0, nil
|
||||
}),
|
||||
}
|
||||
|
||||
get := func(url string) string {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create HTTP request: %v", err)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
code, err := md.ServeHTTP(rec, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, code)
|
||||
}
|
||||
return rec.Body.String()
|
||||
}
|
||||
|
||||
respBody := get("/blog/test.md")
|
||||
expectedBody := `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Markdown test 1</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Header for: Markdown test 1</h1>
|
||||
Welcome to A Caddy website!
|
||||
<h2>Welcome on the blog</h2>
|
||||
|
||||
<p>Body</p>
|
||||
|
||||
<pre><code class="language-go">func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
if respBody != expectedBody {
|
||||
t.Fatalf("Expected body:\n%q\ngot:\n%q", expectedBody, respBody)
|
||||
}
|
||||
|
||||
respBody = get("/docflags/test.md")
|
||||
expectedBody = `Doc.var_string hello
|
||||
Doc.var_bool true
|
||||
`
|
||||
|
||||
if respBody != expectedBody {
|
||||
t.Fatalf("Expected body:\n%q\ngot:\n%q", expectedBody, respBody)
|
||||
}
|
||||
|
||||
respBody = get("/log/test.md")
|
||||
expectedBody = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Markdown test 2</title>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link rel="stylesheet" href="/resources/css/log.css">
|
||||
<link rel="stylesheet" href="/resources/css/default.css">
|
||||
<script src="/resources/js/log.js"></script>
|
||||
<script src="/resources/js/default.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Welcome on the blog</h2>
|
||||
|
||||
<p>Body</p>
|
||||
|
||||
<pre><code class="language-go">func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
if respBody != expectedBody {
|
||||
t.Fatalf("Expected body:\n%q\ngot:\n%q", expectedBody, respBody)
|
||||
}
|
||||
|
||||
respBody = get("/og/first.md")
|
||||
expectedBody = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>first_post</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Header for: first_post</h1>
|
||||
Welcome to title!
|
||||
<h1>Test h1</h1>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
if respBody != expectedBody {
|
||||
t.Fatalf("Expected body:\n%q\ngot:\n%q", expectedBody, respBody)
|
||||
}
|
||||
}
|
||||
|
||||
func setDefaultTemplate(filename string) *template.Template {
|
||||
buf, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return template.Must(GetDefaultTemplate().Parse(string(buf)))
|
||||
}
|
||||
|
||||
func TestTemplateReload(t *testing.T) {
|
||||
const (
|
||||
templateFile = "testdata/test.html"
|
||||
targetFile = "testdata/hello.md"
|
||||
)
|
||||
c := caddy.NewTestController("http", `markdown {
|
||||
template `+templateFile+`
|
||||
}`)
|
||||
|
||||
err := ioutil.WriteFile(templateFile, []byte("hello {{.Doc.body}}"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile(targetFile, []byte("caddy"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
os.Remove(templateFile)
|
||||
os.Remove(targetFile)
|
||||
}()
|
||||
|
||||
config, err := markdownParse(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
md := Markdown{
|
||||
Root: "./testdata",
|
||||
FileSys: http.Dir("./testdata"),
|
||||
Configs: config,
|
||||
Next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
t.Fatalf("Next shouldn't be called")
|
||||
return 0, nil
|
||||
}),
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", "/hello.md", nil)
|
||||
get := func() string {
|
||||
rec := httptest.NewRecorder()
|
||||
code, err := md.ServeHTTP(rec, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if code != http.StatusOK {
|
||||
t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, code)
|
||||
}
|
||||
return rec.Body.String()
|
||||
}
|
||||
|
||||
if expect, got := "hello <p>caddy</p>\n", get(); expect != got {
|
||||
t.Fatalf("Expected body:\n%q\nbut got:\n%q", expect, got)
|
||||
}
|
||||
|
||||
// update template
|
||||
err = ioutil.WriteFile(templateFile, []byte("hi {{.Doc.body}}"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if expect, got := "hi <p>caddy</p>\n", get(); expect != got {
|
||||
t.Fatalf("Expected body:\n%q\nbut got:\n%q", expect, got)
|
||||
}
|
||||
|
||||
}
|
160
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata.go
generated
vendored
Normal file
160
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Date format YYYY-MM-DD HH:MM:SS or YYYY-MM-DD
|
||||
timeLayout = []string{
|
||||
`2006-01-02 15:04:05-0700`,
|
||||
`2006-01-02 15:04:05`,
|
||||
`2006-01-02`,
|
||||
}
|
||||
)
|
||||
|
||||
// Metadata stores a page's metadata
|
||||
type Metadata struct {
|
||||
// Page title
|
||||
Title string
|
||||
|
||||
// Page template
|
||||
Template string
|
||||
|
||||
// Publish date
|
||||
Date time.Time
|
||||
|
||||
// Variables to be used with Template
|
||||
Variables map[string]interface{}
|
||||
}
|
||||
|
||||
// NewMetadata returns a new Metadata struct, loaded with the given map
|
||||
func NewMetadata(parsedMap map[string]interface{}) Metadata {
|
||||
md := Metadata{
|
||||
Variables: make(map[string]interface{}),
|
||||
}
|
||||
md.load(parsedMap)
|
||||
|
||||
return md
|
||||
}
|
||||
|
||||
// load loads parsed values in parsedMap into Metadata
|
||||
func (m *Metadata) load(parsedMap map[string]interface{}) {
|
||||
|
||||
// Pull top level things out
|
||||
if title, ok := parsedMap["title"]; ok {
|
||||
m.Title, _ = title.(string)
|
||||
}
|
||||
if template, ok := parsedMap["template"]; ok {
|
||||
m.Template, _ = template.(string)
|
||||
}
|
||||
if date, ok := parsedMap["date"].(string); ok {
|
||||
for _, layout := range timeLayout {
|
||||
if t, err := time.Parse(layout, date); err == nil {
|
||||
m.Date = t
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.Variables = parsedMap
|
||||
}
|
||||
|
||||
// Parser is a an interface that must be satisfied by each parser
|
||||
type Parser interface {
|
||||
// Initialize a parser
|
||||
Init(b *bytes.Buffer) bool
|
||||
|
||||
// Type of metadata
|
||||
Type() string
|
||||
|
||||
// Parsed metadata.
|
||||
Metadata() Metadata
|
||||
|
||||
// Raw markdown.
|
||||
Markdown() []byte
|
||||
}
|
||||
|
||||
// GetParser returns a parser for the given data
|
||||
func GetParser(buf []byte) Parser {
|
||||
for _, p := range parsers() {
|
||||
b := bytes.NewBuffer(buf)
|
||||
if p.Init(b) {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsers returns all available parsers
|
||||
func parsers() []Parser {
|
||||
return []Parser{
|
||||
&TOMLParser{},
|
||||
&YAMLParser{},
|
||||
&JSONParser{},
|
||||
|
||||
// This one must be last
|
||||
&NoneParser{},
|
||||
}
|
||||
}
|
||||
|
||||
// Split out prefixed/suffixed 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
|
||||
}
|
70
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_json.go
generated
vendored
Normal file
70
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_json.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// JSONParser is the MetadataParser for JSON
|
||||
type JSONParser struct {
|
||||
metadata Metadata
|
||||
markdown *bytes.Buffer
|
||||
}
|
||||
|
||||
// Type returns the kind of metadata parser implemented by this struct.
|
||||
func (j *JSONParser) Type() string {
|
||||
return "JSON"
|
||||
}
|
||||
|
||||
// Init prepares the metadata metadata/markdown file and parses it
|
||||
func (j *JSONParser) Init(b *bytes.Buffer) bool {
|
||||
m := make(map[string]interface{})
|
||||
|
||||
err := json.Unmarshal(b.Bytes(), &m)
|
||||
if err != nil {
|
||||
var offset int
|
||||
|
||||
jerr, ok := err.(*json.SyntaxError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
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(m)
|
||||
j.markdown = bytes.NewBuffer(b.Bytes())
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Metadata returns parsed metadata. It should be called
|
||||
// only after a call to Parse returns without error.
|
||||
func (j *JSONParser) Metadata() Metadata {
|
||||
return j.metadata
|
||||
}
|
||||
|
||||
// Markdown returns the markdown text. It should be called only after a call to Parse returns without error.
|
||||
func (j *JSONParser) Markdown() []byte {
|
||||
return j.markdown.Bytes()
|
||||
}
|
56
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_none.go
generated
vendored
Normal file
56
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_none.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// NoneParser is the parser for plaintext markdown with no metadata.
|
||||
type NoneParser struct {
|
||||
metadata Metadata
|
||||
markdown *bytes.Buffer
|
||||
}
|
||||
|
||||
// Type returns the kind of parser this struct is.
|
||||
func (n *NoneParser) Type() string {
|
||||
return "None"
|
||||
}
|
||||
|
||||
// Init prepases and parses the metadata and markdown file
|
||||
func (n *NoneParser) Init(b *bytes.Buffer) bool {
|
||||
m := make(map[string]interface{})
|
||||
n.metadata = NewMetadata(m)
|
||||
n.markdown = bytes.NewBuffer(b.Bytes())
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Parse the metadata
|
||||
func (n *NoneParser) 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 *NoneParser) Metadata() Metadata {
|
||||
return n.metadata
|
||||
}
|
||||
|
||||
// Markdown returns parsed markdown. It should be called
|
||||
// only after a call to Parse returns without error.
|
||||
func (n *NoneParser) Markdown() []byte {
|
||||
return n.markdown.Bytes()
|
||||
}
|
297
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_test.go
generated
vendored
Normal file
297
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_test.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func check(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var TOML = [5]string{`
|
||||
title = "A title"
|
||||
template = "default"
|
||||
name = "value"
|
||||
positive = true
|
||||
negative = false
|
||||
number = 1410
|
||||
float = 1410.07
|
||||
`,
|
||||
`+++
|
||||
title = "A title"
|
||||
template = "default"
|
||||
name = "value"
|
||||
positive = true
|
||||
negative = false
|
||||
number = 1410
|
||||
float = 1410.07
|
||||
+++
|
||||
Page content
|
||||
`,
|
||||
`+++
|
||||
title = "A title"
|
||||
template = "default"
|
||||
name = "value"
|
||||
positive = true
|
||||
negative = false
|
||||
number = 1410
|
||||
float = 1410.07
|
||||
`,
|
||||
`title = "A title" template = "default" [variables] name = "value"`,
|
||||
`+++
|
||||
title = "A title"
|
||||
template = "default"
|
||||
name = "value"
|
||||
positive = true
|
||||
negative = false
|
||||
number = 1410
|
||||
float = 1410.07
|
||||
+++
|
||||
`,
|
||||
}
|
||||
|
||||
var YAML = [5]string{`
|
||||
title : A title
|
||||
template : default
|
||||
name : value
|
||||
positive : true
|
||||
negative : false
|
||||
number : 1410
|
||||
float : 1410.07
|
||||
`,
|
||||
`---
|
||||
title : A title
|
||||
template : default
|
||||
name : value
|
||||
positive : true
|
||||
negative : false
|
||||
number : 1410
|
||||
float : 1410.07
|
||||
---
|
||||
Page content
|
||||
`,
|
||||
`---
|
||||
title : A title
|
||||
template : default
|
||||
name : value
|
||||
number : 1410
|
||||
float : 1410.07
|
||||
`,
|
||||
`title : A title template : default variables : name : value : positive : true : negative : false`,
|
||||
`---
|
||||
title : A title
|
||||
template : default
|
||||
name : value
|
||||
positive : true
|
||||
negative : false
|
||||
number : 1410
|
||||
float : 1410.07
|
||||
---
|
||||
`,
|
||||
}
|
||||
|
||||
var JSON = [5]string{`
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"name" : "value",
|
||||
"positive" : true,
|
||||
"negative" : false,
|
||||
"number": 1410,
|
||||
"float": 1410.07
|
||||
`,
|
||||
`{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"name" : "value",
|
||||
"positive" : true,
|
||||
"negative" : false,
|
||||
"number" : 1410,
|
||||
"float": 1410.07
|
||||
}
|
||||
Page content
|
||||
`,
|
||||
`
|
||||
{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"name" : "value",
|
||||
"positive" : true,
|
||||
"negative" : false,
|
||||
"number" : 1410,
|
||||
"float": 1410.07
|
||||
`,
|
||||
`
|
||||
{
|
||||
"title" :: "A title",
|
||||
"template" : "default",
|
||||
"name" : "value",
|
||||
"positive" : true,
|
||||
"negative" : false,
|
||||
"number" : 1410,
|
||||
"float": 1410.07
|
||||
}
|
||||
`,
|
||||
`{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"name" : "value",
|
||||
"positive" : true,
|
||||
"negative" : false,
|
||||
"number" : 1410,
|
||||
"float": 1410.07
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
func TestParsers(t *testing.T) {
|
||||
expected := Metadata{
|
||||
Title: "A title",
|
||||
Template: "default",
|
||||
Variables: map[string]interface{}{
|
||||
"name": "value",
|
||||
"title": "A title",
|
||||
"template": "default",
|
||||
"number": 1410,
|
||||
"float": 1410.07,
|
||||
"positive": true,
|
||||
"negative": false,
|
||||
},
|
||||
}
|
||||
compare := func(m Metadata) bool {
|
||||
if m.Title != expected.Title {
|
||||
return false
|
||||
}
|
||||
if m.Template != expected.Template {
|
||||
return false
|
||||
}
|
||||
for k, v := range m.Variables {
|
||||
if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", expected.Variables[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
varLenOK := len(m.Variables) == len(expected.Variables)
|
||||
return varLenOK
|
||||
}
|
||||
|
||||
data := []struct {
|
||||
parser Parser
|
||||
testData [5]string
|
||||
name string
|
||||
}{
|
||||
{&JSONParser{}, JSON, "JSON"},
|
||||
{&YAMLParser{}, YAML, "YAML"},
|
||||
{&TOMLParser{}, TOML, "TOML"},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
// metadata without identifiers
|
||||
if v.parser.Init(bytes.NewBufferString(v.testData[0])) {
|
||||
t.Fatalf("Expected error for invalid metadata for %v", v.name)
|
||||
}
|
||||
|
||||
// metadata with identifiers
|
||||
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)
|
||||
}
|
||||
// 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 v.parser.Init(bytes.NewBufferString(v.testData[2])) {
|
||||
t.Fatalf("Expected error for missing closing identifier for %v parser", v.name)
|
||||
}
|
||||
|
||||
// invalid metadata
|
||||
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 !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"
|
||||
}
|
||||
|
||||
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 TOML = `+++
|
||||
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 YAML = `---
|
||||
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 {
|
||||
pType string
|
||||
testData string
|
||||
}{
|
||||
{"JSON", JSON},
|
||||
{"TOML", TOML},
|
||||
{"YAML", YAML},
|
||||
{"None", NONE},
|
||||
}
|
||||
for _, v := range data {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
60
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_toml.go
generated
vendored
Normal file
60
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_toml.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/naoina/toml"
|
||||
)
|
||||
|
||||
// TOMLParser is the Parser for TOML
|
||||
type TOMLParser struct {
|
||||
metadata Metadata
|
||||
markdown *bytes.Buffer
|
||||
}
|
||||
|
||||
// Type returns the kind of parser this struct is.
|
||||
func (t *TOMLParser) Type() string {
|
||||
return "TOML"
|
||||
}
|
||||
|
||||
// Init prepares and parses the metadata and markdown file itself
|
||||
func (t *TOMLParser) 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(m)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Metadata returns parsed metadata. It should be called
|
||||
// only after a call to Parse returns without error.
|
||||
func (t *TOMLParser) Metadata() Metadata {
|
||||
return t.metadata
|
||||
}
|
||||
|
||||
// Markdown returns parser markdown. It should be called only after a call to Parse returns without error.
|
||||
func (t *TOMLParser) Markdown() []byte {
|
||||
return t.markdown.Bytes()
|
||||
}
|
60
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_yaml.go
generated
vendored
Normal file
60
vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_yaml.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// YAMLParser is the Parser for YAML
|
||||
type YAMLParser struct {
|
||||
metadata Metadata
|
||||
markdown *bytes.Buffer
|
||||
}
|
||||
|
||||
// Type returns the kind of metadata parser.
|
||||
func (y *YAMLParser) Type() string {
|
||||
return "YAML"
|
||||
}
|
||||
|
||||
// Init prepares the metadata parser for parsing.
|
||||
func (y *YAMLParser) 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(m)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Metadata returns parsed metadata. It should be called
|
||||
// only after a call to Parse returns without error.
|
||||
func (y *YAMLParser) Metadata() Metadata {
|
||||
return y.metadata
|
||||
}
|
||||
|
||||
// Markdown renders the text as a byte array
|
||||
func (y *YAMLParser) Markdown() []byte {
|
||||
return y.markdown.Bytes()
|
||||
}
|
106
vendor/github.com/mholt/caddy/caddyhttp/markdown/process.go
generated
vendored
Normal file
106
vendor/github.com/mholt/caddy/caddyhttp/markdown/process.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"github.com/mholt/caddy/caddyhttp/markdown/metadata"
|
||||
"github.com/mholt/caddy/caddyhttp/markdown/summary"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// FileInfo represents a file in a particular server context. It wraps the os.FileInfo struct.
|
||||
type FileInfo struct {
|
||||
os.FileInfo
|
||||
ctx httpserver.Context
|
||||
}
|
||||
|
||||
var recognizedMetaTags = []string{
|
||||
"author",
|
||||
"copyright",
|
||||
"description",
|
||||
"subject",
|
||||
}
|
||||
|
||||
// Summarize returns an abbreviated string representation of the markdown stored in this file.
|
||||
// wordcount is the number of words returned in the summary.
|
||||
func (f FileInfo) Summarize(wordcount int) (string, error) {
|
||||
fp, err := f.ctx.Root.Open(f.Name())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
buf, err := ioutil.ReadAll(fp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(summary.Markdown(buf, wordcount)), nil
|
||||
}
|
||||
|
||||
// Markdown processes the contents of a page in b. It parses the metadata
|
||||
// (if any) and uses the template (if found).
|
||||
func (c *Config) Markdown(title string, r io.Reader, dirents []os.FileInfo, ctx httpserver.Context) ([]byte, error) {
|
||||
body, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := metadata.GetParser(body)
|
||||
markdown := parser.Markdown()
|
||||
mdata := parser.Metadata()
|
||||
|
||||
// process markdown
|
||||
extns := 0
|
||||
extns |= blackfriday.EXTENSION_TABLES
|
||||
extns |= blackfriday.EXTENSION_FENCED_CODE
|
||||
extns |= blackfriday.EXTENSION_STRIKETHROUGH
|
||||
extns |= blackfriday.EXTENSION_DEFINITION_LISTS
|
||||
html := blackfriday.Markdown(markdown, c.Renderer, extns)
|
||||
|
||||
// set it as body for template
|
||||
mdata.Variables["body"] = string(html)
|
||||
|
||||
// fixup title
|
||||
mdata.Variables["title"] = mdata.Title
|
||||
if mdata.Variables["title"] == "" {
|
||||
mdata.Variables["title"] = title
|
||||
}
|
||||
|
||||
// move available and valid front matters to the meta values
|
||||
meta := make(map[string]string)
|
||||
for _, val := range recognizedMetaTags {
|
||||
if mVal, ok := mdata.Variables[val]; ok {
|
||||
meta[val] = mVal.(string)
|
||||
}
|
||||
}
|
||||
|
||||
// massage possible files
|
||||
files := []FileInfo{}
|
||||
for _, ent := range dirents {
|
||||
file := FileInfo{
|
||||
FileInfo: ent,
|
||||
ctx: ctx,
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
|
||||
return execTemplate(c, mdata, meta, files, ctx)
|
||||
}
|
57
vendor/github.com/mholt/caddy/caddyhttp/markdown/process_test.go
generated
vendored
Normal file
57
vendor/github.com/mholt/caddy/caddyhttp/markdown/process_test.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func TestConfig_Markdown(t *testing.T) {
|
||||
tests := []map[string]string{
|
||||
{"author": "authorVal"},
|
||||
{"copyright": "copyrightVal"},
|
||||
{"description": "descriptionVal"},
|
||||
{"subject": "subjectVal"},
|
||||
{"author": "authorVal", "copyright": "copyrightVal"},
|
||||
{"author": "authorVal", "copyright": "copyrightVal", "description": "descriptionVal"},
|
||||
{"author": "authorVal", "copyright": "copyrightVal", "description": "descriptionVal", "subject": "subjectVal"},
|
||||
}
|
||||
|
||||
for i, meta := range tests {
|
||||
config := &Config{
|
||||
Template: GetDefaultTemplate(),
|
||||
}
|
||||
|
||||
toml := "+++"
|
||||
for key, val := range meta {
|
||||
toml = toml + "\n" + key + "= \"" + val + "\""
|
||||
}
|
||||
toml = toml + "\n+++"
|
||||
|
||||
res, _ := config.Markdown("Test title", strings.NewReader(toml), []os.FileInfo{}, httpserver.Context{})
|
||||
sRes := string(res)
|
||||
|
||||
for key, val := range meta {
|
||||
c := strings.Contains(sRes, "<meta name=\""+key+"\" content=\""+val+"\">")
|
||||
if !c {
|
||||
t.Error("Test case", i, "should contain meta", key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
179
vendor/github.com/mholt/caddy/caddyhttp/markdown/setup.go
generated
vendored
Normal file
179
vendor/github.com/mholt/caddy/caddyhttp/markdown/setup.go
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("markdown", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
// setup configures a new Markdown middleware instance.
|
||||
func setup(c *caddy.Controller) error {
|
||||
mdconfigs, err := markdownParse(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := httpserver.GetConfig(c)
|
||||
|
||||
md := Markdown{
|
||||
Root: cfg.Root,
|
||||
FileSys: http.Dir(cfg.Root),
|
||||
Configs: mdconfigs,
|
||||
}
|
||||
|
||||
cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
||||
md.Next = next
|
||||
return md
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func markdownParse(c *caddy.Controller) ([]*Config, error) {
|
||||
var mdconfigs []*Config
|
||||
|
||||
for c.Next() {
|
||||
md := &Config{
|
||||
Renderer: blackfriday.HtmlRenderer(0, "", ""),
|
||||
Extensions: make(map[string]struct{}),
|
||||
Template: GetDefaultTemplate(),
|
||||
IndexFiles: []string{},
|
||||
TemplateFiles: make(map[string]*cachedFileInfo),
|
||||
}
|
||||
|
||||
// Get the path scope
|
||||
args := c.RemainingArgs()
|
||||
switch len(args) {
|
||||
case 0:
|
||||
md.PathScope = "/"
|
||||
case 1:
|
||||
md.PathScope = args[0]
|
||||
default:
|
||||
return mdconfigs, c.ArgErr()
|
||||
}
|
||||
|
||||
// Load any other configuration parameters
|
||||
for c.NextBlock() {
|
||||
if err := loadParams(c, md); err != nil {
|
||||
return mdconfigs, err
|
||||
}
|
||||
}
|
||||
|
||||
// If no extensions were specified, assume some defaults
|
||||
if len(md.Extensions) == 0 {
|
||||
md.Extensions[".md"] = struct{}{}
|
||||
md.Extensions[".markdown"] = struct{}{}
|
||||
md.Extensions[".mdown"] = struct{}{}
|
||||
}
|
||||
|
||||
// Make a list of index files to match extensions
|
||||
for ext := range md.Extensions {
|
||||
md.IndexFiles = append(md.IndexFiles, "index"+ext)
|
||||
}
|
||||
mdconfigs = append(mdconfigs, md)
|
||||
}
|
||||
|
||||
return mdconfigs, nil
|
||||
}
|
||||
|
||||
func loadParams(c *caddy.Controller, mdc *Config) error {
|
||||
cfg := httpserver.GetConfig(c)
|
||||
|
||||
switch c.Val() {
|
||||
case "ext":
|
||||
for _, ext := range c.RemainingArgs() {
|
||||
mdc.Extensions[ext] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
case "css":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
mdc.Styles = append(mdc.Styles, c.Val())
|
||||
return nil
|
||||
case "js":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
mdc.Scripts = append(mdc.Scripts, c.Val())
|
||||
return nil
|
||||
case "template":
|
||||
tArgs := c.RemainingArgs()
|
||||
switch len(tArgs) {
|
||||
default:
|
||||
return c.ArgErr()
|
||||
case 1:
|
||||
fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[0]))
|
||||
|
||||
if err := SetTemplate(mdc.Template, "", fpath); err != nil {
|
||||
return c.Errf("default template parse error: %v", err)
|
||||
}
|
||||
|
||||
mdc.TemplateFiles[""] = &cachedFileInfo{
|
||||
path: fpath,
|
||||
}
|
||||
return nil
|
||||
case 2:
|
||||
fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[1]))
|
||||
|
||||
if err := SetTemplate(mdc.Template, tArgs[0], fpath); err != nil {
|
||||
return c.Errf("template parse error: %v", err)
|
||||
}
|
||||
|
||||
mdc.TemplateFiles[tArgs[0]] = &cachedFileInfo{
|
||||
path: fpath,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case "templatedir":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
|
||||
pattern := c.Val()
|
||||
_, err := mdc.Template.ParseGlob(pattern)
|
||||
if err != nil {
|
||||
return c.Errf("template load error: %v", err)
|
||||
}
|
||||
if c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
|
||||
paths, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return c.Errf("glob %q failed: %v", pattern, err)
|
||||
}
|
||||
for _, path := range paths {
|
||||
mdc.TemplateFiles[filepath.Base(path)] = &cachedFileInfo{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return c.Err("Expected valid markdown configuration property")
|
||||
}
|
||||
}
|
172
vendor/github.com/mholt/caddy/caddyhttp/markdown/setup_test.go
generated
vendored
Normal file
172
vendor/github.com/mholt/caddy/caddyhttp/markdown/setup_test.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
c := caddy.NewTestController("http", `markdown /blog`)
|
||||
err := setup(c)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no errors, got: %v", err)
|
||||
}
|
||||
mids := httpserver.GetConfig(c).Middleware()
|
||||
if len(mids) == 0 {
|
||||
t.Fatal("Expected middleware, got 0 instead")
|
||||
}
|
||||
|
||||
handler := mids[0](httpserver.EmptyNext)
|
||||
myHandler, ok := handler.(Markdown)
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("Expected handler to be type Markdown, got: %#v", handler)
|
||||
}
|
||||
|
||||
if myHandler.Configs[0].PathScope != "/blog" {
|
||||
t.Errorf("Expected /blog as the Path Scope")
|
||||
}
|
||||
if len(myHandler.Configs[0].Extensions) != 3 {
|
||||
t.Error("Expected 3 markdown extensions")
|
||||
}
|
||||
for _, key := range []string{".md", ".markdown", ".mdown"} {
|
||||
if ext, ok := myHandler.Configs[0].Extensions[key]; !ok {
|
||||
t.Errorf("Expected extensions to contain %v", ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkdownParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
inputMarkdownConfig string
|
||||
shouldErr bool
|
||||
expectedMarkdownConfig []Config
|
||||
}{
|
||||
|
||||
{`markdown /blog {
|
||||
ext .md .txt
|
||||
css /resources/css/blog.css
|
||||
js /resources/js/blog.js
|
||||
}`, false, []Config{{
|
||||
PathScope: "/blog",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
".txt": {},
|
||||
},
|
||||
Styles: []string{"/resources/css/blog.css"},
|
||||
Scripts: []string{"/resources/js/blog.js"},
|
||||
Template: GetDefaultTemplate(),
|
||||
TemplateFiles: make(map[string]*cachedFileInfo),
|
||||
}}},
|
||||
{`markdown /blog {
|
||||
ext .md
|
||||
template tpl_with_include.html
|
||||
}`, false, []Config{{
|
||||
PathScope: "/blog",
|
||||
Extensions: map[string]struct{}{
|
||||
".md": {},
|
||||
},
|
||||
Template: setDefaultTemplate("./testdata/tpl_with_include.html"),
|
||||
TemplateFiles: map[string]*cachedFileInfo{
|
||||
"": {path: "testdata/tpl_with_include.html"},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("http", test.inputMarkdownConfig)
|
||||
httpserver.GetConfig(c).Root = "./testdata"
|
||||
actualMarkdownConfigs, err := markdownParse(c)
|
||||
|
||||
if err == nil && test.shouldErr {
|
||||
t.Errorf("Test %d didn't error, but it should have", i)
|
||||
} else if err != nil && !test.shouldErr {
|
||||
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
|
||||
}
|
||||
if len(actualMarkdownConfigs) != len(test.expectedMarkdownConfig) {
|
||||
t.Fatalf("Test %d expected %d no of WebSocket configs, but got %d ",
|
||||
i, len(test.expectedMarkdownConfig), len(actualMarkdownConfigs))
|
||||
}
|
||||
for j, actualMarkdownConfig := range actualMarkdownConfigs {
|
||||
|
||||
if actualMarkdownConfig.PathScope != test.expectedMarkdownConfig[j].PathScope {
|
||||
t.Errorf("Test %d expected %dth Markdown PathScope to be %s , but got %s",
|
||||
i, j, test.expectedMarkdownConfig[j].PathScope, actualMarkdownConfig.PathScope)
|
||||
}
|
||||
|
||||
if fmt.Sprint(actualMarkdownConfig.Styles) != fmt.Sprint(test.expectedMarkdownConfig[j].Styles) {
|
||||
t.Errorf("Test %d expected %dth Markdown Config Styles to be %s , but got %s",
|
||||
i, j, fmt.Sprint(test.expectedMarkdownConfig[j].Styles), fmt.Sprint(actualMarkdownConfig.Styles))
|
||||
}
|
||||
if fmt.Sprint(actualMarkdownConfig.Scripts) != fmt.Sprint(test.expectedMarkdownConfig[j].Scripts) {
|
||||
t.Errorf("Test %d expected %dth Markdown Config Scripts to be %s , but got %s",
|
||||
i, j, fmt.Sprint(test.expectedMarkdownConfig[j].Scripts), fmt.Sprint(actualMarkdownConfig.Scripts))
|
||||
}
|
||||
if ok, tx, ty := equalTemplates(actualMarkdownConfig.Template, test.expectedMarkdownConfig[j].Template); !ok {
|
||||
t.Errorf("Test %d the %dth Markdown Config Templates did not match, expected %s to be %s", i, j, tx, ty)
|
||||
}
|
||||
if expect, got := test.expectedMarkdownConfig[j].TemplateFiles, actualMarkdownConfig.TemplateFiles; !reflect.DeepEqual(expect, got) {
|
||||
t.Errorf("Test %d the %d Markdown config TemplateFiles did not match, expect %v, but got %v", i, j, expect, got)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalTemplates(i, j *template.Template) (bool, string, string) {
|
||||
// Just in case :)
|
||||
if i == j {
|
||||
return true, "", ""
|
||||
}
|
||||
|
||||
// We can't do much here, templates can't really be compared. However,
|
||||
// we can execute the templates and compare their outputs to be reasonably
|
||||
// sure that they're the same.
|
||||
|
||||
// This is exceedingly ugly.
|
||||
ctx := httpserver.Context{
|
||||
Root: http.Dir("./testdata"),
|
||||
}
|
||||
|
||||
md := Data{
|
||||
Context: ctx,
|
||||
Doc: make(map[string]interface{}),
|
||||
Styles: []string{"style1"},
|
||||
Scripts: []string{"js1"},
|
||||
}
|
||||
md.Doc["title"] = "some title"
|
||||
md.Doc["body"] = "some body"
|
||||
|
||||
bufi := new(bytes.Buffer)
|
||||
bufj := new(bytes.Buffer)
|
||||
|
||||
if err := i.Execute(bufi, md); err != nil {
|
||||
return false, fmt.Sprintf("%v", err), ""
|
||||
}
|
||||
if err := j.Execute(bufj, md); err != nil {
|
||||
return false, "", fmt.Sprintf("%v", err)
|
||||
}
|
||||
|
||||
return bytes.Equal(bufi.Bytes(), bufj.Bytes()), string(bufi.Bytes()), string(bufj.Bytes())
|
||||
}
|
167
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/render.go
generated
vendored
Normal file
167
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/render.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package summary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// Ensure we implement the Blackfriday Markdown Renderer interface
|
||||
var _ blackfriday.Renderer = (*renderer)(nil)
|
||||
|
||||
// renderer renders Markdown to plain-text meant for listings and excerpts,
|
||||
// and implements the blackfriday.Renderer interface.
|
||||
//
|
||||
// Many of the methods are stubs with no output to prevent output of HTML markup.
|
||||
type renderer struct{}
|
||||
|
||||
// Blocklevel callbacks
|
||||
|
||||
// BlockCode is the code tag callback.
|
||||
func (r renderer) BlockCode(out *bytes.Buffer, text []byte, land string) {}
|
||||
|
||||
// BlockQuote is the quote tag callback.
|
||||
func (r renderer) BlockQuote(out *bytes.Buffer, text []byte) {}
|
||||
|
||||
// BlockHtml is the HTML tag callback.
|
||||
func (r renderer) BlockHtml(out *bytes.Buffer, text []byte) {}
|
||||
|
||||
// Header is the header tag callback.
|
||||
func (r renderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {}
|
||||
|
||||
// HRule is the horizontal rule tag callback.
|
||||
func (r renderer) HRule(out *bytes.Buffer) {}
|
||||
|
||||
// List is the list tag callback.
|
||||
func (r renderer) List(out *bytes.Buffer, text func() bool, flags int) {
|
||||
// TODO: This is not desired (we'd rather not write lists as part of summary),
|
||||
// but see this issue: https://github.com/russross/blackfriday/issues/189
|
||||
marker := out.Len()
|
||||
if !text() {
|
||||
out.Truncate(marker)
|
||||
}
|
||||
out.Write([]byte{' '})
|
||||
}
|
||||
|
||||
// ListItem is the list item tag callback.
|
||||
func (r renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {}
|
||||
|
||||
// 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) {
|
||||
marker := out.Len()
|
||||
if !text() {
|
||||
out.Truncate(marker)
|
||||
}
|
||||
out.Write([]byte{' '})
|
||||
}
|
||||
|
||||
// Table is the table tag callback.
|
||||
func (r renderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {}
|
||||
|
||||
// TableRow is the table row tag callback.
|
||||
func (r renderer) TableRow(out *bytes.Buffer, text []byte) {}
|
||||
|
||||
// TableHeaderCell is the table header cell tag callback.
|
||||
func (r renderer) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {}
|
||||
|
||||
// TableCell is the table cell tag callback.
|
||||
func (r renderer) TableCell(out *bytes.Buffer, text []byte, flags int) {}
|
||||
|
||||
// Footnotes is the foot notes tag callback.
|
||||
func (r renderer) Footnotes(out *bytes.Buffer, text func() bool) {}
|
||||
|
||||
// FootnoteItem is the footnote item tag callback.
|
||||
func (r renderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {}
|
||||
|
||||
// TitleBlock is the title tag callback.
|
||||
func (r renderer) TitleBlock(out *bytes.Buffer, text []byte) {}
|
||||
|
||||
// Spanlevel callbacks
|
||||
|
||||
// AutoLink is the autolink tag callback.
|
||||
func (r renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {}
|
||||
|
||||
// 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) {
|
||||
out.Write([]byte("`"))
|
||||
out.Write(text)
|
||||
out.Write([]byte("`"))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
out.Write(text)
|
||||
}
|
||||
|
||||
// Emphasis is the emphasis tag callback. Outputs a simple plain-text
|
||||
// version of the input.
|
||||
func (r renderer) Emphasis(out *bytes.Buffer, text []byte) {
|
||||
out.Write(text)
|
||||
}
|
||||
|
||||
// Image is the image tag callback.
|
||||
func (r renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {}
|
||||
|
||||
// LineBreak is the line break tag callback.
|
||||
func (r renderer) LineBreak(out *bytes.Buffer) {}
|
||||
|
||||
// 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) {
|
||||
out.Write(content)
|
||||
}
|
||||
|
||||
// RawHtmlTag is the raw HTML tag callback.
|
||||
func (r renderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {}
|
||||
|
||||
// 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) {
|
||||
out.Write(text)
|
||||
}
|
||||
|
||||
// StrikeThrough is the strikethrough tag callback.
|
||||
func (r renderer) StrikeThrough(out *bytes.Buffer, text []byte) {}
|
||||
|
||||
// FootnoteRef is the footnote ref tag callback.
|
||||
func (r renderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {}
|
||||
|
||||
// Lowlevel callbacks
|
||||
|
||||
// Entity callback. Outputs a simple plain-text version of the input.
|
||||
func (r renderer) Entity(out *bytes.Buffer, entity []byte) {
|
||||
out.Write(entity)
|
||||
}
|
||||
|
||||
// NormalText callback. Outputs a simple plain-text version of the input.
|
||||
func (r renderer) NormalText(out *bytes.Buffer, text []byte) {
|
||||
out.Write(text)
|
||||
}
|
||||
|
||||
// Header and footer
|
||||
|
||||
// DocumentHeader callback.
|
||||
func (r renderer) DocumentHeader(out *bytes.Buffer) {}
|
||||
|
||||
// DocumentFooter callback.
|
||||
func (r renderer) DocumentFooter(out *bytes.Buffer) {}
|
||||
|
||||
// GetFlags returns zero.
|
||||
func (r renderer) GetFlags() int { return 0 }
|
31
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary.go
generated
vendored
Normal file
31
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package summary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"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 {
|
||||
words := bytes.Fields(blackfriday.Markdown(input, renderer{}, 0))
|
||||
if wordcount > len(words) {
|
||||
wordcount = len(words)
|
||||
}
|
||||
return bytes.Join(words[0:wordcount], []byte{' '})
|
||||
}
|
25
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary_test.go
generated
vendored
Normal file
25
vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary_test.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package summary
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMarkdown(t *testing.T) {
|
||||
input := []byte(`Testing with just a few words.`)
|
||||
got := string(Markdown(input, 3))
|
||||
if want := "Testing with just"; want != got {
|
||||
t.Errorf("Expected '%s' but got '%s'", want, got)
|
||||
}
|
||||
}
|
162
vendor/github.com/mholt/caddy/caddyhttp/markdown/template.go
generated
vendored
Normal file
162
vendor/github.com/mholt/caddy/caddyhttp/markdown/template.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
"github.com/mholt/caddy/caddyhttp/markdown/metadata"
|
||||
)
|
||||
|
||||
// Data represents a markdown document.
|
||||
type Data struct {
|
||||
httpserver.Context
|
||||
Doc map[string]interface{}
|
||||
Styles []string
|
||||
Scripts []string
|
||||
Meta map[string]string
|
||||
Files []FileInfo
|
||||
}
|
||||
|
||||
// Include "overrides" the embedded httpserver.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, args ...interface{}) (string, error) {
|
||||
d.Args = args
|
||||
return httpserver.ContextInclude(filename, d, d.Root)
|
||||
}
|
||||
|
||||
var templateUpdateMu sync.RWMutex
|
||||
|
||||
// execTemplate executes a template given a requestPath, template, and metadata
|
||||
func execTemplate(c *Config, mdata metadata.Metadata, meta map[string]string, files []FileInfo, ctx httpserver.Context) ([]byte, error) {
|
||||
mdData := Data{
|
||||
Context: ctx,
|
||||
Doc: mdata.Variables,
|
||||
Styles: c.Styles,
|
||||
Scripts: c.Scripts,
|
||||
Meta: meta,
|
||||
Files: files,
|
||||
}
|
||||
templateName := mdata.Template
|
||||
|
||||
updateTemplate := func() error {
|
||||
templateUpdateMu.Lock()
|
||||
defer templateUpdateMu.Unlock()
|
||||
|
||||
templateFile, ok := c.TemplateFiles[templateName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
currentFileInfo, err := os.Lstat(templateFile.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fileChanged(currentFileInfo, templateFile.fi) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// update template due to file changes
|
||||
err = SetTemplate(c.Template, templateName, templateFile.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
templateFile.fi = currentFileInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateTemplate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
templateUpdateMu.RLock()
|
||||
defer templateUpdateMu.RUnlock()
|
||||
if err := c.Template.ExecuteTemplate(b, templateName, mdData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func fileChanged(new, old os.FileInfo) bool {
|
||||
// never checked before
|
||||
if old == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if new.Size() != old.Size() ||
|
||||
new.Mode() != old.Mode() ||
|
||||
new.ModTime() != old.ModTime() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetTemplate reads in the template with the filename provided. If the file does not exist or is not parsable, it will return an error.
|
||||
func SetTemplate(t *template.Template, name, filename string) error {
|
||||
|
||||
// Read template
|
||||
buf, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update if exists
|
||||
if tt := t.Lookup(name); tt != nil {
|
||||
_, err = tt.Parse(string(buf))
|
||||
return err
|
||||
}
|
||||
|
||||
// Allocate new name if not
|
||||
_, err = t.New(name).Parse(string(buf))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDefaultTemplate returns the default template.
|
||||
func GetDefaultTemplate() *template.Template {
|
||||
return template.Must(template.New("").Parse(defaultTemplate))
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTemplate = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Doc.title}}</title>
|
||||
<meta charset="utf-8">
|
||||
{{range $key, $val := .Meta}}
|
||||
<meta name="{{$key}}" content="{{$val}}">
|
||||
{{end}}
|
||||
{{- range .Styles}}
|
||||
<link rel="stylesheet" href="{{.}}">
|
||||
{{- end}}
|
||||
{{- range .Scripts}}
|
||||
<script src="{{.}}"></script>
|
||||
{{- end}}
|
||||
</head>
|
||||
<body>
|
||||
{{.Doc.body}}
|
||||
</body>
|
||||
</html>`
|
||||
)
|
14
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/blog/test.md
generated
vendored
Normal file
14
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/blog/test.md
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Markdown test 1
|
||||
sitename: A Caddy website
|
||||
---
|
||||
|
||||
## Welcome on the blog
|
||||
|
||||
Body
|
||||
|
||||
``` go
|
||||
func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
```
|
2
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/docflags/template.txt
generated
vendored
Normal file
2
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/docflags/template.txt
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Doc.var_string {{.Doc.var_string}}
|
||||
Doc.var_bool {{.Doc.var_bool}}
|
4
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/docflags/test.md
generated
vendored
Normal file
4
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/docflags/test.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
var_string: hello
|
||||
var_bool: true
|
||||
---
|
1
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/header.html
generated
vendored
Normal file
1
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/header.html
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Header for: {{.Doc.title}}</h1>
|
14
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/log/test.md
generated
vendored
Normal file
14
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/log/test.md
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Markdown test 2
|
||||
sitename: A Caddy website
|
||||
---
|
||||
|
||||
## Welcome on the blog
|
||||
|
||||
Body
|
||||
|
||||
``` go
|
||||
func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
```
|
11
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/markdown_tpl.html
generated
vendored
Normal file
11
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/markdown_tpl.html
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Doc.title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
{{.Include "header.html"}}
|
||||
Welcome to {{.Doc.sitename}}!
|
||||
{{.Doc.body}}
|
||||
</body>
|
||||
</html>
|
5
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/og/first.md
generated
vendored
Normal file
5
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/og/first.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: first_post
|
||||
sitename: title
|
||||
---
|
||||
# Test h1
|
11
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/tpl_with_include.html
generated
vendored
Normal file
11
vendor/github.com/mholt/caddy/caddyhttp/markdown/testdata/tpl_with_include.html
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Doc.title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
Welcome to {{.Doc.sitename}}!
|
||||
<br><br>
|
||||
{{.Doc.body}}
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user