Home | History | Annotate | Download | only in template
      1 // 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 
      5 package template
      6 
      7 import (
      8 	"fmt"
      9 	"io"
     10 	"io/ioutil"
     11 	"path/filepath"
     12 	"sync"
     13 	"text/template"
     14 	"text/template/parse"
     15 )
     16 
     17 // Template is a specialized Template from "text/template" that produces a safe
     18 // HTML document fragment.
     19 type Template struct {
     20 	// Sticky error if escaping fails, or escapeOK if succeeded.
     21 	escapeErr error
     22 	// 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.
     25 	text *template.Template
     26 	// The underlying template's parse tree, updated to be HTML-safe.
     27 	Tree       *parse.Tree
     28 	*nameSpace // common to all associated templates
     29 }
     30 
     31 // escapeOK is a sentinel value used to indicate valid escaping.
     32 var escapeOK = fmt.Errorf("template escaped correctly")
     33 
     34 // nameSpace is the data structure shared by all templates in an association.
     35 type nameSpace struct {
     36 	mu      sync.Mutex
     37 	set     map[string]*Template
     38 	escaped bool
     39 }
     40 
     41 // Templates returns a slice of the templates associated with t, including t
     42 // itself.
     43 func (t *Template) Templates() []*Template {
     44 	ns := t.nameSpace
     45 	ns.mu.Lock()
     46 	defer ns.mu.Unlock()
     47 	// Return a slice so we don't expose the map.
     48 	m := make([]*Template, 0, len(ns.set))
     49 	for _, v := range ns.set {
     50 		m = append(m, v)
     51 	}
     52 	return m
     53 }
     54 
     55 // Option sets options for the template. Options are described by
     56 // strings, either a simple string or "key=value". There can be at
     57 // most one equals sign in an option string. If the option string
     58 // is unrecognized or otherwise invalid, Option panics.
     59 //
     60 // Known options:
     61 //
     62 // missingkey: Control the behavior during execution if a map is
     63 // indexed with a key that is not present in the map.
     64 //	"missingkey=default" or "missingkey=invalid"
     65 //		The default behavior: Do nothing and continue execution.
     66 //		If printed, the result of the index operation is the string
     67 //		"<no value>".
     68 //	"missingkey=zero"
     69 //		The operation returns the zero value for the map type's element.
     70 //	"missingkey=error"
     71 //		Execution stops immediately with an error.
     72 //
     73 func (t *Template) Option(opt ...string) *Template {
     74 	t.text.Option(opt...)
     75 	return t
     76 }
     77 
     78 // checkCanParse checks whether it is OK to parse templates.
     79 // If not, it returns an error.
     80 func (t *Template) checkCanParse() error {
     81 	if t == nil {
     82 		return nil
     83 	}
     84 	t.nameSpace.mu.Lock()
     85 	defer t.nameSpace.mu.Unlock()
     86 	if t.nameSpace.escaped {
     87 		return fmt.Errorf("html/template: cannot Parse after Execute")
     88 	}
     89 	return nil
     90 }
     91 
     92 // escape escapes all associated templates.
     93 func (t *Template) escape() error {
     94 	t.nameSpace.mu.Lock()
     95 	defer t.nameSpace.mu.Unlock()
     96 	t.nameSpace.escaped = true
     97 	if t.escapeErr == nil {
     98 		if t.Tree == nil {
     99 			return fmt.Errorf("template: %q is an incomplete or empty template", t.Name())
    100 		}
    101 		if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
    102 			return err
    103 		}
    104 	} else if t.escapeErr != escapeOK {
    105 		return t.escapeErr
    106 	}
    107 	return nil
    108 }
    109 
    110 // Execute applies a parsed template to the specified data object,
    111 // writing the output to wr.
    112 // If an error occurs executing the template or writing its output,
    113 // execution stops, but partial results may already have been written to
    114 // the output writer.
    115 // A template may be executed safely in parallel.
    116 func (t *Template) Execute(wr io.Writer, data interface{}) error {
    117 	if err := t.escape(); err != nil {
    118 		return err
    119 	}
    120 	return t.text.Execute(wr, data)
    121 }
    122 
    123 // ExecuteTemplate applies the template associated with t that has the given
    124 // name to the specified data object and writes the output to wr.
    125 // If an error occurs executing the template or writing its output,
    126 // execution stops, but partial results may already have been written to
    127 // the output writer.
    128 // A template may be executed safely in parallel.
    129 func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
    130 	tmpl, err := t.lookupAndEscapeTemplate(name)
    131 	if err != nil {
    132 		return err
    133 	}
    134 	return tmpl.text.Execute(wr, data)
    135 }
    136 
    137 // lookupAndEscapeTemplate guarantees that the template with the given name
    138 // is escaped, or returns an error if it cannot be. It returns the named
    139 // template.
    140 func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
    141 	t.nameSpace.mu.Lock()
    142 	defer t.nameSpace.mu.Unlock()
    143 	t.nameSpace.escaped = true
    144 	tmpl = t.set[name]
    145 	if tmpl == nil {
    146 		return nil, fmt.Errorf("html/template: %q is undefined", name)
    147 	}
    148 	if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
    149 		return nil, tmpl.escapeErr
    150 	}
    151 	if tmpl.text.Tree == nil || tmpl.text.Root == nil {
    152 		return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
    153 	}
    154 	if t.text.Lookup(name) == nil {
    155 		panic("html/template internal error: template escaping out of sync")
    156 	}
    157 	if tmpl.escapeErr == nil {
    158 		err = escapeTemplate(tmpl, tmpl.text.Root, name)
    159 	}
    160 	return tmpl, err
    161 }
    162 
    163 // DefinedTemplates returns a string listing the defined templates,
    164 // prefixed by the string "; defined templates are: ". If there are none,
    165 // it returns the empty string. Used to generate an error message.
    166 func (t *Template) DefinedTemplates() string {
    167 	return t.text.DefinedTemplates()
    168 }
    169 
    170 // Parse parses text as a template body for t.
    171 // Named template definitions ({{define ...}} or {{block ...}} statements) in text
    172 // define additional templates associated with t and are removed from the
    173 // definition of t itself.
    174 //
    175 // Templates can be redefined in successive calls to Parse,
    176 // before the first use of Execute on t or any associated template.
    177 // A template definition with a body containing only white space and comments
    178 // is considered empty and will not replace an existing template's body.
    179 // This allows using Parse to add new named template definitions without
    180 // overwriting the main template body.
    181 func (t *Template) Parse(text string) (*Template, error) {
    182 	if err := t.checkCanParse(); err != nil {
    183 		return nil, err
    184 	}
    185 
    186 	ret, err := t.text.Parse(text)
    187 	if err != nil {
    188 		return nil, err
    189 	}
    190 
    191 	// In general, all the named templates might have changed underfoot.
    192 	// Regardless, some new ones may have been defined.
    193 	// The template.Template set has been updated; update ours.
    194 	t.nameSpace.mu.Lock()
    195 	defer t.nameSpace.mu.Unlock()
    196 	for _, v := range ret.Templates() {
    197 		name := v.Name()
    198 		tmpl := t.set[name]
    199 		if tmpl == nil {
    200 			tmpl = t.new(name)
    201 		}
    202 		tmpl.text = v
    203 		tmpl.Tree = v.Tree
    204 	}
    205 	return t, nil
    206 }
    207 
    208 // AddParseTree creates a new template with the name and parse tree
    209 // and associates it with t.
    210 //
    211 // It returns an error if t or any associated template has already been executed.
    212 func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
    213 	if err := t.checkCanParse(); err != nil {
    214 		return nil, err
    215 	}
    216 
    217 	t.nameSpace.mu.Lock()
    218 	defer t.nameSpace.mu.Unlock()
    219 	text, err := t.text.AddParseTree(name, tree)
    220 	if err != nil {
    221 		return nil, err
    222 	}
    223 	ret := &Template{
    224 		nil,
    225 		text,
    226 		text.Tree,
    227 		t.nameSpace,
    228 	}
    229 	t.set[name] = ret
    230 	return ret, nil
    231 }
    232 
    233 // Clone returns a duplicate of the template, including all associated
    234 // templates. The actual representation is not copied, but the name space of
    235 // associated templates is, so further calls to Parse in the copy will add
    236 // templates to the copy but not to the original. Clone can be used to prepare
    237 // common templates and use them with variant definitions for other templates
    238 // by adding the variants after the clone is made.
    239 //
    240 // It returns an error if t has already been executed.
    241 func (t *Template) Clone() (*Template, error) {
    242 	t.nameSpace.mu.Lock()
    243 	defer t.nameSpace.mu.Unlock()
    244 	if t.escapeErr != nil {
    245 		return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
    246 	}
    247 	textClone, err := t.text.Clone()
    248 	if err != nil {
    249 		return nil, err
    250 	}
    251 	ret := &Template{
    252 		nil,
    253 		textClone,
    254 		textClone.Tree,
    255 		&nameSpace{
    256 			set: make(map[string]*Template),
    257 		},
    258 	}
    259 	ret.set[ret.Name()] = ret
    260 	for _, x := range textClone.Templates() {
    261 		name := x.Name()
    262 		src := t.set[name]
    263 		if src == nil || src.escapeErr != nil {
    264 			return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
    265 		}
    266 		x.Tree = x.Tree.Copy()
    267 		ret.set[name] = &Template{
    268 			nil,
    269 			x,
    270 			x.Tree,
    271 			ret.nameSpace,
    272 		}
    273 	}
    274 	// Return the template associated with the name of this template.
    275 	return ret.set[ret.Name()], nil
    276 }
    277 
    278 // New allocates a new HTML template with the given name.
    279 func New(name string) *Template {
    280 	tmpl := &Template{
    281 		nil,
    282 		template.New(name),
    283 		nil,
    284 		&nameSpace{
    285 			set: make(map[string]*Template),
    286 		},
    287 	}
    288 	tmpl.set[name] = tmpl
    289 	return tmpl
    290 }
    291 
    292 // New allocates a new HTML template associated with the given one
    293 // and with the same delimiters. The association, which is transitive,
    294 // allows one template to invoke another with a {{template}} action.
    295 func (t *Template) New(name string) *Template {
    296 	t.nameSpace.mu.Lock()
    297 	defer t.nameSpace.mu.Unlock()
    298 	return t.new(name)
    299 }
    300 
    301 // new is the implementation of New, without the lock.
    302 func (t *Template) new(name string) *Template {
    303 	tmpl := &Template{
    304 		nil,
    305 		t.text.New(name),
    306 		nil,
    307 		t.nameSpace,
    308 	}
    309 	tmpl.set[name] = tmpl
    310 	return tmpl
    311 }
    312 
    313 // Name returns the name of the template.
    314 func (t *Template) Name() string {
    315 	return t.text.Name()
    316 }
    317 
    318 // FuncMap is the type of the map defining the mapping from names to
    319 // functions. Each function must have either a single return value, or two
    320 // return values of which the second has type error. In that case, if the
    321 // second (error) argument evaluates to non-nil during execution, execution
    322 // terminates and Execute returns that error. FuncMap has the same base type
    323 // as FuncMap in "text/template", copied here so clients need not import
    324 // "text/template".
    325 type FuncMap map[string]interface{}
    326 
    327 // Funcs adds the elements of the argument map to the template's function map.
    328 // It panics if a value in the map is not a function with appropriate return
    329 // type. However, it is legal to overwrite elements of the map. The return
    330 // value is the template, so calls can be chained.
    331 func (t *Template) Funcs(funcMap FuncMap) *Template {
    332 	t.text.Funcs(template.FuncMap(funcMap))
    333 	return t
    334 }
    335 
    336 // Delims sets the action delimiters to the specified strings, to be used in
    337 // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
    338 // definitions will inherit the settings. An empty delimiter stands for the
    339 // corresponding default: {{ or }}.
    340 // The return value is the template, so calls can be chained.
    341 func (t *Template) Delims(left, right string) *Template {
    342 	t.text.Delims(left, right)
    343 	return t
    344 }
    345 
    346 // Lookup returns the template with the given name that is associated with t,
    347 // or nil if there is no such template.
    348 func (t *Template) Lookup(name string) *Template {
    349 	t.nameSpace.mu.Lock()
    350 	defer t.nameSpace.mu.Unlock()
    351 	return t.set[name]
    352 }
    353 
    354 // Must is a helper that wraps a call to a function returning (*Template, error)
    355 // and panics if the error is non-nil. It is intended for use in variable initializations
    356 // such as
    357 //	var t = template.Must(template.New("name").Parse("html"))
    358 func Must(t *Template, err error) *Template {
    359 	if err != nil {
    360 		panic(err)
    361 	}
    362 	return t
    363 }
    364 
    365 // ParseFiles creates a new Template and parses the template definitions from
    366 // the named files. The returned template's name will have the (base) name and
    367 // (parsed) contents of the first file. There must be at least one file.
    368 // If an error occurs, parsing stops and the returned *Template is nil.
    369 //
    370 // When parsing multiple files with the same name in different directories,
    371 // the last one mentioned will be the one that results.
    372 // For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
    373 // named "foo", while "a/foo" is unavailable.
    374 func ParseFiles(filenames ...string) (*Template, error) {
    375 	return parseFiles(nil, filenames...)
    376 }
    377 
    378 // ParseFiles parses the named files and associates the resulting templates with
    379 // t. If an error occurs, parsing stops and the returned template is nil;
    380 // otherwise it is t. There must be at least one file.
    381 //
    382 // When parsing multiple files with the same name in different directories,
    383 // the last one mentioned will be the one that results.
    384 //
    385 // ParseFiles returns an error if t or any associated template has already been executed.
    386 func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
    387 	return parseFiles(t, filenames...)
    388 }
    389 
    390 // parseFiles is the helper for the method and function. If the argument
    391 // template is nil, it is created from the first file.
    392 func parseFiles(t *Template, filenames ...string) (*Template, error) {
    393 	if err := t.checkCanParse(); err != nil {
    394 		return nil, err
    395 	}
    396 
    397 	if len(filenames) == 0 {
    398 		// Not really a problem, but be consistent.
    399 		return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
    400 	}
    401 	for _, filename := range filenames {
    402 		b, err := ioutil.ReadFile(filename)
    403 		if err != nil {
    404 			return nil, err
    405 		}
    406 		s := string(b)
    407 		name := filepath.Base(filename)
    408 		// First template becomes return value if not already defined,
    409 		// and we use that one for subsequent New calls to associate
    410 		// all the templates together. Also, if this file has the same name
    411 		// as t, this file becomes the contents of t, so
    412 		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
    413 		// works. Otherwise we create a new template associated with t.
    414 		var tmpl *Template
    415 		if t == nil {
    416 			t = New(name)
    417 		}
    418 		if name == t.Name() {
    419 			tmpl = t
    420 		} else {
    421 			tmpl = t.New(name)
    422 		}
    423 		_, err = tmpl.Parse(s)
    424 		if err != nil {
    425 			return nil, err
    426 		}
    427 	}
    428 	return t, nil
    429 }
    430 
    431 // ParseGlob creates a new Template and parses the template definitions from the
    432 // files identified by the pattern, which must match at least one file. The
    433 // returned template will have the (base) name and (parsed) contents of the
    434 // first file matched by the pattern. ParseGlob is equivalent to calling
    435 // ParseFiles with the list of files matched by the pattern.
    436 //
    437 // When parsing multiple files with the same name in different directories,
    438 // the last one mentioned will be the one that results.
    439 func ParseGlob(pattern string) (*Template, error) {
    440 	return parseGlob(nil, pattern)
    441 }
    442 
    443 // ParseGlob parses the template definitions in the files identified by the
    444 // pattern and associates the resulting templates with t. The pattern is
    445 // processed by filepath.Glob and must match at least one file. ParseGlob is
    446 // equivalent to calling t.ParseFiles with the list of files matched by the
    447 // pattern.
    448 //
    449 // When parsing multiple files with the same name in different directories,
    450 // the last one mentioned will be the one that results.
    451 //
    452 // ParseGlob returns an error if t or any associated template has already been executed.
    453 func (t *Template) ParseGlob(pattern string) (*Template, error) {
    454 	return parseGlob(t, pattern)
    455 }
    456 
    457 // parseGlob is the implementation of the function and method ParseGlob.
    458 func parseGlob(t *Template, pattern string) (*Template, error) {
    459 	if err := t.checkCanParse(); err != nil {
    460 		return nil, err
    461 	}
    462 	filenames, err := filepath.Glob(pattern)
    463 	if err != nil {
    464 		return nil, err
    465 	}
    466 	if len(filenames) == 0 {
    467 		return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
    468 	}
    469 	return parseFiles(t, filenames...)
    470 }
    471 
    472 // IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
    473 // and whether the value has a meaningful truth value. This is the definition of
    474 // truth used by if and other such actions.
    475 func IsTrue(val interface{}) (truth, ok bool) {
    476 	return template.IsTrue(val)
    477 }
    478