blob: bb9140a4daf0855433ad0565cf109b70eeb0ac00 [file] [log] [blame]
Mike Samuela5291092011-11-04 13:09:21 -04001// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Rob Pike6ab6c492011-11-08 15:38:47 -08005package template
Mike Samuela5291092011-11-04 13:09:21 -04006
7import (
8 "fmt"
9 "io"
Rob Pike07ee3cc2011-11-30 17:42:18 -050010 "io/ioutil"
Mike Samuela5291092011-11-04 13:09:21 -040011 "path/filepath"
Rob Pike9a86e242011-11-30 20:11:57 -080012 "sync"
Rob Pike6ab6c492011-11-08 15:38:47 -080013 "text/template"
Rob Piked38cc472011-12-01 09:19:53 -080014 "text/template/parse"
Mike Samuela5291092011-11-04 13:09:21 -040015)
16
Francesc Campoy1590be92013-01-14 12:11:22 +000017// Template is a specialized Template from "text/template" that produces a safe
18// HTML document fragment.
Mike Samuela5291092011-11-04 13:09:21 -040019type Template struct {
Andrew Gerrand0fee6332014-08-19 14:24:14 +100020 // Sticky error if escaping fails.
21 escapeErr error
Rob Pike07ee3cc2011-11-30 17:42:18 -050022 // We could embed the text/template field, but it's safer not to because
23 // we need to keep our version of the name space and the underlying
24 // template's in sync.
Rob Pike80f39f72013-09-05 08:23:11 +100025 text *template.Template
26 // The underlying template's parse tree, updated to be HTML-safe.
27 Tree *parse.Tree
Rob Pike9a86e242011-11-30 20:11:57 -080028 *nameSpace // common to all associated templates
29}
30
Andrew Gerrand0fee6332014-08-19 14:24:14 +100031// escapeOK is a sentinel value used to indicate valid escaping.
32var escapeOK = fmt.Errorf("template escaped correctly")
33
Rob Pike9a86e242011-11-30 20:11:57 -080034// nameSpace is the data structure shared by all templates in an association.
35type nameSpace struct {
36 mu sync.Mutex
37 set map[string]*Template
38}
39
Rob Pike49be7f72012-03-20 14:38:07 +110040// Templates returns a slice of the templates associated with t, including t
41// itself.
42func (t *Template) Templates() []*Template {
43 ns := t.nameSpace
44 ns.mu.Lock()
45 defer ns.mu.Unlock()
46 // Return a slice so we don't expose the map.
47 m := make([]*Template, 0, len(ns.set))
48 for _, v := range ns.set {
49 m = append(m, v)
50 }
51 return m
52}
53
Rob Pike4e5ac452015-04-03 13:10:47 -070054// Option sets options for the template. Options are described by
55// strings, either a simple string or "key=value". There can be at
56// most one equals sign in an option string. If the option string
57// is unrecognized or otherwise invalid, Option panics.
58//
59// Known options:
60//
61// missingkey: Control the behavior during execution if a map is
62// indexed with a key that is not present in the map.
63// "missingkey=default" or "missingkey=invalid"
64// The default behavior: Do nothing and continue execution.
65// If printed, the result of the index operation is the string
66// "<no value>".
67// "missingkey=zero"
68// The operation returns the zero value for the map type's element.
69// "missingkey=error"
70// Execution stops immediately with an error.
71//
72func (t *Template) Option(opt ...string) *Template {
73 t.text.Option(opt...)
74 return t
75}
76
Robert Figueiredoe1a5aa82013-03-12 14:35:14 -070077// escape escapes all associated templates.
78func (t *Template) escape() error {
79 t.nameSpace.mu.Lock()
80 defer t.nameSpace.mu.Unlock()
Andrew Gerrand0fee6332014-08-19 14:24:14 +100081 if t.escapeErr == nil {
Rob Pike11dba2e2015-03-20 10:47:52 -070082 if t.Tree == nil {
83 return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.text.DefinedTemplates())
84 }
Rob Pike1ad1c0b2014-08-29 09:54:00 -070085 if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
Robert Figueiredoe1a5aa82013-03-12 14:35:14 -070086 return err
87 }
Andrew Gerrand0fee6332014-08-19 14:24:14 +100088 } else if t.escapeErr != escapeOK {
89 return t.escapeErr
Robert Figueiredoe1a5aa82013-03-12 14:35:14 -070090 }
91 return nil
92}
93
Rob Pike9a86e242011-11-30 20:11:57 -080094// Execute applies a parsed template to the specified data object,
95// writing the output to wr.
Rob Pike431b96b2014-05-19 14:29:45 -070096// If an error occurs executing the template or writing its output,
97// execution stops, but partial results may already have been written to
98// the output writer.
Rob Pikeaeb37522014-04-15 08:48:40 -070099// A template may be executed safely in parallel.
Robert Figueiredoe1a5aa82013-03-12 14:35:14 -0700100func (t *Template) Execute(wr io.Writer, data interface{}) error {
101 if err := t.escape(); err != nil {
102 return err
Rob Pike9a86e242011-11-30 20:11:57 -0800103 }
104 return t.text.Execute(wr, data)
Mike Samuela5291092011-11-04 13:09:21 -0400105}
106
Rob Pikeee8b5972011-12-06 12:47:12 -0800107// ExecuteTemplate applies the template associated with t that has the given
108// name to the specified data object and writes the output to wr.
Rob Pike431b96b2014-05-19 14:29:45 -0700109// If an error occurs executing the template or writing its output,
110// execution stops, but partial results may already have been written to
111// the output writer.
Rob Pikeaeb37522014-04-15 08:48:40 -0700112// A template may be executed safely in parallel.
Rob Pike0397b282011-12-08 10:15:53 -0800113func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
Nigel Tao0c523942012-02-15 16:16:30 +1100114 tmpl, err := t.lookupAndEscapeTemplate(name)
Rob Pike0397b282011-12-08 10:15:53 -0800115 if err != nil {
116 return err
117 }
118 return tmpl.text.Execute(wr, data)
119}
120
121// lookupAndEscapeTemplate guarantees that the template with the given name
122// is escaped, or returns an error if it cannot be. It returns the named
123// template.
Nigel Tao0c523942012-02-15 16:16:30 +1100124func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
Rob Pike9a86e242011-11-30 20:11:57 -0800125 t.nameSpace.mu.Lock()
Rob Pike0397b282011-12-08 10:15:53 -0800126 defer t.nameSpace.mu.Unlock()
127 tmpl = t.set[name]
Rob Pike214a1ca2012-03-14 15:08:54 +1100128 if tmpl == nil {
129 return nil, fmt.Errorf("html/template: %q is undefined", name)
130 }
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000131 if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
132 return nil, tmpl.escapeErr
133 }
Rob Pike214a1ca2012-03-14 15:08:54 +1100134 if tmpl.text.Tree == nil || tmpl.text.Root == nil {
135 return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
136 }
137 if t.text.Lookup(name) == nil {
Rob Pikeee8b5972011-12-06 12:47:12 -0800138 panic("html/template internal error: template escaping out of sync")
Rob Pike07ee3cc2011-11-30 17:42:18 -0500139 }
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000140 if tmpl.escapeErr == nil {
Rob Pike1ad1c0b2014-08-29 09:54:00 -0700141 err = escapeTemplate(tmpl, tmpl.text.Root, name)
Rob Pike9a86e242011-11-30 20:11:57 -0800142 }
Rob Pike0397b282011-12-08 10:15:53 -0800143 return tmpl, err
Mike Samuela5291092011-11-04 13:09:21 -0400144}
145
Rob Piked38cc472011-12-01 09:19:53 -0800146// Parse parses a string into a template. Nested template definitions
147// will be associated with the top-level template t. Parse may be
148// called multiple times to parse definitions of templates to associate
149// with t. It is an error if a resulting template is non-empty (contains
150// content other than template definitions) and would replace a
151// non-empty template with the same name. (In multiple calls to Parse
152// with the same receiver template, only one call can contain text
153// other than space, comments, and template definitions.)
Rob Pike07ee3cc2011-11-30 17:42:18 -0500154func (t *Template) Parse(src string) (*Template, error) {
Rob Pike9a86e242011-11-30 20:11:57 -0800155 t.nameSpace.mu.Lock()
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000156 t.escapeErr = nil
Rob Pike9a86e242011-11-30 20:11:57 -0800157 t.nameSpace.mu.Unlock()
Rob Pike07ee3cc2011-11-30 17:42:18 -0500158 ret, err := t.text.Parse(src)
Mike Samuela5291092011-11-04 13:09:21 -0400159 if err != nil {
160 return nil, err
161 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500162 // In general, all the named templates might have changed underfoot.
163 // Regardless, some new ones may have been defined.
164 // The template.Template set has been updated; update ours.
Rob Pike9a86e242011-11-30 20:11:57 -0800165 t.nameSpace.mu.Lock()
166 defer t.nameSpace.mu.Unlock()
Rob Pike07ee3cc2011-11-30 17:42:18 -0500167 for _, v := range ret.Templates() {
168 name := v.Name()
Rob Pike9a86e242011-11-30 20:11:57 -0800169 tmpl := t.set[name]
Rob Pike07ee3cc2011-11-30 17:42:18 -0500170 if tmpl == nil {
Rob Pike9a86e242011-11-30 20:11:57 -0800171 tmpl = t.new(name)
Rob Pike07ee3cc2011-11-30 17:42:18 -0500172 }
Rob Pikee2e9d1d2013-09-25 10:00:09 +1000173 // Restore our record of this text/template to its unescaped original state.
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000174 tmpl.escapeErr = nil
Rob Pike07ee3cc2011-11-30 17:42:18 -0500175 tmpl.text = v
Rob Pikee2e9d1d2013-09-25 10:00:09 +1000176 tmpl.Tree = v.Tree
Mike Samuela5291092011-11-04 13:09:21 -0400177 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500178 return t, nil
Mike Samuela5291092011-11-04 13:09:21 -0400179}
180
Nigel Tao0c523942012-02-15 16:16:30 +1100181// AddParseTree creates a new template with the name and parse tree
182// and associates it with t.
183//
184// It returns an error if t has already been executed.
185func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
186 t.nameSpace.mu.Lock()
187 defer t.nameSpace.mu.Unlock()
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000188 if t.escapeErr != nil {
Nigel Tao0c523942012-02-15 16:16:30 +1100189 return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
190 }
191 text, err := t.text.AddParseTree(name, tree)
192 if err != nil {
193 return nil, err
194 }
195 ret := &Template{
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000196 nil,
Nigel Tao0c523942012-02-15 16:16:30 +1100197 text,
Rob Pike80f39f72013-09-05 08:23:11 +1000198 text.Tree,
Nigel Tao0c523942012-02-15 16:16:30 +1100199 t.nameSpace,
200 }
201 t.set[name] = ret
202 return ret, nil
Rob Pike07ee3cc2011-11-30 17:42:18 -0500203}
204
Nigel Tao0c523942012-02-15 16:16:30 +1100205// Clone returns a duplicate of the template, including all associated
206// templates. The actual representation is not copied, but the name space of
207// associated templates is, so further calls to Parse in the copy will add
208// templates to the copy but not to the original. Clone can be used to prepare
209// common templates and use them with variant definitions for other templates
210// by adding the variants after the clone is made.
211//
212// It returns an error if t has already been executed.
213func (t *Template) Clone() (*Template, error) {
214 t.nameSpace.mu.Lock()
215 defer t.nameSpace.mu.Unlock()
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000216 if t.escapeErr != nil {
Nigel Tao0c523942012-02-15 16:16:30 +1100217 return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
218 }
219 textClone, err := t.text.Clone()
220 if err != nil {
221 return nil, err
222 }
223 ret := &Template{
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000224 nil,
Nigel Tao0c523942012-02-15 16:16:30 +1100225 textClone,
Rob Pike80f39f72013-09-05 08:23:11 +1000226 textClone.Tree,
Nigel Tao0c523942012-02-15 16:16:30 +1100227 &nameSpace{
228 set: make(map[string]*Template),
229 },
230 }
231 for _, x := range textClone.Templates() {
232 name := x.Name()
233 src := t.set[name]
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000234 if src == nil || src.escapeErr != nil {
Nigel Tao0c523942012-02-15 16:16:30 +1100235 return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
236 }
Josh Bleecher Snydereeb75852013-09-17 14:19:44 +1000237 x.Tree = x.Tree.Copy()
Nigel Tao0c523942012-02-15 16:16:30 +1100238 ret.set[name] = &Template{
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000239 nil,
Nigel Tao0c523942012-02-15 16:16:30 +1100240 x,
Rob Pike80f39f72013-09-05 08:23:11 +1000241 x.Tree,
Nigel Tao0c523942012-02-15 16:16:30 +1100242 ret.nameSpace,
243 }
244 }
245 return ret, nil
Mike Samuela5291092011-11-04 13:09:21 -0400246}
247
248// New allocates a new HTML template with the given name.
249func New(name string) *Template {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500250 tmpl := &Template{
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000251 nil,
Rob Pike07ee3cc2011-11-30 17:42:18 -0500252 template.New(name),
Rob Pike80f39f72013-09-05 08:23:11 +1000253 nil,
Rob Pike9a86e242011-11-30 20:11:57 -0800254 &nameSpace{
255 set: make(map[string]*Template),
256 },
Rob Pike07ee3cc2011-11-30 17:42:18 -0500257 }
Rob Pike9a86e242011-11-30 20:11:57 -0800258 tmpl.set[name] = tmpl
Rob Pike07ee3cc2011-11-30 17:42:18 -0500259 return tmpl
260}
261
262// New allocates a new HTML template associated with the given one
263// and with the same delimiters. The association, which is transitive,
264// allows one template to invoke another with a {{template}} action.
265func (t *Template) New(name string) *Template {
Rob Pike9a86e242011-11-30 20:11:57 -0800266 t.nameSpace.mu.Lock()
267 defer t.nameSpace.mu.Unlock()
268 return t.new(name)
269}
270
271// new is the implementation of New, without the lock.
272func (t *Template) new(name string) *Template {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500273 tmpl := &Template{
Andrew Gerrand0fee6332014-08-19 14:24:14 +1000274 nil,
Rob Pike07ee3cc2011-11-30 17:42:18 -0500275 t.text.New(name),
Rob Pike80f39f72013-09-05 08:23:11 +1000276 nil,
Rob Pike9a86e242011-11-30 20:11:57 -0800277 t.nameSpace,
Rob Pike07ee3cc2011-11-30 17:42:18 -0500278 }
Rob Pike9a86e242011-11-30 20:11:57 -0800279 tmpl.set[name] = tmpl
Rob Pike07ee3cc2011-11-30 17:42:18 -0500280 return tmpl
281}
282
283// Name returns the name of the template.
284func (t *Template) Name() string {
285 return t.text.Name()
286}
287
Rob Pike1402d1a2011-12-14 11:22:17 -0800288// FuncMap is the type of the map defining the mapping from names to
289// functions. Each function must have either a single return value, or two
290// return values of which the second has type error. In that case, if the
291// second (error) argument evaluates to non-nil during execution, execution
292// terminates and Execute returns that error. FuncMap has the same base type
Francesc Campoy1590be92013-01-14 12:11:22 +0000293// as FuncMap in "text/template", copied here so clients need not import
294// "text/template".
Rob Pike1402d1a2011-12-14 11:22:17 -0800295type FuncMap map[string]interface{}
296
Rob Pike07ee3cc2011-11-30 17:42:18 -0500297// Funcs adds the elements of the argument map to the template's function map.
298// It panics if a value in the map is not a function with appropriate return
299// type. However, it is legal to overwrite elements of the map. The return
300// value is the template, so calls can be chained.
Rob Pike1402d1a2011-12-14 11:22:17 -0800301func (t *Template) Funcs(funcMap FuncMap) *Template {
302 t.text.Funcs(template.FuncMap(funcMap))
Rob Pike07ee3cc2011-11-30 17:42:18 -0500303 return t
304}
305
306// Delims sets the action delimiters to the specified strings, to be used in
307// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
308// definitions will inherit the settings. An empty delimiter stands for the
309// corresponding default: {{ or }}.
310// The return value is the template, so calls can be chained.
311func (t *Template) Delims(left, right string) *Template {
312 t.text.Delims(left, right)
313 return t
314}
315
316// Lookup returns the template with the given name that is associated with t,
317// or nil if there is no such template.
318func (t *Template) Lookup(name string) *Template {
Rob Pike9a86e242011-11-30 20:11:57 -0800319 t.nameSpace.mu.Lock()
320 defer t.nameSpace.mu.Unlock()
321 return t.set[name]
Mike Samuela5291092011-11-04 13:09:21 -0400322}
323
Francesc Campoy1590be92013-01-14 12:11:22 +0000324// Must is a helper that wraps a call to a function returning (*Template, error)
325// and panics if the error is non-nil. It is intended for use in variable initializations
326// such as
327// var t = template.Must(template.New("name").Parse("html"))
Mike Samuela5291092011-11-04 13:09:21 -0400328func Must(t *Template, err error) *Template {
Rob Pike59128692011-12-09 10:47:36 -0800329 if err != nil {
330 panic(err)
331 }
Mike Samuela5291092011-11-04 13:09:21 -0400332 return t
333}
334
Rob Pike07ee3cc2011-11-30 17:42:18 -0500335// ParseFiles creates a new Template and parses the template definitions from
336// the named files. The returned template's name will have the (base) name and
337// (parsed) contents of the first file. There must be at least one file.
338// If an error occurs, parsing stops and the returned *Template is nil.
339func ParseFiles(filenames ...string) (*Template, error) {
340 return parseFiles(nil, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400341}
342
Rob Pike07ee3cc2011-11-30 17:42:18 -0500343// ParseFiles parses the named files and associates the resulting templates with
344// t. If an error occurs, parsing stops and the returned template is nil;
345// otherwise it is t. There must be at least one file.
346func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
347 return parseFiles(t, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400348}
349
Rob Pike07ee3cc2011-11-30 17:42:18 -0500350// parseFiles is the helper for the method and function. If the argument
351// template is nil, it is created from the first file.
352func parseFiles(t *Template, filenames ...string) (*Template, error) {
353 if len(filenames) == 0 {
354 // Not really a problem, but be consistent.
Rob Pike214a1ca2012-03-14 15:08:54 +1100355 return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
Mike Samuela5291092011-11-04 13:09:21 -0400356 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500357 for _, filename := range filenames {
358 b, err := ioutil.ReadFile(filename)
359 if err != nil {
360 return nil, err
361 }
362 s := string(b)
363 name := filepath.Base(filename)
364 // First template becomes return value if not already defined,
365 // and we use that one for subsequent New calls to associate
366 // all the templates together. Also, if this file has the same name
367 // as t, this file becomes the contents of t, so
368 // t, err := New(name).Funcs(xxx).ParseFiles(name)
369 // works. Otherwise we create a new template associated with t.
370 var tmpl *Template
371 if t == nil {
372 t = New(name)
373 }
374 if name == t.Name() {
375 tmpl = t
376 } else {
377 tmpl = t.New(name)
378 }
379 _, err = tmpl.Parse(s)
380 if err != nil {
381 return nil, err
382 }
383 }
384 return t, nil
Mike Samuela5291092011-11-04 13:09:21 -0400385}
386
Rob Pike07ee3cc2011-11-30 17:42:18 -0500387// ParseGlob creates a new Template and parses the template definitions from the
388// files identified by the pattern, which must match at least one file. The
389// returned template will have the (base) name and (parsed) contents of the
390// first file matched by the pattern. ParseGlob is equivalent to calling
391// ParseFiles with the list of files matched by the pattern.
392func ParseGlob(pattern string) (*Template, error) {
393 return parseGlob(nil, pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400394}
395
Rob Pike07ee3cc2011-11-30 17:42:18 -0500396// ParseGlob parses the template definitions in the files identified by the
397// pattern and associates the resulting templates with t. The pattern is
398// processed by filepath.Glob and must match at least one file. ParseGlob is
399// equivalent to calling t.ParseFiles with the list of files matched by the
400// pattern.
401func (t *Template) ParseGlob(pattern string) (*Template, error) {
402 return parseGlob(t, pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400403}
404
Rob Pike07ee3cc2011-11-30 17:42:18 -0500405// parseGlob is the implementation of the function and method ParseGlob.
406func parseGlob(t *Template, pattern string) (*Template, error) {
Mike Samuela5291092011-11-04 13:09:21 -0400407 filenames, err := filepath.Glob(pattern)
408 if err != nil {
409 return nil, err
410 }
411 if len(filenames) == 0 {
Rob Pike214a1ca2012-03-14 15:08:54 +1100412 return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400413 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500414 return parseFiles(t, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400415}