Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make tags and filters private for templateSet #335

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 13 additions & 19 deletions filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,37 @@ import (
// FilterFunction is the type filter functions must fulfil
type FilterFunction func(in *Value, param *Value) (out *Value, err *Error)

var filters map[string]FilterFunction

func init() {
filters = make(map[string]FilterFunction)
}

// FilterExists returns true if the given filter is already registered
func FilterExists(name string) bool {
_, existing := filters[name]
func (set *TemplateSet) FilterExists(name string) bool {
_, existing := set.filters[name]
return existing
}

// RegisterFilter registers a new filter. If there's already a filter with the same. You usually
// want to call this function in the filter's init() function:
//
// http://golang.org/doc/effective_go.html#init
func RegisterFilter(name string, fn FilterFunction) error {
if FilterExists(name) {
func (set *TemplateSet) RegisterFilter(name string, fn FilterFunction) error {
if set.FilterExists(name) {
return fmt.Errorf("filter with name '%s' is already registered", name)
}
filters[name] = fn
set.filters[name] = fn
return nil
}

// ReplaceFilter replaces an already registered filter with a new implementation. Use this
// function with caution since it allows you to change existing filter behaviour.
func ReplaceFilter(name string, fn FilterFunction) error {
if !FilterExists(name) {
func (set *TemplateSet) ReplaceFilter(name string, fn FilterFunction) error {
if !set.FilterExists(name) {
return fmt.Errorf("filter with name '%s' does not exist (therefore cannot be overridden)", name)
}
filters[name] = fn
set.filters[name] = fn
return nil
}

// MustApplyFilter behaves like ApplyFilter, but panics on an error.
func MustApplyFilter(name string, value *Value, param *Value) *Value {
val, err := ApplyFilter(name, value, param)
func (set *TemplateSet) MustApplyFilter(name string, value *Value, param *Value) *Value {
val, err := set.ApplyFilter(name, value, param)
if err != nil {
panic(err)
}
Expand All @@ -52,8 +46,8 @@ func MustApplyFilter(name string, value *Value, param *Value) *Value {

// ApplyFilter applies a filter to a given value using the given parameters.
// Returns a *pongo2.Value or an error.
func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) {
fn, existing := filters[name]
func (set *TemplateSet) ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) {
fn, existing := set.filters[name]
if !existing {
return nil, &Error{
Sender: "applyfilter",
Expand Down Expand Up @@ -113,7 +107,7 @@ func (p *Parser) parseFilter() (*filterCall, *Error) {
}

// Get the appropriate filter function and bind it
filterFn, exists := filters[identToken.Val]
filterFn, exists := p.template.set.filters[identToken.Val]
if !exists {
return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", identToken.Val), identToken)
}
Expand Down
20 changes: 7 additions & 13 deletions tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,15 @@ type tag struct {
parser TagParser
}

var tags map[string]*tag

func init() {
tags = make(map[string]*tag)
}

// Registers a new tag. You usually want to call this
// function in the tag's init() function:
// http://golang.org/doc/effective_go.html#init
func RegisterTag(name string, parserFn TagParser) error {
_, existing := tags[name]
func (set *TemplateSet) RegisterTag(name string, parserFn TagParser) error {
_, existing := set.tags[name]
if existing {
return fmt.Errorf("tag with name '%s' is already registered", name)
}
tags[name] = &tag{
set.tags[name] = &tag{
name: name,
parser: parserFn,
}
Expand All @@ -70,12 +64,12 @@ func RegisterTag(name string, parserFn TagParser) error {

// Replaces an already registered tag with a new implementation. Use this
// function with caution since it allows you to change existing tag behaviour.
func ReplaceTag(name string, parserFn TagParser) error {
_, existing := tags[name]
func (set *TemplateSet) ReplaceTag(name string, parserFn TagParser) error {
_, existing := set.tags[name]
if !existing {
return fmt.Errorf("tag with name '%s' does not exist (therefore cannot be overridden)", name)
}
tags[name] = &tag{
set.tags[name] = &tag{
name: name,
parser: parserFn,
}
Expand All @@ -93,7 +87,7 @@ func (p *Parser) parseTagElement() (INodeTag, *Error) {
}

// Check for the existing tag
tag, exists := tags[tokenName.Val]
tag, exists := p.template.set.tags[tokenName.Val]
if !exists {
// Does not exists
return nil, p.Error(fmt.Sprintf("Tag '%s' not found (or beginning tag not provided)", tokenName.Val), tokenName)
Expand Down
37 changes: 30 additions & 7 deletions template_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type TemplateSet struct {
// added your first template to the set (restrictions are statically checked).
// After you added one, it's not possible anymore (for your personal security).
firstTemplateCreated bool
tags map[string]*tag
filters map[string]FilterFunction
bannedTags map[string]bool
bannedFilters map[string]bool

Expand All @@ -56,10 +58,8 @@ type TemplateSet struct {
templateCacheMutex sync.Mutex
}

// NewSet can be used to create sets with different kind of templates
// (e. g. web from mail templates), with different globals or
// other configurations.
func NewSet(name string, loaders ...TemplateLoader) *TemplateSet {
// newSet only be used to create sets without default tags and filters
func newSet(name string, loaders ...TemplateLoader) *TemplateSet {
if len(loaders) == 0 {
panic(fmt.Errorf("at least one template loader must be specified"))
}
Expand All @@ -68,13 +68,30 @@ func NewSet(name string, loaders ...TemplateLoader) *TemplateSet {
name: name,
loaders: loaders,
Globals: make(Context),
tags: make(map[string]*tag),
filters: make(map[string]FilterFunction),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gave this a spin - having empty filters leads to panic when redering variables with escaping, since escape filter is not present. Think NewDefaultSet will have to include at least that one!

Copy link
Author

@honmaple honmaple Oct 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@draganm It might be a good idea to use newSet instead of NewDefaultSet. Users can only use NewSet which includes all default filters and tags to create a special template set. What do you think of this idea?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that works, but as I said, NewDefaultSet has this huge dependency list of things that need to be provided, that it makes is quite niche. Upon reflection, probably just a word of warning in the GoDoc would be probably enough to point people to NewSet

bannedTags: make(map[string]bool),
bannedFilters: make(map[string]bool),
templateCache: make(map[string]*Template),
Options: newOptions(),
}
}

// NewSet can be used to create sets with different kind of templates
// (e. g. web from mail templates), with different globals or
// other configurations.
func NewSet(name string, loaders ...TemplateLoader) *TemplateSet {
set := newSet(name, loaders...)

for name, tag := range DefaultSet.tags {
set.tags[name] = tag
}
for name, filter := range DefaultSet.filters {
set.filters[name] = filter
}
return set
}

func (set *TemplateSet) AddLoader(loaders ...TemplateLoader) {
set.loaders = append(set.loaders, loaders...)
}
Expand All @@ -97,7 +114,7 @@ func (set *TemplateSet) resolveFilenameForLoader(loader TemplateLoader, tpl *Tem

// BanTag bans a specific tag for this template set. See more in the documentation for TemplateSet.
func (set *TemplateSet) BanTag(name string) error {
_, has := tags[name]
_, has := set.tags[name]
if !has {
return fmt.Errorf("tag '%s' not found", name)
}
Expand All @@ -115,7 +132,7 @@ func (set *TemplateSet) BanTag(name string) error {

// BanFilter bans a specific filter for this template set. See more in the documentation for TemplateSet.
func (set *TemplateSet) BanFilter(name string) error {
_, has := filters[name]
_, has := set.filters[name]
if !has {
return fmt.Errorf("filter '%s' not found", name)
}
Expand Down Expand Up @@ -288,7 +305,7 @@ var (
DefaultLoader = MustNewLocalFileSystemLoader("")

// DefaultSet is a set created for you for convinience reasons.
DefaultSet = NewSet("default", DefaultLoader)
DefaultSet = newSet("default", DefaultLoader)

// Methods on the default set
FromString = DefaultSet.FromString
Expand All @@ -297,6 +314,12 @@ var (
FromCache = DefaultSet.FromCache
RenderTemplateString = DefaultSet.RenderTemplateString
RenderTemplateFile = DefaultSet.RenderTemplateFile
ReplaceTag = DefaultSet.ReplaceTag
RegisterTag = DefaultSet.RegisterTag
ReplaceFilter = DefaultSet.ReplaceFilter
RegisterFilter = DefaultSet.RegisterFilter
ApplyFilter = DefaultSet.ApplyFilter
MustApplyFilter = DefaultSet.MustApplyFilter

// Globals for the default set
Globals = DefaultSet.Globals
Expand Down
2 changes: 1 addition & 1 deletion variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (nv *nodeVariable) Execute(ctx *ExecutionContext, writer TemplateWriter) *E

if !nv.expr.FilterApplied("safe") && !value.safe && value.IsString() && ctx.Autoescape {
// apply escape filter
value, err = filters["escape"](value, nil)
value, err = ctx.template.set.filters["escape"](value, nil)
if err != nil {
return err
}
Expand Down