| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 | // Copyright 2014 The Gogs Authors. All rights reserved.// Use of this source code is governed by a MIT-style// license that can be found in the LICENSE file.// foked from https://github.com/martini-contrib/render/blob/master/render.gopackage middlewareimport (	"bytes"	"encoding/json"	"fmt"	"html/template"	"io"	"io/ioutil"	"net/http"	"os"	"path/filepath"	"time"	"github.com/go-martini/martini"	"github.com/gogits/gogs/modules/base")const (	ContentType    = "Content-Type"	ContentLength  = "Content-Length"	ContentJSON    = "application/json"	ContentHTML    = "text/html"	ContentXHTML   = "application/xhtml+xml"	defaultCharset = "UTF-8")var helperFuncs = template.FuncMap{	"yield": func() (string, error) {		return "", fmt.Errorf("yield called with no layout defined")	},}type Delims struct {	Left string	Right string}type RenderOptions struct {	Directory string	Layout string	Extensions []string	Funcs []template.FuncMap	Delims Delims	Charset string	IndentJSON bool	HTMLContentType string}type HTMLOptions struct {	Layout string}func Renderer(options ...RenderOptions) martini.Handler {	opt := prepareOptions(options)	cs := prepareCharset(opt.Charset)	t := compile(opt)	return func(res http.ResponseWriter, req *http.Request, c martini.Context) {		var tc *template.Template		if martini.Env == martini.Dev {			tc = compile(opt)		} else {			tc, _ = t.Clone()		}		rd := &Render{res, req, tc, opt, cs, base.TmplData{}, time.Time{}}		rd.Data["TmplLoadTimes"] = func() string {			if rd.startTime.IsZero() {				return ""			}			return fmt.Sprint(time.Since(rd.startTime).Nanoseconds()/1e6) + "ms"		}		c.Map(rd.Data)		c.Map(rd)	}}func prepareCharset(charset string) string {	if len(charset) != 0 {		return "; charset=" + charset	}	return "; charset=" + defaultCharset}func prepareOptions(options []RenderOptions) RenderOptions {	var opt RenderOptions	if len(options) > 0 {		opt = options[0]	}	if len(opt.Directory) == 0 {		opt.Directory = "templates"	}	if len(opt.Extensions) == 0 {		opt.Extensions = []string{".tmpl"}	}	if len(opt.HTMLContentType) == 0 {		opt.HTMLContentType = ContentHTML	}	return opt}func compile(options RenderOptions) *template.Template {	dir := options.Directory	t := template.New(dir)	t.Delims(options.Delims.Left, options.Delims.Right)	template.Must(t.Parse("Martini"))	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {		r, err := filepath.Rel(dir, path)		if err != nil {			return err		}		ext := filepath.Ext(r)		for _, extension := range options.Extensions {			if ext == extension {				buf, err := ioutil.ReadFile(path)				if err != nil {					panic(err)				}				name := (r[0 : len(r)-len(ext)])				tmpl := t.New(filepath.ToSlash(name))				for _, funcs := range options.Funcs {					tmpl = tmpl.Funcs(funcs)				}				template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))				break			}		}		return nil	})	return t}type Render struct {	http.ResponseWriter	req             *http.Request	t               *template.Template	opt             RenderOptions	compiledCharset string	Data base.TmplData	startTime time.Time}func (r *Render) JSON(status int, v interface{}) {	var result []byte	var 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	}	r.Header().Set(ContentType, ContentJSON+r.compiledCharset)	r.WriteHeader(status)	r.Write(result)}func (r *Render) 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 *Render) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) {	opt := r.prepareHTMLOptions(htmlOpt)	if len(opt.Layout) > 0 {		r.addYield(name, binding)		name = opt.Layout	}	out, err := r.execute(name, binding)	if err != nil {		return nil, err	}	return out, nil}func (r *Render) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) {	r.startTime = time.Now()	out, err := r.renderBytes(name, binding, htmlOpt...)	if err != nil {		http.Error(r, err.Error(), http.StatusInternalServerError)		return	}	r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset)	r.WriteHeader(status)	io.Copy(r, out)}func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) {	if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil {		return "", err	} else {		return out.String(), nil	}}func (r *Render) Error(status int, message ...string) {	r.WriteHeader(status)	if len(message) > 0 {		r.Write([]byte(message[0]))	}}func (r *Render) Redirect(location string, status ...int) {	code := http.StatusFound	if len(status) == 1 {		code = status[0]	}	http.Redirect(r, r.req, location, code)}func (r *Render) Template() *template.Template {	return r.t}func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) {	buf := new(bytes.Buffer)	return buf, r.t.ExecuteTemplate(buf, name, binding)}func (r *Render) addYield(name string, binding interface{}) {	funcs := template.FuncMap{		"yield": func() (template.HTML, error) {			buf, err := r.execute(name, binding)			return template.HTML(buf.String()), err		},	}	r.t.Funcs(funcs)}func (r *Render) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {	if len(htmlOpt) > 0 {		return htmlOpt[0]	}	return HTMLOptions{		Layout: r.opt.Layout,	}}
 |