commit
						8e1f339fd6
					
				@ -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, | 
				
			||||||
 | 
						} | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue