| 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.go
 
- package middleware
 
- import (
 
- 	"bytes"
 
- 	"encoding/json"
 
- 	"fmt"
 
- 	"html/template"
 
- 	"io"
 
- 	"io/ioutil"
 
- 	"net/http"
 
- 	"os"
 
- 	"path/filepath"
 
- 	"time"
 
- 	"github.com/codegangsta/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.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,
 
- 	}
 
- }
 
 
  |