forked from redneckbeard/gadget
/
template.go
106 lines (99 loc) · 3.32 KB
/
template.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package templates
import (
"bytes"
"fmt"
"github.com/redneckbeard/gadget"
"github.com/redneckbeard/gadget/env"
"html/template"
"strconv"
"text/template/parse"
)
var (
registry = make(template.FuncMap)
TemplatePath = "templates"
)
// AddHelper registers functions with TemplateBroker that will be available in
// templates during rendering.
func AddHelper(name string, f interface{}) {
registry[name] = f
}
func templatePath(components ...string) string {
components[len(components)-1] += ".html"
return env.RelPath(append([]string{TemplatePath}, components...)...)
}
// TemplateBroker attempts to render interface{} value body as the context of a
// html/template.Template. It requires adherence to a few simple conventions
// for locating templates: 1) all templates are inside a "templates" directory
// in the root directory of the Gadget application; 2) a "templates/base.html"
// file exists and can be parsed as a Go template; 3) templates will be loaded
// from a directory matching the lower-cased plural name of the controller and
// the lower-cased name of the action, plus the extension ".html". For example,
// the Show method of a FavoriteController would look for a template
// "templates/favorites/show.html."
//
// All error codes can also be served via their own templates. Non-200 statuses
// will result in TemplateBroker looking for a "templates/403.html",
// "templates/502.html", etc.
func TemplateBroker(r *gadget.Request, status int, body interface{}, data *gadget.RouteData) (int, string) {
var helpers = make(template.FuncMap)
helpers["request"] = func() *gadget.Request {
return r
}
helpers["render"] = func(templateName string, context interface{}) template.HTML {
var (
t *template.Template
err error
)
t, err = template.New(templateName + ".html").Funcs(helpers).ParseFiles(templatePath(data.ControllerName, templateName))
if err != nil {
t, err = template.New(templateName + ".html").Funcs(helpers).ParseFiles(templatePath(templateName))
if err != nil {
panic(fmt.Sprintf("Could not locate subtemplate at %s or %s", templatePath(data.ControllerName, templateName), templatePath(templateName)))
}
}
buf := new(bytes.Buffer)
err = t.Execute(buf, context)
if err != nil {
panic(err)
}
return template.HTML(string(buf.Bytes()))
}
for name, helper := range registry {
helpers[name] = helper
}
t, err := template.New("base.html").Funcs(helpers).ParseFiles(templatePath("base"))
if err != nil {
return 404, err.Error()
}
var mainTemplatePath string
if status >= 200 && status < 300 {
mainTemplatePath = templatePath(data.ControllerName, data.Action)
} else {
if status == 500 && env.Debug {
t, _ = template.New("debug").Parse(SERVER_ERROR_TEMPLATE)
} else {
mainTemplatePath = templatePath(strconv.FormatInt(int64(status), 10))
}
}
if mainTemplatePath != "" {
_, err = t.ParseFiles(mainTemplatePath)
if err != nil {
return 500, err.Error()
}
// fill in any undefined templates that are called in base
for _, node := range t.Tree.Root.Nodes {
if node.Type() == parse.NodeTemplate {
tnode := node.(*parse.TemplateNode)
if subt := t.Lookup(tnode.Name); subt == nil {
t.New(tnode.Name).Parse("")
}
}
}
}
buf := new(bytes.Buffer)
err = t.Execute(buf, body)
if err != nil {
return 500, err.Error()
}
return status, string(buf.Bytes())
}