blob: 04066ab40e5bc11f5b6eb989ecf211fba2e71a1a [file] [log] [blame]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package html
import (
"fmt"
"io"
"path/filepath"
"template"
)
// Set is a specialized template.Set that produces a safe HTML document
// fragment.
type Set struct {
escaped map[string]bool
template.Set
}
// Template is a specialized template.Template that produces a safe HTML
// document fragment.
type Template struct {
escaped bool
*template.Template
}
// Execute applies the named template to the specified data object, writing
// the output to wr.
func (s *Set) Execute(wr io.Writer, name string, data interface{}) error {
if !s.escaped[name] {
if err := escapeSet(&s.Set, name); err != nil {
return err
}
if s.escaped == nil {
s.escaped = make(map[string]bool)
}
s.escaped[name] = true
}
return s.Set.Execute(wr, name, data)
}
// Parse parses a string into a set of named templates. Parse may be called
// multiple times for a given set, adding the templates defined in the string
// to the set. If a template is redefined, the element in the set is
// overwritten with the new definition.
func (set *Set) Parse(src string) (*Set, error) {
set.escaped = nil
s, err := set.Set.Parse(src)
if err != nil {
return nil, err
}
if s != &(set.Set) {
panic("allocated new set")
}
return set, nil
}
// Parse parses the template definition string to construct an internal
// representation of the template for execution.
func (tmpl *Template) Parse(src string) (*Template, error) {
tmpl.escaped = false
t, err := tmpl.Template.Parse(src)
if err != nil {
return nil, err
}
tmpl.Template = t
return tmpl, nil
}
// Execute applies a parsed template to the specified data object,
// writing the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) error {
if !t.escaped {
if err := escape(t.Template); err != nil {
return err
}
t.escaped = true
}
return t.Template.Execute(wr, data)
}
// New allocates a new HTML template with the given name.
func New(name string) *Template {
return &Template{false, template.New(name)}
}
// Must panics if err is non-nil in the same way as template.Must.
func Must(t *Template, err error) *Template {
t.Template = template.Must(t.Template, err)
return t
}
// ParseFile creates a new Template and parses the template definition from
// the named file. The template name is the base name of the file.
func ParseFile(filename string) (*Template, error) {
t, err := template.ParseFile(filename)
if err != nil {
return nil, err
}
return &Template{false, t}, nil
}
// ParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
// The returned template will be nil if an error occurs.
func (tmpl *Template) ParseFile(filename string) (*Template, error) {
t, err := tmpl.Template.ParseFile(filename)
if err != nil {
return nil, err
}
tmpl.Template = t
return tmpl, nil
}
// SetMust panics if the error is non-nil just like template.SetMust.
func SetMust(s *Set, err error) *Set {
if err != nil {
template.SetMust(&(s.Set), err)
}
return s
}
// ParseFiles parses the named files into a set of named templates.
// Each file must be parseable by itself.
// If an error occurs, parsing stops and the returned set is nil.
func (set *Set) ParseFiles(filenames ...string) (*Set, error) {
s, err := set.Set.ParseFiles(filenames...)
if err != nil {
return nil, err
}
if s != &(set.Set) {
panic("allocated new set")
}
return set, nil
}
// ParseSetFiles creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
func ParseSetFiles(filenames ...string) (*Set, error) {
set := new(Set)
s, err := set.Set.ParseFiles(filenames...)
if err != nil {
return nil, err
}
if s != &(set.Set) {
panic("allocated new set")
}
return set, nil
}
// ParseGlob parses the set definition from the files identified by the
// pattern. The pattern is processed by filepath.Glob and must match at
// least one file.
// If an error occurs, parsing stops and the returned set is nil.
func (s *Set) ParseGlob(pattern string) (*Set, error) {
filenames, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
if len(filenames) == 0 {
return nil, fmt.Errorf("pattern matches no files: %#q", pattern)
}
return s.ParseFiles(filenames...)
}
// ParseSetGlob creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob
// and must match at least one file.
func ParseSetGlob(pattern string) (*Set, error) {
set, err := new(Set).ParseGlob(pattern)
if err != nil {
return nil, err
}
return set, nil
}
// Functions and methods to parse stand-alone template files into a set.
// ParseTemplateFiles parses the named template files and adds them to the set
// in the same way as template.ParseTemplateFiles but ensures that templates
// with upper-case names are contextually-autoescaped.
func (set *Set) ParseTemplateFiles(filenames ...string) (*Set, error) {
s, err := set.Set.ParseTemplateFiles(filenames...)
if err != nil {
return nil, err
}
if s != &(set.Set) {
panic("new set allocated")
}
return set, nil
}
// ParseTemplateGlob parses the template files matched by the
// patern and adds them to the set. Each template will be named
// the base name of its file.
// Unlike with ParseGlob, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateGlob is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself.
// If an error occurs, parsing stops and the returned set is nil.
func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) {
filenames, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
return s.ParseTemplateFiles(filenames...)
}
// ParseTemplateFiles creates a set by parsing the named files,
// each of which defines a single template. Each template will be
// named the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFiles(filenames ...string) (*Set, error) {
return new(Set).ParseTemplateFiles(filenames...)
}
// ParseTemplateGlob creates a set by parsing the files matched
// by the pattern, each of which defines a single template. The pattern
// is processed by filepath.Glob and must match at least one file. Each
// template will be named the base name of its file.
// Unlike with ParseGlob, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateGlob is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateGlob(pattern string) (*Set, error) {
return new(Set).ParseTemplateGlob(pattern)
}