tpl/tplimpl: Reimplement the ".Params tolower" template transformer

All `.Params` are stored lowercase, but it should work to access them `.Page.camelCase` etc. There was, however, some holes in the logic with the old transformer.

This commit fixes that by applying a blacklist instead of the old whitelist logic. `.Param` is a very distinct key. The original case will be kept in `.Data.Params.myParam`, but other than that it will be lowercased.

Fixes #5068
This commit is contained in:
Bjørn Erik Pedersen
2018-08-13 20:50:07 +02:00
parent 56c61559b2
commit 5c5384916e
2 changed files with 150 additions and 75 deletions

View File

@@ -24,15 +24,14 @@ import (
// decl keeps track of the variable mappings, i.e. $mysite => .Site etc.
type decl map[string]string
var paramsPaths = [][]string{
{"Params"},
{"Site", "Params"},
const (
paramsIdentifier = "Params"
)
// Site and Pag referenced from shortcodes
{"Page", "Site", "Params"},
{"Page", "Params"},
{"Site", "Language", "Params"},
// Containers that may contain Params that we will not touch.
var reservedContainers = map[string]bool{
// Aka .Site.Data.Params which must stay case sensitive.
"Data": true,
}
type templateContext struct {
@@ -155,6 +154,7 @@ func (c *templateContext) updateIdentsIfNeeded(idents []string) {
for i := index; i < len(idents); i++ {
idents[i] = strings.ToLower(idents[i])
}
}
// indexOfReplacementStart will return the index of where to start doing replacement,
@@ -167,31 +167,101 @@ func (d decl) indexOfReplacementStart(idents []string) int {
return -1
}
first := idents[0]
firstIsVar := first[0] == '$'
if l == 1 && !firstIsVar {
// This can not be a Params.x
return -1
}
if !firstIsVar {
found := false
for _, paramsPath := range paramsPaths {
if first == paramsPath[0] {
found = true
break
}
}
if !found {
if l == 1 {
first := idents[0]
if first == "" || first == paramsIdentifier || first[0] == '$' {
// This can not be a Params.x
return -1
}
}
var lookFurther bool
var needsVarExpansion bool
for _, ident := range idents {
if ident[0] == '$' {
lookFurther = true
needsVarExpansion = true
break
} else if ident == paramsIdentifier {
lookFurther = true
break
}
}
if !lookFurther {
return -1
}
var resolvedIdents []string
if !needsVarExpansion {
resolvedIdents = idents
} else {
var ok bool
resolvedIdents, ok = d.resolveVariables(idents)
if !ok {
return -1
}
}
var paramFound bool
for i, ident := range resolvedIdents {
if ident == paramsIdentifier {
if i > 0 {
container := resolvedIdents[i-1]
if reservedContainers[container] {
// .Data.Params.someKey
return -1
}
if !d.isKeyword(container) {
// where $pages ".Params.toc_hide" "!=" true
return -1
}
}
if i < len(resolvedIdents)-1 {
next := resolvedIdents[i+1]
if !d.isKeyword(next) {
return -1
}
}
paramFound = true
break
}
}
if !paramFound {
return -1
}
var paramSeen bool
idx := -1
for i, ident := range idents {
if ident == "" || ident[0] == '$' {
continue
}
if ident == paramsIdentifier {
paramSeen = true
idx = -1
} else {
if paramSeen {
return i
}
if idx == -1 {
idx = i
}
}
}
return idx
}
func (d decl) resolveVariables(idents []string) ([]string, bool) {
var (
resolvedIdents []string
replacements []string
replaced []string
replacements []string
replaced []string
)
// An Ident can start out as one of
@@ -206,7 +276,7 @@ func (d decl) indexOfReplacementStart(idents []string) int {
if i > 20 {
// bail out
return -1
return nil, false
}
potentialVar := replacements[i]
@@ -225,7 +295,7 @@ func (d decl) indexOfReplacementStart(idents []string) int {
if !ok {
// Temporary range vars. We do not care about those.
return -1
return nil, false
}
replacement = strings.TrimPrefix(replacement, ".")
@@ -242,52 +312,10 @@ func (d decl) indexOfReplacementStart(idents []string) int {
}
}
resolvedIdents = append(replaced, idents[1:]...)
for _, paramPath := range paramsPaths {
if index := indexOfFirstRealIdentAfterWords(resolvedIdents, idents, paramPath...); index != -1 {
return index
}
}
return -1
return append(replaced, idents[1:]...), true
}
func indexOfFirstRealIdentAfterWords(resolvedIdents, idents []string, words ...string) int {
if !sliceStartsWith(resolvedIdents, words...) {
return -1
}
for i, ident := range idents {
if ident == "" || ident[0] == '$' {
continue
}
found := true
for _, word := range words {
if ident == word {
found = false
break
}
}
if found {
return i
}
}
return -1
}
func sliceStartsWith(slice []string, words ...string) bool {
if len(slice) < len(words) {
return false
}
for i, word := range words {
if word != slice[i] {
return false
}
}
return true
func (d decl) isKeyword(s string) bool {
return !strings.ContainsAny(s, " -\"")
}