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.IsNotExist(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))
|
||||
}
|
||||
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 preparses 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()
|
||||
}
|
||||
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)
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
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 simple 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{' '})
|
||||
}
|
||||
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>`
|
||||
)
|
||||
Reference in New Issue
Block a user