mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	Merge branch 'master' of https://github.com/mholt/caddy
This commit is contained in:
		
						commit
						ec51e14451
					
				@ -1,6 +1,7 @@
 | 
				
			|||||||
package setup
 | 
					package setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/mholt/caddy/middleware/fastcgi"
 | 
						"github.com/mholt/caddy/middleware/fastcgi"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -34,3 +35,63 @@ func TestFastCGI(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFastcgiParse(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							inputFastcgiConfig    string
 | 
				
			||||||
 | 
							shouldErr             bool
 | 
				
			||||||
 | 
							expectedFastcgiConfig []fastcgi.Rule
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{`fastcgi /blog 127.0.0.1:9000 php`,
 | 
				
			||||||
 | 
								false, []fastcgi.Rule{{
 | 
				
			||||||
 | 
									Path:       "/blog",
 | 
				
			||||||
 | 
									Address:    "127.0.0.1:9000",
 | 
				
			||||||
 | 
									Ext:        ".php",
 | 
				
			||||||
 | 
									SplitPath:  ".php",
 | 
				
			||||||
 | 
									IndexFiles: []string{"index.php"},
 | 
				
			||||||
 | 
								}}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, test := range tests {
 | 
				
			||||||
 | 
							c := NewTestController(test.inputFastcgiConfig)
 | 
				
			||||||
 | 
							actualFastcgiConfigs, err := fastcgiParse(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(actualFastcgiConfigs) != len(test.expectedFastcgiConfig) {
 | 
				
			||||||
 | 
								t.Fatalf("Test %d expected %d no of FastCGI configs, but got %d ",
 | 
				
			||||||
 | 
									i, len(test.expectedFastcgiConfig), len(actualFastcgiConfigs))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for j, actualFastcgiConfig := range actualFastcgiConfigs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if actualFastcgiConfig.Path != test.expectedFastcgiConfig[j].Path {
 | 
				
			||||||
 | 
									t.Errorf("Test %d expected %dth FastCGI Path to be  %s  , but got %s",
 | 
				
			||||||
 | 
										i, j, test.expectedFastcgiConfig[j].Path, actualFastcgiConfig.Path)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if actualFastcgiConfig.Address != test.expectedFastcgiConfig[j].Address {
 | 
				
			||||||
 | 
									t.Errorf("Test %d expected %dth FastCGI Address to be  %s  , but got %s",
 | 
				
			||||||
 | 
										i, j, test.expectedFastcgiConfig[j].Address, actualFastcgiConfig.Address)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if actualFastcgiConfig.Ext != test.expectedFastcgiConfig[j].Ext {
 | 
				
			||||||
 | 
									t.Errorf("Test %d expected %dth FastCGI Ext to be  %s  , but got %s",
 | 
				
			||||||
 | 
										i, j, test.expectedFastcgiConfig[j].Ext, actualFastcgiConfig.Ext)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if actualFastcgiConfig.SplitPath != test.expectedFastcgiConfig[j].SplitPath {
 | 
				
			||||||
 | 
									t.Errorf("Test %d expected %dth FastCGI SplitPath to be  %s  , but got %s",
 | 
				
			||||||
 | 
										i, j, test.expectedFastcgiConfig[j].SplitPath, actualFastcgiConfig.SplitPath)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if fmt.Sprint(actualFastcgiConfig.IndexFiles) != fmt.Sprint(test.expectedFastcgiConfig[j].IndexFiles) {
 | 
				
			||||||
 | 
									t.Errorf("Test %d expected %dth FastCGI IndexFiles to be  %s  , but got %s",
 | 
				
			||||||
 | 
										i, j, test.expectedFastcgiConfig[j].IndexFiles, actualFastcgiConfig.IndexFiles)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,10 @@ func Markdown(c *Controller) (middleware.Middleware, error) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := markdown.GenerateLinks(md, &cfg); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// If generated site already exists, clear it out
 | 
								// If generated site already exists, clear it out
 | 
				
			||||||
			_, err := os.Stat(cfg.StaticDir)
 | 
								_, err := os.Stat(cfg.StaticDir)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,11 @@ package markdown
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mholt/caddy/middleware"
 | 
						"github.com/mholt/caddy/middleware"
 | 
				
			||||||
	"github.com/russross/blackfriday"
 | 
						"github.com/russross/blackfriday"
 | 
				
			||||||
@ -64,13 +66,19 @@ type Config struct {
 | 
				
			|||||||
	// Map of request URL to static files generated
 | 
						// Map of request URL to static files generated
 | 
				
			||||||
	StaticFiles map[string]string
 | 
						StaticFiles map[string]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Links to all markdown pages ordered by date.
 | 
				
			||||||
 | 
						Links []PageLink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Directory to store static files
 | 
						// Directory to store static files
 | 
				
			||||||
	StaticDir string
 | 
						StaticDir string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sync.RWMutex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ServeHTTP implements the http.Handler interface.
 | 
					// ServeHTTP implements the http.Handler interface.
 | 
				
			||||||
func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
 | 
					func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
 | 
				
			||||||
	for _, m := range md.Configs {
 | 
						for i := range md.Configs {
 | 
				
			||||||
 | 
							m := &md.Configs[i]
 | 
				
			||||||
		if !middleware.Path(r.URL.Path).Matches(m.PathScope) {
 | 
							if !middleware.Path(r.URL.Path).Matches(m.PathScope) {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -114,6 +122,13 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if m.StaticDir != "" {
 | 
				
			||||||
 | 
										// Markdown modified or new. Update links.
 | 
				
			||||||
 | 
										if err := GenerateLinks(md, m); err != nil {
 | 
				
			||||||
 | 
											log.Println(err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				body, err := ioutil.ReadAll(f)
 | 
									body, err := ioutil.ReadAll(f)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return http.StatusInternalServerError, err
 | 
										return http.StatusInternalServerError, err
 | 
				
			||||||
@ -124,7 +139,7 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
 | 
				
			|||||||
					Req:  r,
 | 
										Req:  r,
 | 
				
			||||||
					URL:  r.URL,
 | 
										URL:  r.URL,
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				html, err := md.Process(m, fpath, body, ctx)
 | 
									html, err := md.Process(*m, fpath, body, ctx)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return http.StatusInternalServerError, err
 | 
										return http.StatusInternalServerError, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,12 @@
 | 
				
			|||||||
package markdown
 | 
					package markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,6 +28,8 @@ func TestMarkdown(t *testing.T) {
 | 
				
			|||||||
				Styles:      []string{},
 | 
									Styles:      []string{},
 | 
				
			||||||
				Scripts:     []string{},
 | 
									Scripts:     []string{},
 | 
				
			||||||
				Templates:   templates,
 | 
									Templates:   templates,
 | 
				
			||||||
 | 
									StaticDir:   DefaultStaticDir,
 | 
				
			||||||
 | 
									StaticFiles: make(map[string]string),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Config{
 | 
								Config{
 | 
				
			||||||
				Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
									Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
				
			||||||
@ -34,6 +38,8 @@ func TestMarkdown(t *testing.T) {
 | 
				
			|||||||
				Styles:      []string{"/resources/css/log.css", "/resources/css/default.css"},
 | 
									Styles:      []string{"/resources/css/log.css", "/resources/css/default.css"},
 | 
				
			||||||
				Scripts:     []string{"/resources/js/log.js", "/resources/js/default.js"},
 | 
									Scripts:     []string{"/resources/js/log.js", "/resources/js/default.js"},
 | 
				
			||||||
				Templates:   make(map[string]string),
 | 
									Templates:   make(map[string]string),
 | 
				
			||||||
 | 
									StaticDir:   DefaultStaticDir,
 | 
				
			||||||
 | 
									StaticFiles: make(map[string]string),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Config{
 | 
								Config{
 | 
				
			||||||
				Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
									Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
				
			||||||
@ -44,6 +50,14 @@ func TestMarkdown(t *testing.T) {
 | 
				
			|||||||
				Templates:   templates,
 | 
									Templates:   templates,
 | 
				
			||||||
				StaticDir:   "testdata/og_static",
 | 
									StaticDir:   "testdata/og_static",
 | 
				
			||||||
				StaticFiles: map[string]string{"/og/first.md": "testdata/og_static/og/first.md/index.html"},
 | 
									StaticFiles: map[string]string{"/og/first.md": "testdata/og_static/og/first.md/index.html"},
 | 
				
			||||||
 | 
									Links: []PageLink{
 | 
				
			||||||
 | 
										PageLink{
 | 
				
			||||||
 | 
											Title:   "first",
 | 
				
			||||||
 | 
											Summary: "",
 | 
				
			||||||
 | 
											Date:    time.Now(),
 | 
				
			||||||
 | 
											Url:     "/og/first.md",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		IndexFiles: []string{"index.html"},
 | 
							IndexFiles: []string{"index.html"},
 | 
				
			||||||
@ -168,4 +182,40 @@ func getTrue() bool {
 | 
				
			|||||||
	if respBody != expectedBody {
 | 
						if respBody != expectedBody {
 | 
				
			||||||
		t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
 | 
							t.Fatalf("Expected body: %v got: %v", expectedBody, respBody)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expectedLinks := []string{
 | 
				
			||||||
 | 
							"/blog/test.md",
 | 
				
			||||||
 | 
							"/log/test.md",
 | 
				
			||||||
 | 
							"/og/first.md",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, c := range md.Configs {
 | 
				
			||||||
 | 
							log.Printf("Test number: %d, configuration links: %v, config: %v", i, c.Links, c)
 | 
				
			||||||
 | 
							if c.Links[0].Url != expectedLinks[i] {
 | 
				
			||||||
 | 
								t.Fatalf("Expected %v got %v", expectedLinks[i], c.Links[0].Url)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// attempt to trigger race condition
 | 
				
			||||||
 | 
						var w sync.WaitGroup
 | 
				
			||||||
 | 
						f := func() {
 | 
				
			||||||
 | 
							req, err := http.NewRequest("GET", "/log/test.md", nil)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatalf("Could not create HTTP request: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rec := httptest.NewRecorder()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							md.ServeHTTP(rec, req)
 | 
				
			||||||
 | 
							w.Done()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := 0; i < 5; i++ {
 | 
				
			||||||
 | 
							w.Add(1)
 | 
				
			||||||
 | 
							go f()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						w.Wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = os.RemoveAll(DefaultStaticDir); err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Error while removing the generated static files: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,14 +9,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/BurntSushi/toml"
 | 
						"github.com/BurntSushi/toml"
 | 
				
			||||||
	"gopkg.in/yaml.v2"
 | 
						"gopkg.in/yaml.v2"
 | 
				
			||||||
)
 | 
						"time"
 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	parsers = []MetadataParser{
 | 
					 | 
				
			||||||
		&JSONMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
					 | 
				
			||||||
		&TOMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
					 | 
				
			||||||
		&YAMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Metadata stores a page's metadata
 | 
					// Metadata stores a page's metadata
 | 
				
			||||||
@ -27,20 +20,31 @@ type Metadata struct {
 | 
				
			|||||||
	// Page template
 | 
						// Page template
 | 
				
			||||||
	Template string
 | 
						Template string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Publish date
 | 
				
			||||||
 | 
						Date time.Time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Variables to be used with Template
 | 
						// Variables to be used with Template
 | 
				
			||||||
	Variables map[string]string
 | 
						Variables map[string]string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// load loads parsed values in parsedMap into Metadata
 | 
					// 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 title, ok := parsedMap["title"]; ok {
 | 
				
			||||||
		m.Title, _ = template.(string)
 | 
							m.Title, _ = title.(string)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if template, ok := parsedMap["template"]; ok {
 | 
						if template, ok := parsedMap["template"]; ok {
 | 
				
			||||||
		m.Template, _ = template.(string)
 | 
							m.Template, _ = template.(string)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if variables, ok := parsedMap["variables"]; ok {
 | 
						if date, ok := parsedMap["date"].(string); ok {
 | 
				
			||||||
		m.Variables, _ = variables.(map[string]string)
 | 
							if t, err := time.Parse(timeLayout, date); err == nil {
 | 
				
			||||||
 | 
								m.Date = t
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// store everything as a variable
 | 
				
			||||||
 | 
						for key, val := range parsedMap {
 | 
				
			||||||
 | 
							if v, ok := val.(string); ok {
 | 
				
			||||||
 | 
								m.Variables[key] = v
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -62,7 +66,7 @@ type MetadataParser interface {
 | 
				
			|||||||
	Metadata() Metadata
 | 
						Metadata() Metadata
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// JSONMetadataParser is the MetdataParser for JSON
 | 
					// JSONMetadataParser is the MetadataParser for JSON
 | 
				
			||||||
type JSONMetadataParser struct {
 | 
					type JSONMetadataParser struct {
 | 
				
			||||||
	metadata Metadata
 | 
						metadata Metadata
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -76,16 +80,6 @@ func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
 | 
				
			|||||||
	if err := decoder.Decode(&m); err != nil {
 | 
						if err := decoder.Decode(&m); err != nil {
 | 
				
			||||||
		return b, err
 | 
							return b, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if vars, ok := m["variables"].(map[string]interface{}); ok {
 | 
					 | 
				
			||||||
		vars1 := make(map[string]string)
 | 
					 | 
				
			||||||
		for k, v := range vars {
 | 
					 | 
				
			||||||
			if val, ok := v.(string); ok {
 | 
					 | 
				
			||||||
				vars1[k] = val
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		m["variables"] = vars1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	j.metadata.load(m)
 | 
						j.metadata.load(m)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Retrieve remaining bytes after decoding
 | 
						// Retrieve remaining bytes after decoding
 | 
				
			||||||
@ -129,15 +123,6 @@ func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
 | 
				
			|||||||
	if err := toml.Unmarshal(b, &m); err != nil {
 | 
						if err := toml.Unmarshal(b, &m); err != nil {
 | 
				
			||||||
		return markdown, err
 | 
							return markdown, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if vars, ok := m["variables"].(map[string]interface{}); ok {
 | 
					 | 
				
			||||||
		vars1 := make(map[string]string)
 | 
					 | 
				
			||||||
		for k, v := range vars {
 | 
					 | 
				
			||||||
			if val, ok := v.(string); ok {
 | 
					 | 
				
			||||||
				vars1[k] = val
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		m["variables"] = vars1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	t.metadata.load(m)
 | 
						t.metadata.load(m)
 | 
				
			||||||
	return markdown, nil
 | 
						return markdown, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -174,21 +159,6 @@ func (y *YAMLMetadataParser) Parse(b []byte) ([]byte, error) {
 | 
				
			|||||||
	if err := yaml.Unmarshal(b, &m); err != nil {
 | 
						if err := yaml.Unmarshal(b, &m); err != nil {
 | 
				
			||||||
		return markdown, err
 | 
							return markdown, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// convert variables (if present) to map[string]interface{}
 | 
					 | 
				
			||||||
	// to match expected type
 | 
					 | 
				
			||||||
	if vars, ok := m["variables"].(map[interface{}]interface{}); ok {
 | 
					 | 
				
			||||||
		vars1 := make(map[string]string)
 | 
					 | 
				
			||||||
		for k, v := range vars {
 | 
					 | 
				
			||||||
			if key, ok := k.(string); ok {
 | 
					 | 
				
			||||||
				if val, ok := v.(string); ok {
 | 
					 | 
				
			||||||
					vars1[key] = val
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		m["variables"] = vars1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	y.metadata.load(m)
 | 
						y.metadata.load(m)
 | 
				
			||||||
	return markdown, nil
 | 
						return markdown, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -260,10 +230,19 @@ func findParser(b []byte) MetadataParser {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	line = bytes.TrimSpace(line)
 | 
						line = bytes.TrimSpace(line)
 | 
				
			||||||
	for _, parser := range parsers {
 | 
						for _, parser := range parsers() {
 | 
				
			||||||
		if bytes.Equal(parser.Opening(), line) {
 | 
							if bytes.Equal(parser.Opening(), line) {
 | 
				
			||||||
			return parser
 | 
								return parser
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// parsers returns all available parsers
 | 
				
			||||||
 | 
					func parsers() []MetadataParser {
 | 
				
			||||||
 | 
						return []MetadataParser{
 | 
				
			||||||
 | 
							&JSONMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
				
			||||||
 | 
							&TOMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
				
			||||||
 | 
							&YAMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,13 +11,11 @@ import (
 | 
				
			|||||||
var TOML = [4]string{`
 | 
					var TOML = [4]string{`
 | 
				
			||||||
title = "A title"
 | 
					title = "A title"
 | 
				
			||||||
template = "default"
 | 
					template = "default"
 | 
				
			||||||
[variables]
 | 
					 | 
				
			||||||
name = "value"
 | 
					name = "value"
 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`+++
 | 
						`+++
 | 
				
			||||||
title = "A title"
 | 
					title = "A title"
 | 
				
			||||||
template = "default"
 | 
					template = "default"
 | 
				
			||||||
[variables]
 | 
					 | 
				
			||||||
name = "value"
 | 
					name = "value"
 | 
				
			||||||
+++
 | 
					+++
 | 
				
			||||||
Page content
 | 
					Page content
 | 
				
			||||||
@ -25,7 +23,6 @@ Page content
 | 
				
			|||||||
	`+++
 | 
						`+++
 | 
				
			||||||
title = "A title"
 | 
					title = "A title"
 | 
				
			||||||
template = "default"
 | 
					template = "default"
 | 
				
			||||||
[variables]
 | 
					 | 
				
			||||||
name = "value"
 | 
					name = "value"
 | 
				
			||||||
	`,
 | 
						`,
 | 
				
			||||||
	`title = "A title" template = "default" [variables] name = "value"`,
 | 
						`title = "A title" template = "default" [variables] name = "value"`,
 | 
				
			||||||
@ -34,13 +31,11 @@ name = "value"
 | 
				
			|||||||
var YAML = [4]string{`
 | 
					var YAML = [4]string{`
 | 
				
			||||||
title : A title
 | 
					title : A title
 | 
				
			||||||
template : default
 | 
					template : default
 | 
				
			||||||
variables :
 | 
					 | 
				
			||||||
name : value
 | 
					name : value
 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`---
 | 
						`---
 | 
				
			||||||
title : A title
 | 
					title : A title
 | 
				
			||||||
template : default
 | 
					template : default
 | 
				
			||||||
variables :
 | 
					 | 
				
			||||||
name : value
 | 
					name : value
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
Page content
 | 
					Page content
 | 
				
			||||||
@ -48,7 +43,6 @@ Page content
 | 
				
			|||||||
	`---
 | 
						`---
 | 
				
			||||||
title : A title
 | 
					title : A title
 | 
				
			||||||
template : default
 | 
					template : default
 | 
				
			||||||
variables :
 | 
					 | 
				
			||||||
name : value
 | 
					name : value
 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`title : A title template : default variables : name : value`,
 | 
						`title : A title template : default variables : name : value`,
 | 
				
			||||||
@ -56,35 +50,27 @@ variables :
 | 
				
			|||||||
var JSON = [4]string{`
 | 
					var JSON = [4]string{`
 | 
				
			||||||
	"title" : "A title",
 | 
						"title" : "A title",
 | 
				
			||||||
	"template" : "default",
 | 
						"template" : "default",
 | 
				
			||||||
	"variables" : {
 | 
					 | 
				
			||||||
	"name" : "value"
 | 
						"name" : "value"
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`{
 | 
						`{
 | 
				
			||||||
	"title" : "A title",
 | 
						"title" : "A title",
 | 
				
			||||||
	"template" : "default",
 | 
						"template" : "default",
 | 
				
			||||||
	"variables" : {
 | 
					 | 
				
			||||||
	"name" : "value"
 | 
						"name" : "value"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
Page content
 | 
					Page content
 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`
 | 
						`
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	"title" : "A title",
 | 
						"title" : "A title",
 | 
				
			||||||
	"template" : "default",
 | 
						"template" : "default",
 | 
				
			||||||
	"variables" : {
 | 
					 | 
				
			||||||
	"name" : "value"
 | 
						"name" : "value"
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
`,
 | 
					`,
 | 
				
			||||||
	`
 | 
						`
 | 
				
			||||||
{{
 | 
					{{
 | 
				
			||||||
	"title" : "A title",
 | 
						"title" : "A title",
 | 
				
			||||||
	"template" : "default",
 | 
						"template" : "default",
 | 
				
			||||||
	"variables" : {
 | 
					 | 
				
			||||||
	"name" : "value"
 | 
						"name" : "value"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
	`,
 | 
						`,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,7 +84,11 @@ func TestParsers(t *testing.T) {
 | 
				
			|||||||
	expected := Metadata{
 | 
						expected := Metadata{
 | 
				
			||||||
		Title:    "A title",
 | 
							Title:    "A title",
 | 
				
			||||||
		Template: "default",
 | 
							Template: "default",
 | 
				
			||||||
		Variables: map[string]string{"name": "value"},
 | 
							Variables: map[string]string{
 | 
				
			||||||
 | 
								"name":     "value",
 | 
				
			||||||
 | 
								"title":    "A title",
 | 
				
			||||||
 | 
								"template": "default",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	compare := func(m Metadata) bool {
 | 
						compare := func(m Metadata) bool {
 | 
				
			||||||
		if m.Title != expected.Title {
 | 
							if m.Title != expected.Title {
 | 
				
			||||||
@ -112,7 +102,7 @@ func TestParsers(t *testing.T) {
 | 
				
			|||||||
				return false
 | 
									return false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return len(m.Variables) == 1
 | 
							return len(m.Variables) == len(expected.Variables)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := []struct {
 | 
						data := []struct {
 | 
				
			||||||
@ -120,9 +110,9 @@ func TestParsers(t *testing.T) {
 | 
				
			|||||||
		testData [4]string
 | 
							testData [4]string
 | 
				
			||||||
		name     string
 | 
							name     string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{&JSONMetadataParser{}, JSON, "json"},
 | 
							{&JSONMetadataParser{metadata: Metadata{Variables: make(map[string]string)}}, JSON, "json"},
 | 
				
			||||||
		{&YAMLMetadataParser{}, YAML, "yaml"},
 | 
							{&YAMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}}, YAML, "yaml"},
 | 
				
			||||||
		{&TOMLMetadataParser{}, TOML, "toml"},
 | 
							{&TOMLMetadataParser{metadata: Metadata{Variables: make(map[string]string)}}, TOML, "toml"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, v := range data {
 | 
						for _, v := range data {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										161
									
								
								middleware/markdown/page.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								middleware/markdown/page.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					package markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/russross/blackfriday"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// Date format YYYY-MM-DD HH:MM:SS
 | 
				
			||||||
 | 
						timeLayout = `2006-01-02 15:04:05`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Length of page summary.
 | 
				
			||||||
 | 
						summaryLen = 150
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PageLink represents a statically generated markdown page.
 | 
				
			||||||
 | 
					type PageLink struct {
 | 
				
			||||||
 | 
						Title   string
 | 
				
			||||||
 | 
						Summary string
 | 
				
			||||||
 | 
						Date    time.Time
 | 
				
			||||||
 | 
						Url     string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// byDate sorts PageLink by newest date to oldest.
 | 
				
			||||||
 | 
					type byDate []PageLink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p byDate) Len() int           { return len(p) }
 | 
				
			||||||
 | 
					func (p byDate) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 | 
				
			||||||
 | 
					func (p byDate) Less(i, j int) bool { return p[i].Date.After(p[j].Date) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type linkGen struct {
 | 
				
			||||||
 | 
						generating bool
 | 
				
			||||||
 | 
						waiters    int
 | 
				
			||||||
 | 
						lastErr    error
 | 
				
			||||||
 | 
						sync.RWMutex
 | 
				
			||||||
 | 
						sync.WaitGroup
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *linkGen) addWaiter() {
 | 
				
			||||||
 | 
						l.WaitGroup.Add(1)
 | 
				
			||||||
 | 
						l.waiters++
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *linkGen) discardWaiters() {
 | 
				
			||||||
 | 
						l.Lock()
 | 
				
			||||||
 | 
						defer l.Unlock()
 | 
				
			||||||
 | 
						for i := 0; i < l.waiters; i++ {
 | 
				
			||||||
 | 
							l.Done()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *linkGen) started() bool {
 | 
				
			||||||
 | 
						l.RLock()
 | 
				
			||||||
 | 
						defer l.RUnlock()
 | 
				
			||||||
 | 
						return l.generating
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
 | 
				
			||||||
 | 
						l.Lock()
 | 
				
			||||||
 | 
						l.generating = true
 | 
				
			||||||
 | 
						l.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fp := filepath.Join(md.Root, cfg.PathScope)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg.Links = []PageLink{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg.Lock()
 | 
				
			||||||
 | 
						l.lastErr = filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							for _, ext := range cfg.Extensions {
 | 
				
			||||||
 | 
								if !info.IsDir() && strings.HasSuffix(info.Name(), ext) {
 | 
				
			||||||
 | 
									// Load the file
 | 
				
			||||||
 | 
									body, err := ioutil.ReadFile(path)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Get the relative path as if it were a HTTP request,
 | 
				
			||||||
 | 
									// then prepend with "/" (like a real HTTP request)
 | 
				
			||||||
 | 
									reqPath, err := filepath.Rel(md.Root, path)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									reqPath = "/" + reqPath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									parser := findParser(body)
 | 
				
			||||||
 | 
									if parser == nil {
 | 
				
			||||||
 | 
										// no metadata, ignore.
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									summary, err := parser.Parse(body)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if len(summary) > summaryLen {
 | 
				
			||||||
 | 
										summary = summary[:summaryLen]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									metadata := parser.Metadata()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									cfg.Links = append(cfg.Links, PageLink{
 | 
				
			||||||
 | 
										Title:   metadata.Title,
 | 
				
			||||||
 | 
										Url:     reqPath,
 | 
				
			||||||
 | 
										Date:    metadata.Date,
 | 
				
			||||||
 | 
										Summary: string(blackfriday.Markdown(summary, PlaintextRenderer{}, 0)),
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									break // don't try other file extensions
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// sort by newest date
 | 
				
			||||||
 | 
						sort.Sort(byDate(cfg.Links))
 | 
				
			||||||
 | 
						cfg.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.Lock()
 | 
				
			||||||
 | 
						l.generating = false
 | 
				
			||||||
 | 
						l.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type linkGenerator struct {
 | 
				
			||||||
 | 
						gens map[*Config]*linkGen
 | 
				
			||||||
 | 
						sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var generator = linkGenerator{gens: make(map[*Config]*linkGen)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenerateLinks generates links to all markdown files ordered by newest date.
 | 
				
			||||||
 | 
					// This blocks until link generation is done. When called by multiple goroutines,
 | 
				
			||||||
 | 
					// the first caller starts the generation and others only wait.
 | 
				
			||||||
 | 
					func GenerateLinks(md Markdown, cfg *Config) error {
 | 
				
			||||||
 | 
						generator.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if link generator exists for config and running, wait.
 | 
				
			||||||
 | 
						if g, ok := generator.gens[cfg]; ok {
 | 
				
			||||||
 | 
							if g.started() {
 | 
				
			||||||
 | 
								g.addWaiter()
 | 
				
			||||||
 | 
								generator.Unlock()
 | 
				
			||||||
 | 
								g.Wait()
 | 
				
			||||||
 | 
								return g.lastErr
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g := &linkGen{}
 | 
				
			||||||
 | 
						generator.gens[cfg] = g
 | 
				
			||||||
 | 
						generator.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.generateLinks(md, cfg)
 | 
				
			||||||
 | 
						g.discardWaiters()
 | 
				
			||||||
 | 
						return g.lastErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -21,6 +21,7 @@ const (
 | 
				
			|||||||
type MarkdownData struct {
 | 
					type MarkdownData struct {
 | 
				
			||||||
	middleware.Context
 | 
						middleware.Context
 | 
				
			||||||
	Doc   map[string]string
 | 
						Doc   map[string]string
 | 
				
			||||||
 | 
						Links []PageLink
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Process processes the contents of a page in b. It parses the metadata
 | 
					// Process processes the contents of a page in b. It parses the metadata
 | 
				
			||||||
@ -97,9 +98,14 @@ func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, me
 | 
				
			|||||||
	mdData := MarkdownData{
 | 
						mdData := MarkdownData{
 | 
				
			||||||
		Context: ctx,
 | 
							Context: ctx,
 | 
				
			||||||
		Doc:     metadata.Variables,
 | 
							Doc:     metadata.Variables,
 | 
				
			||||||
 | 
							Links:   c.Links,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = t.Execute(b, mdData); err != nil {
 | 
						c.RLock()
 | 
				
			||||||
 | 
						err = t.Execute(b, mdData)
 | 
				
			||||||
 | 
						c.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										93
									
								
								middleware/markdown/renderer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								middleware/markdown/renderer.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					package markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PlaintextRenderer struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Block-level callbacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) BlockQuote(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) BlockHtml(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) HRule(out *bytes.Buffer) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) List(out *bytes.Buffer, text func() bool, flags int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
 | 
				
			||||||
 | 
						marker := out.Len()
 | 
				
			||||||
 | 
						if !text() {
 | 
				
			||||||
 | 
							out.Truncate(marker)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out.Write([]byte{' '})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) TableRow(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) TableCell(out *bytes.Buffer, text []byte, flags int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Footnotes(out *bytes.Buffer, text func() bool) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) TitleBlock(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Span-level callbacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) CodeSpan(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) {
 | 
				
			||||||
 | 
						out.Write(text)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Emphasis(out *bytes.Buffer, text []byte) {
 | 
				
			||||||
 | 
						out.Write(text)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) LineBreak(out *bytes.Buffer) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
 | 
				
			||||||
 | 
						out.Write(content)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) {
 | 
				
			||||||
 | 
						out.Write(text)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) StrikeThrough(out *bytes.Buffer, text []byte) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Low-level callbacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) Entity(out *bytes.Buffer, entity []byte) {
 | 
				
			||||||
 | 
						out.Write(entity)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) NormalText(out *bytes.Buffer, text []byte) {
 | 
				
			||||||
 | 
						out.Write(text)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Header and footer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) DocumentHeader(out *bytes.Buffer) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) DocumentFooter(out *bytes.Buffer) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r PlaintextRenderer) GetFlags() int { return 0 }
 | 
				
			||||||
							
								
								
									
										1
									
								
								middleware/markdown/testdata/blog/test.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								middleware/markdown/testdata/blog/test.md
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,5 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
title: Markdown test
 | 
					title: Markdown test
 | 
				
			||||||
variables:
 | 
					 | 
				
			||||||
sitename: A Caddy website
 | 
					sitename: A Caddy website
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								middleware/markdown/testdata/log/test.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								middleware/markdown/testdata/log/test.md
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,5 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
title: Markdown test
 | 
					title: Markdown test
 | 
				
			||||||
variables:
 | 
					 | 
				
			||||||
sitename: A Caddy website
 | 
					sitename: A Caddy website
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -109,6 +109,8 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
 | 
				
			|||||||
					return upstreams, c.ArgErr()
 | 
										return upstreams, c.ArgErr()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				upstream.WithoutPathPrefix = c.Val()
 | 
									upstream.WithoutPathPrefix = c.Val()
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return upstreams, c.Errf("unknown property '%s'", c.Val())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user