You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							725 lines
						
					
					
						
							18 KiB
						
					
					
				
			
		
		
	
	
							725 lines
						
					
					
						
							18 KiB
						
					
					
				| // 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"
 | |
| 	"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
 | |
| 		Get(string) (io.Reader, error)
 | |
| 	}
 | |
| 
 | |
| 	// 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 (fs TplFileSystem) Get(name string) (io.Reader, error) {
 | |
| 	for i := range fs.files {
 | |
| 		if fs.files[i].Name()+fs.files[i].Ext() == name {
 | |
| 			return bytes.NewReader(fs.files[i].Data()), nil
 | |
| 		}
 | |
| 	}
 | |
| 	return nil, fmt.Errorf("file '%s' not found", name)
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 |