diff options
author | Thomas Boerger <thomas@webhippie.de> | 2016-11-03 23:16:01 +0100 |
---|---|---|
committer | Thomas Boerger <thomas@webhippie.de> | 2016-11-04 08:43:11 +0100 |
commit | 1ebb35b98889ff77299f24d82da426b434b0cca0 (patch) | |
tree | 6dcb814d6df4d11c7e7a0ba6da8a6945628e2c5d /vendor/gopkg.in/macaron.v1/render.go | |
parent | 78f86abba45cb35018c58b8bd5f4c48a86cc8634 (diff) | |
download | gitea-1ebb35b98889ff77299f24d82da426b434b0cca0.tar.gz gitea-1ebb35b98889ff77299f24d82da426b434b0cca0.zip |
Added all required dependencies
Diffstat (limited to 'vendor/gopkg.in/macaron.v1/render.go')
-rw-r--r-- | vendor/gopkg.in/macaron.v1/render.go | 714 |
1 files changed, 714 insertions, 0 deletions
diff --git a/vendor/gopkg.in/macaron.v1/render.go b/vendor/gopkg.in/macaron.v1/render.go new file mode 100644 index 0000000000..ff2dcaacdd --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/render.go @@ -0,0 +1,714 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 macaron + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "html/template" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/Unknwon/com" +) + +const ( + _CONTENT_TYPE = "Content-Type" + _CONTENT_LENGTH = "Content-Length" + _CONTENT_BINARY = "application/octet-stream" + _CONTENT_JSON = "application/json" + _CONTENT_HTML = "text/html" + _CONTENT_PLAIN = "text/plain" + _CONTENT_XHTML = "application/xhtml+xml" + _CONTENT_XML = "text/xml" + _DEFAULT_CHARSET = "UTF-8" +) + +var ( + // Provides a temporary buffer to execute templates into and catch errors. + bufpool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, + } + + // Included helper functions for use when rendering html + helperFuncs = template.FuncMap{ + "yield": func() (string, error) { + return "", fmt.Errorf("yield called with no layout defined") + }, + "current": func() (string, error) { + return "", nil + }, + } +) + +type ( + // TemplateFile represents a interface of template file that has name and can be read. + TemplateFile interface { + Name() string + Data() []byte + Ext() string + } + // TemplateFileSystem represents a interface of template file system that able to list all files. + TemplateFileSystem interface { + ListFiles() []TemplateFile + } + + // Delims represents a set of Left and Right delimiters for HTML template rendering + Delims struct { + // Left delimiter, defaults to {{ + Left string + // Right delimiter, defaults to }} + Right string + } + + // RenderOptions represents a struct for specifying configuration options for the Render middleware. + RenderOptions struct { + // Directory to load templates. Default is "templates". + Directory string + // Addtional directories to overwite templates. + AppendDirectories []string + // Layout template name. Will not render a layout if "". Default is to "". + Layout string + // Extensions to parse template files from. Defaults are [".tmpl", ".html"]. + Extensions []string + // Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Default is []. + Funcs []template.FuncMap + // Delims sets the action delimiters to the specified strings in the Delims struct. + Delims Delims + // Appends the given charset to the Content-Type header. Default is "UTF-8". + Charset string + // Outputs human readable JSON. + IndentJSON bool + // Outputs human readable XML. + IndentXML bool + // Prefixes the JSON output with the given bytes. + PrefixJSON []byte + // Prefixes the XML output with the given bytes. + PrefixXML []byte + // Allows changing of output to XHTML instead of HTML. Default is "text/html" + HTMLContentType string + // TemplateFileSystem is the interface for supporting any implmentation of template file system. + TemplateFileSystem + } + + // HTMLOptions is a struct for overriding some rendering Options for specific HTML call + HTMLOptions struct { + // Layout template name. Overrides Options.Layout. + Layout string + } + + Render interface { + http.ResponseWriter + SetResponseWriter(http.ResponseWriter) + + JSON(int, interface{}) + JSONString(interface{}) (string, error) + RawData(int, []byte) // Serve content as binary + PlainText(int, []byte) // Serve content as plain text + HTML(int, string, interface{}, ...HTMLOptions) + HTMLSet(int, string, string, interface{}, ...HTMLOptions) + HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) + HTMLString(string, interface{}, ...HTMLOptions) (string, error) + HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) + HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) + XML(int, interface{}) + Error(int, ...string) + Status(int) + SetTemplatePath(string, string) + HasTemplateSet(string) bool + } +) + +// TplFile implements TemplateFile interface. +type TplFile struct { + name string + data []byte + ext string +} + +// NewTplFile cerates new template file with given name and data. +func NewTplFile(name string, data []byte, ext string) *TplFile { + return &TplFile{name, data, ext} +} + +func (f *TplFile) Name() string { + return f.name +} + +func (f *TplFile) Data() []byte { + return f.data +} + +func (f *TplFile) Ext() string { + return f.ext +} + +// TplFileSystem implements TemplateFileSystem interface. +type TplFileSystem struct { + files []TemplateFile +} + +// NewTemplateFileSystem creates new template file system with given options. +func NewTemplateFileSystem(opt RenderOptions, omitData bool) TplFileSystem { + fs := TplFileSystem{} + fs.files = make([]TemplateFile, 0, 10) + + // Directories are composed in reverse order because later one overwrites previous ones, + // so once found, we can directly jump out of the loop. + dirs := make([]string, 0, len(opt.AppendDirectories)+1) + for i := len(opt.AppendDirectories) - 1; i >= 0; i-- { + dirs = append(dirs, opt.AppendDirectories[i]) + } + dirs = append(dirs, opt.Directory) + + var err error + for i := range dirs { + // Skip ones that does not exists for symlink test, + // but allow non-symlink ones added after start. + if !com.IsExist(dirs[i]) { + continue + } + + dirs[i], err = filepath.EvalSymlinks(dirs[i]) + if err != nil { + panic("EvalSymlinks(" + dirs[i] + "): " + err.Error()) + } + } + lastDir := dirs[len(dirs)-1] + + // We still walk the last (original) directory because it's non-sense we load templates not exist in original directory. + if err = filepath.Walk(lastDir, func(path string, info os.FileInfo, err error) error { + r, err := filepath.Rel(lastDir, path) + if err != nil { + return err + } + + ext := GetExt(r) + + for _, extension := range opt.Extensions { + if ext != extension { + continue + } + + var data []byte + if !omitData { + // Loop over candidates of directory, break out once found. + // The file always exists because it's inside the walk function, + // and read original file is the worst case. + for i := range dirs { + path = filepath.Join(dirs[i], r) + if !com.IsFile(path) { + continue + } + + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + break + } + } + + name := filepath.ToSlash((r[0 : len(r)-len(ext)])) + fs.files = append(fs.files, NewTplFile(name, data, ext)) + } + + return nil + }); err != nil { + panic("NewTemplateFileSystem: " + err.Error()) + } + + return fs +} + +func (fs TplFileSystem) ListFiles() []TemplateFile { + return fs.files +} + +func PrepareCharset(charset string) string { + if len(charset) != 0 { + return "; charset=" + charset + } + + return "; charset=" + _DEFAULT_CHARSET +} + +func GetExt(s string) string { + index := strings.Index(s, ".") + if index == -1 { + return "" + } + return s[index:] +} + +func compile(opt RenderOptions) *template.Template { + t := template.New(opt.Directory) + t.Delims(opt.Delims.Left, opt.Delims.Right) + // Parse an initial template in case we don't have any. + template.Must(t.Parse("Macaron")) + + if opt.TemplateFileSystem == nil { + opt.TemplateFileSystem = NewTemplateFileSystem(opt, false) + } + + for _, f := range opt.TemplateFileSystem.ListFiles() { + tmpl := t.New(f.Name()) + for _, funcs := range opt.Funcs { + tmpl.Funcs(funcs) + } + // Bomb out if parse fails. We don't want any silent server starts. + template.Must(tmpl.Funcs(helperFuncs).Parse(string(f.Data()))) + } + + return t +} + +const ( + DEFAULT_TPL_SET_NAME = "DEFAULT" +) + +// TemplateSet represents a template set of type *template.Template. +type TemplateSet struct { + lock sync.RWMutex + sets map[string]*template.Template + dirs map[string]string +} + +// NewTemplateSet initializes a new empty template set. +func NewTemplateSet() *TemplateSet { + return &TemplateSet{ + sets: make(map[string]*template.Template), + dirs: make(map[string]string), + } +} + +func (ts *TemplateSet) Set(name string, opt *RenderOptions) *template.Template { + t := compile(*opt) + + ts.lock.Lock() + defer ts.lock.Unlock() + + ts.sets[name] = t + ts.dirs[name] = opt.Directory + return t +} + +func (ts *TemplateSet) Get(name string) *template.Template { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.sets[name] +} + +func (ts *TemplateSet) GetDir(name string) string { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.dirs[name] +} + +func prepareRenderOptions(options []RenderOptions) RenderOptions { + var opt RenderOptions + if len(options) > 0 { + opt = options[0] + } + + // Defaults. + if len(opt.Directory) == 0 { + opt.Directory = "templates" + } + if len(opt.Extensions) == 0 { + opt.Extensions = []string{".tmpl", ".html"} + } + if len(opt.HTMLContentType) == 0 { + opt.HTMLContentType = _CONTENT_HTML + } + + return opt +} + +func ParseTplSet(tplSet string) (tplName string, tplDir string) { + tplSet = strings.TrimSpace(tplSet) + if len(tplSet) == 0 { + panic("empty template set argument") + } + infos := strings.Split(tplSet, ":") + if len(infos) == 1 { + tplDir = infos[0] + tplName = path.Base(tplDir) + } else { + tplName = infos[0] + tplDir = infos[1] + } + + if !com.IsDir(tplDir) { + panic("template set path does not exist or is not a directory") + } + return tplName, tplDir +} + +func renderHandler(opt RenderOptions, tplSets []string) Handler { + cs := PrepareCharset(opt.Charset) + ts := NewTemplateSet() + ts.Set(DEFAULT_TPL_SET_NAME, &opt) + + var tmpOpt RenderOptions + for _, tplSet := range tplSets { + tplName, tplDir := ParseTplSet(tplSet) + tmpOpt = opt + tmpOpt.Directory = tplDir + ts.Set(tplName, &tmpOpt) + } + + return func(ctx *Context) { + r := &TplRender{ + ResponseWriter: ctx.Resp, + TemplateSet: ts, + Opt: &opt, + CompiledCharset: cs, + } + ctx.Data["TmplLoadTimes"] = func() string { + if r.startTime.IsZero() { + return "" + } + return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms" + } + + ctx.Render = r + ctx.MapTo(r, (*Render)(nil)) + } +} + +// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain. +// An single variadic macaron.RenderOptions struct can be optionally provided to configure +// HTML rendering. The default directory for templates is "templates" and the default +// file extension is ".tmpl" and ".html". +// +// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the +// MACARON_ENV environment variable to "production". +func Renderer(options ...RenderOptions) Handler { + return renderHandler(prepareRenderOptions(options), []string{}) +} + +func Renderers(options RenderOptions, tplSets ...string) Handler { + return renderHandler(prepareRenderOptions([]RenderOptions{options}), tplSets) +} + +type TplRender struct { + http.ResponseWriter + *TemplateSet + Opt *RenderOptions + CompiledCharset string + + startTime time.Time +} + +func (r *TplRender) SetResponseWriter(rw http.ResponseWriter) { + r.ResponseWriter = rw +} + +func (r *TplRender) JSON(status int, v interface{}) { + var ( + result []byte + err error + ) + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // json rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_JSON+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixJSON) > 0 { + r.Write(r.Opt.PrefixJSON) + } + r.Write(result) +} + +func (r *TplRender) JSONString(v interface{}) (string, error) { + var result []byte + var err error + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + return "", err + } + return string(result), nil +} + +func (r *TplRender) XML(status int, v interface{}) { + var result []byte + var err error + if r.Opt.IndentXML { + result, err = xml.MarshalIndent(v, "", " ") + } else { + result, err = xml.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // XML rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_XML+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixXML) > 0 { + r.Write(r.Opt.PrefixXML) + } + r.Write(result) +} + +func (r *TplRender) data(status int, contentType string, v []byte) { + if r.Header().Get(_CONTENT_TYPE) == "" { + r.Header().Set(_CONTENT_TYPE, contentType) + } + r.WriteHeader(status) + r.Write(v) +} + +func (r *TplRender) RawData(status int, v []byte) { + r.data(status, _CONTENT_BINARY, v) +} + +func (r *TplRender) PlainText(status int, v []byte) { + r.data(status, _CONTENT_PLAIN, v) +} + +func (r *TplRender) execute(t *template.Template, name string, data interface{}) (*bytes.Buffer, error) { + buf := bufpool.Get().(*bytes.Buffer) + return buf, t.ExecuteTemplate(buf, name, data) +} + +func (r *TplRender) addYield(t *template.Template, tplName string, data interface{}) { + funcs := template.FuncMap{ + "yield": func() (template.HTML, error) { + buf, err := r.execute(t, tplName, data) + // return safe html here since we are rendering our own template + return template.HTML(buf.String()), err + }, + "current": func() (string, error) { + return tplName, nil + }, + } + t.Funcs(funcs) +} + +func (r *TplRender) renderBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) { + t := r.TemplateSet.Get(setName) + if Env == DEV { + opt := *r.Opt + opt.Directory = r.TemplateSet.GetDir(setName) + t = r.TemplateSet.Set(setName, &opt) + } + if t == nil { + return nil, fmt.Errorf("html/template: template \"%s\" is undefined", tplName) + } + + opt := r.prepareHTMLOptions(htmlOpt) + + if len(opt.Layout) > 0 { + r.addYield(t, tplName, data) + tplName = opt.Layout + } + + out, err := r.execute(t, tplName, data) + if err != nil { + return nil, err + } + + return out, nil +} + +func (r *TplRender) renderHTML(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.startTime = time.Now() + + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + http.Error(r, err.Error(), http.StatusInternalServerError) + return + } + + r.Header().Set(_CONTENT_TYPE, r.Opt.HTMLContentType+r.CompiledCharset) + r.WriteHeader(status) + + if _, err := out.WriteTo(r); err != nil { + out.Reset() + } + bufpool.Put(out) +} + +func (r *TplRender) HTML(status int, name string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSet(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, setName, tplName, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + return []byte(""), err + } + return out.Bytes(), nil +} + +func (r *TplRender) HTMLBytes(name string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + return r.HTMLSetBytes(DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetString(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLSetBytes(setName, tplName, data, htmlOpt...) + return string(p), err +} + +func (r *TplRender) HTMLString(name string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLBytes(name, data, htmlOpt...) + return string(p), err +} + +// Error writes the given HTTP status to the current ResponseWriter +func (r *TplRender) Error(status int, message ...string) { + r.WriteHeader(status) + if len(message) > 0 { + r.Write([]byte(message[0])) + } +} + +func (r *TplRender) Status(status int) { + r.WriteHeader(status) +} + +func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { + if len(htmlOpt) > 0 { + return htmlOpt[0] + } + + return HTMLOptions{ + Layout: r.Opt.Layout, + } +} + +func (r *TplRender) SetTemplatePath(setName, dir string) { + if len(setName) == 0 { + setName = DEFAULT_TPL_SET_NAME + } + opt := *r.Opt + opt.Directory = dir + r.TemplateSet.Set(setName, &opt) +} + +func (r *TplRender) HasTemplateSet(name string) bool { + return r.TemplateSet.Get(name) != nil +} + +// DummyRender is used when user does not choose any real render to use. +// This way, we can print out friendly message which asks them to register one, +// instead of ugly and confusing 'nil pointer' panic. +type DummyRender struct { + http.ResponseWriter +} + +func renderNotRegistered() { + panic("middleware render hasn't been registered") +} + +func (r *DummyRender) SetResponseWriter(http.ResponseWriter) { + renderNotRegistered() +} + +func (r *DummyRender) JSON(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) JSONString(interface{}) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) RawData(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) PlainText(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) HTML(int, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSet(int, string, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLString(string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) XML(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) Error(int, ...string) { + renderNotRegistered() +} + +func (r *DummyRender) Status(int) { + renderNotRegistered() +} + +func (r *DummyRender) SetTemplatePath(string, string) { + renderNotRegistered() +} + +func (r *DummyRender) HasTemplateSet(string) bool { + renderNotRegistered() + return false +} |