|  | @@ -0,0 +1,286 @@
 | 
	
		
			
				|  |  | +// 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) {
 | 
	
		
			
				|  |  | +	r.WriteHeader(status)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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,
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 |