mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
markdown: documentation done. awaiting test
This commit is contained in:
parent
2c7de8f328
commit
0fccd3707d
3 changed files with 91 additions and 68 deletions
|
@ -61,10 +61,10 @@ type Config struct {
|
||||||
// Map of registered templates
|
// Map of registered templates
|
||||||
Templates map[string]string
|
Templates map[string]string
|
||||||
|
|
||||||
// Static files
|
// Map of request URL to static files generated
|
||||||
StaticFiles map[string]string
|
StaticFiles map[string]string
|
||||||
|
|
||||||
// Static files directory
|
// Directory to store static files
|
||||||
StaticDir string
|
StaticDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,17 @@ var (
|
||||||
|
|
||||||
// Metadata stores a page's metadata
|
// Metadata stores a page's metadata
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
Title string
|
// Page title
|
||||||
Template string
|
Title string
|
||||||
|
|
||||||
|
// Page template
|
||||||
|
Template string
|
||||||
|
|
||||||
|
// Variables to be used with Template
|
||||||
Variables map[string]interface{}
|
Variables map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads parsed metadata into m
|
// load loads parsed values in parsedMap into Metadata
|
||||||
func (m *Metadata) load(parsedMap map[string]interface{}) {
|
func (m *Metadata) load(parsedMap map[string]interface{}) {
|
||||||
if template, ok := parsedMap["title"]; ok {
|
if template, ok := parsedMap["title"]; ok {
|
||||||
m.Title, _ = template.(string)
|
m.Title, _ = template.(string)
|
||||||
|
@ -35,14 +40,19 @@ func (m *Metadata) load(parsedMap map[string]interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetadataParser parses the page metadata
|
// MetadataParser is a an interface that must be satisfied by each parser
|
||||||
// into Metadata
|
|
||||||
type MetadataParser interface {
|
type MetadataParser interface {
|
||||||
// Identifiers
|
// Opening identifier
|
||||||
Opening() []byte
|
Opening() []byte
|
||||||
|
|
||||||
|
// Closing identifier
|
||||||
Closing() []byte
|
Closing() []byte
|
||||||
|
|
||||||
|
// Parse the metadata
|
||||||
Parse([]byte) error
|
Parse([]byte) error
|
||||||
|
|
||||||
|
// Parsed metadata.
|
||||||
|
// Should be called after a call to Parse returns no error
|
||||||
Metadata() Metadata
|
Metadata() Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +61,7 @@ type JSONMetadataParser struct {
|
||||||
metadata Metadata
|
metadata Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses b into metadata
|
// Parse the metadata
|
||||||
func (j *JSONMetadataParser) Parse(b []byte) error {
|
func (j *JSONMetadataParser) Parse(b []byte) error {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
if err := json.Unmarshal(b, &m); err != nil {
|
if err := json.Unmarshal(b, &m); err != nil {
|
||||||
|
@ -61,7 +71,8 @@ func (j *JSONMetadataParser) Parse(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata returns the metadata parsed by this parser
|
// Parsed metadata.
|
||||||
|
// Should be called after a call to Parse returns no error
|
||||||
func (j *JSONMetadataParser) Metadata() Metadata {
|
func (j *JSONMetadataParser) Metadata() Metadata {
|
||||||
return j.metadata
|
return j.metadata
|
||||||
}
|
}
|
||||||
|
@ -81,7 +92,7 @@ type TOMLMetadataParser struct {
|
||||||
metadata Metadata
|
metadata Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses b into metadata
|
// Parse the metadata
|
||||||
func (t *TOMLMetadataParser) Parse(b []byte) error {
|
func (t *TOMLMetadataParser) Parse(b []byte) error {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
if err := toml.Unmarshal(b, &m); err != nil {
|
if err := toml.Unmarshal(b, &m); err != nil {
|
||||||
|
@ -91,7 +102,8 @@ func (t *TOMLMetadataParser) Parse(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata returns the metadata parsed by this parser
|
// Parsed metadata.
|
||||||
|
// Should be called after a call to Parse returns no error
|
||||||
func (t *TOMLMetadataParser) Metadata() Metadata {
|
func (t *TOMLMetadataParser) Metadata() Metadata {
|
||||||
return t.metadata
|
return t.metadata
|
||||||
}
|
}
|
||||||
|
@ -106,12 +118,12 @@ func (t *TOMLMetadataParser) Closing() []byte {
|
||||||
return []byte("+++")
|
return []byte("+++")
|
||||||
}
|
}
|
||||||
|
|
||||||
// YAMLMetadataParser is the MetdataParser for YAML
|
// YAMLMetadataParser is the MetadataParser for YAML
|
||||||
type YAMLMetadataParser struct {
|
type YAMLMetadataParser struct {
|
||||||
metadata Metadata
|
metadata Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses b into metadata
|
// Parse the metadata
|
||||||
func (y *YAMLMetadataParser) Parse(b []byte) error {
|
func (y *YAMLMetadataParser) Parse(b []byte) error {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
if err := yaml.Unmarshal(b, &m); err != nil {
|
if err := yaml.Unmarshal(b, &m); err != nil {
|
||||||
|
@ -121,7 +133,8 @@ func (y *YAMLMetadataParser) Parse(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata returns the metadata parsed by this parser
|
// Parsed metadata.
|
||||||
|
// Should be called after a call to Parse returns no error
|
||||||
func (y *YAMLMetadataParser) Metadata() Metadata {
|
func (y *YAMLMetadataParser) Metadata() Metadata {
|
||||||
return y.metadata
|
return y.metadata
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ const (
|
||||||
StaticDir = ".caddy_static"
|
StaticDir = ".caddy_static"
|
||||||
)
|
)
|
||||||
|
|
||||||
// process the contents of a page.
|
// process processes the contents of a page.
|
||||||
// It parses the metadata if any and uses the template if found
|
// It parses the metadata (if any) and uses the template (if found)
|
||||||
func (md Markdown) process(c Config, fpath string, b []byte) ([]byte, error) {
|
func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, error) {
|
||||||
metadata, markdown, err := extractMetadata(b)
|
metadata, markdown, err := extractMetadata(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -46,10 +46,11 @@ func (md Markdown) process(c Config, fpath string, b []byte) ([]byte, error) {
|
||||||
|
|
||||||
// process markdown
|
// process markdown
|
||||||
markdown = blackfriday.Markdown(markdown, c.Renderer, 0)
|
markdown = blackfriday.Markdown(markdown, c.Renderer, 0)
|
||||||
|
|
||||||
// set it as body for template
|
// set it as body for template
|
||||||
metadata.Variables["body"] = string(markdown)
|
metadata.Variables["body"] = string(markdown)
|
||||||
|
|
||||||
return md.processTemplate(c, fpath, tmpl, metadata)
|
return md.processTemplate(c, requestPath, tmpl, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractMetadata extracts metadata content from a page.
|
// extractMetadata extracts metadata content from a page.
|
||||||
|
@ -60,12 +61,12 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
|
||||||
reader := bytes.NewBuffer(b)
|
reader := bytes.NewBuffer(b)
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
var parser MetadataParser
|
var parser MetadataParser
|
||||||
// if scanner.Scan() &&
|
|
||||||
// Read first line
|
// Read first line
|
||||||
if scanner.Scan() {
|
if scanner.Scan() {
|
||||||
line := scanner.Bytes()
|
line := scanner.Bytes()
|
||||||
parser = findParser(line)
|
parser = findParser(line)
|
||||||
// if no parser found
|
// if no parser found,
|
||||||
// assume metadata not present
|
// assume metadata not present
|
||||||
if parser == nil {
|
if parser == nil {
|
||||||
return metadata, b, nil
|
return metadata, b, nil
|
||||||
|
@ -78,7 +79,8 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
|
||||||
// Read remaining lines until closing identifier is found
|
// Read remaining lines until closing identifier is found
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Bytes()
|
line := scanner.Bytes()
|
||||||
// closing identifier found
|
|
||||||
|
// if closing identifier found
|
||||||
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
|
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
|
||||||
// parse the metadata
|
// parse the metadata
|
||||||
err := parser.Parse(buf.Bytes())
|
err := parser.Parse(buf.Bytes())
|
||||||
|
@ -97,10 +99,12 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
|
||||||
buf.Write(line)
|
buf.Write(line)
|
||||||
buf.WriteString("\r\n")
|
buf.WriteString("\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// closing identifier not found
|
||||||
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
|
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// findParser locates the parser for an opening identifier
|
// findParser finds the parser using line that contains opening identifier
|
||||||
func findParser(line []byte) MetadataParser {
|
func findParser(line []byte) MetadataParser {
|
||||||
line = bytes.TrimSpace(line)
|
line = bytes.TrimSpace(line)
|
||||||
for _, parser := range parsers {
|
for _, parser := range parsers {
|
||||||
|
@ -111,13 +115,16 @@ func findParser(line []byte) MetadataParser {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata Metadata) ([]byte, error) {
|
// processTemplate processes a template given a requestPath,
|
||||||
// if template is specified
|
// template (tmpl) and metadata
|
||||||
// replace parse the template
|
func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, metadata Metadata) ([]byte, error) {
|
||||||
if tmpl != nil {
|
// if template is not specified,
|
||||||
tmpl = defaultTemplate(c, metadata, fpath)
|
// use the default template
|
||||||
|
if tmpl == nil {
|
||||||
|
tmpl = defaultTemplate(c, metadata, requestPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process the template
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
t, err := template.New("").Parse(string(tmpl))
|
t, err := template.New("").Parse(string(tmpl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -128,7 +135,7 @@ func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate static page
|
// generate static page
|
||||||
if err = md.generatePage(c, fpath, b.Bytes()); err != nil {
|
if err = md.generatePage(c, requestPath, b.Bytes()); err != nil {
|
||||||
// if static page generation fails,
|
// if static page generation fails,
|
||||||
// nothing fatal, only log the error.
|
// nothing fatal, only log the error.
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -138,8 +145,47 @@ func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultTemplate(c Config, metadata Metadata, fpath string) []byte {
|
// generatePage generates a static html page from the markdown in content.
|
||||||
// else, use default template
|
func (md Markdown) generatePage(c Config, requestPath string, content []byte) error {
|
||||||
|
// should not happen,
|
||||||
|
// must be set on Markdown init.
|
||||||
|
if c.StaticDir == "" {
|
||||||
|
return fmt.Errorf("Static directory not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if static directory is not existing, create it
|
||||||
|
if _, err := os.Stat(c.StaticDir); err != nil {
|
||||||
|
err := os.MkdirAll(c.StaticDir, os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Join(c.StaticDir, requestPath)
|
||||||
|
|
||||||
|
// If it is index file, use the directory instead
|
||||||
|
if md.IsIndexFile(filepath.Base(requestPath)) {
|
||||||
|
filePath, _ = filepath.Split(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the directory in case it is not existing
|
||||||
|
if err := os.MkdirAll(filePath, os.FileMode(0755)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate index.html file in the directory
|
||||||
|
filePath = filepath.Join(filePath, "index.html")
|
||||||
|
err := ioutil.WriteFile(filePath, content, os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.StaticFiles[requestPath] = filePath
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultTemplate constructs a default template.
|
||||||
|
func defaultTemplate(c Config, metadata Metadata, requestPath string) []byte {
|
||||||
var scripts, styles bytes.Buffer
|
var scripts, styles bytes.Buffer
|
||||||
for _, style := range c.Styles {
|
for _, style := range c.Styles {
|
||||||
styles.WriteString(strings.Replace(cssTemplate, "{{url}}", style, 1))
|
styles.WriteString(strings.Replace(cssTemplate, "{{url}}", style, 1))
|
||||||
|
@ -153,7 +199,7 @@ func defaultTemplate(c Config, metadata Metadata, fpath string) []byte {
|
||||||
// Title is first line (length-limited), otherwise filename
|
// Title is first line (length-limited), otherwise filename
|
||||||
title := metadata.Title
|
title := metadata.Title
|
||||||
if title == "" {
|
if title == "" {
|
||||||
title = filepath.Base(fpath)
|
title = filepath.Base(requestPath)
|
||||||
if body, _ := metadata.Variables["body"].([]byte); len(body) > 128 {
|
if body, _ := metadata.Variables["body"].([]byte); len(body) > 128 {
|
||||||
title = string(body[:128])
|
title = string(body[:128])
|
||||||
} else if len(body) > 0 {
|
} else if len(body) > 0 {
|
||||||
|
@ -169,42 +215,6 @@ func defaultTemplate(c Config, metadata Metadata, fpath string) []byte {
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md Markdown) generatePage(c Config, fpath string, content []byte) error {
|
|
||||||
// should not happen
|
|
||||||
// must be set on init
|
|
||||||
if c.StaticDir == "" {
|
|
||||||
return fmt.Errorf("Static directory not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// if static directory is not existing, create it
|
|
||||||
if _, err := os.Stat(c.StaticDir); err != nil {
|
|
||||||
err := os.MkdirAll(c.StaticDir, os.FileMode(0755))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath := filepath.Join(c.StaticDir, fpath)
|
|
||||||
|
|
||||||
// If it is index file, use the directory instead
|
|
||||||
if md.IsIndexFile(filepath.Base(fpath)) {
|
|
||||||
filePath, _ = filepath.Split(filePath)
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(filePath, os.FileMode(0755)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate index.html file in the directory
|
|
||||||
filePath = filepath.Join(filePath, "index.html")
|
|
||||||
err := ioutil.WriteFile(filePath, content, os.FileMode(0755))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.StaticFiles[fpath] = filePath
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
htmlTemplate = `<!DOCTYPE html>
|
htmlTemplate = `<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
Loading…
Reference in a new issue