|
@@ -1,286 +0,0 @@
|
|
|
-// 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,
|
|
|
- }
|
|
|
-}
|