diff options
author | Bjørn Erik Pedersen <[email protected]> | 2022-10-24 15:28:03 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-10-24 17:21:42 +0200 |
commit | 09e10110a35cd9f0df6227a66186fd1df0c93414 (patch) | |
tree | fead128e70e74ee652f9ba7e8c5c698d8301f834 | |
parent | 2ef60dbd2d9a04a8a1bb4fcdee11a7a32d2416f8 (diff) | |
download | hugo-09e10110a35cd9f0df6227a66186fd1df0c93414.tar.gz hugo-09e10110a35cd9f0df6227a66186fd1df0c93414.zip |
tpl/encoding: Add noHTMLEscape option to jsonify
-rw-r--r-- | docs/content/en/functions/jsonify.md | 11 | ||||
-rw-r--r-- | tpl/encoding/encoding.go | 42 | ||||
-rw-r--r-- | tpl/encoding/encoding_test.go | 9 |
3 files changed, 51 insertions, 11 deletions
diff --git a/docs/content/en/functions/jsonify.md b/docs/content/en/functions/jsonify.md index 28b90534c..3aa38c8c4 100644 --- a/docs/content/en/functions/jsonify.md +++ b/docs/content/en/functions/jsonify.md @@ -32,6 +32,17 @@ more copies of *indent* according to the indentation nesting. {{ dict "title" .Title "content" .Plain | jsonify (dict "prefix" " " "indent" " ") }} ``` +## Jsonify options + +indent ("") +: Indendation to use. + +prefix ("") +: Indentation prefix. + +noHTMLEscape (false) +: Disable escaping of problematic HTML characters inside JSON quoted strings. The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e to avoid certain safety problems that can arise when embedding JSON in HTML. + See also the `.PlainWords`, `.Plain`, and `.RawContent` [page variables][pagevars]. [pagevars]: /variables/page/ diff --git a/tpl/encoding/encoding.go b/tpl/encoding/encoding.go index 272503e0c..0510f86e2 100644 --- a/tpl/encoding/encoding.go +++ b/tpl/encoding/encoding.go @@ -20,7 +20,10 @@ import ( "errors" "html/template" + bp "github.com/gohugoio/hugo/bufferpool" + "github.com/gohugoio/hugo/common/maps" + "github.com/mitchellh/mapstructure" "github.com/spf13/cast" ) @@ -60,24 +63,27 @@ func (ns *Namespace) Base64Encode(content any) (string, error) { // to the indentation nesting. func (ns *Namespace) Jsonify(args ...any) (template.HTML, error) { var ( - b []byte - err error + b []byte + err error + obj any + opts jsonifyOpts ) switch len(args) { case 0: return "", nil case 1: - b, err = json.Marshal(args[0]) + obj = args[0] case 2: - var opts map[string]string - - opts, err = maps.ToStringMapStringE(args[0]) + var m map[string]any + m, err = maps.ToStringMapE(args[0]) if err != nil { break } - - b, err = json.MarshalIndent(args[1], opts["prefix"], opts["indent"]) + if err = mapstructure.WeakDecode(m, &opts); err != nil { + break + } + obj = args[1] default: err = errors.New("too many arguments to jsonify") } @@ -86,5 +92,25 @@ func (ns *Namespace) Jsonify(args ...any) (template.HTML, error) { return "", err } + buff := bp.GetBuffer() + defer bp.PutBuffer(buff) + e := json.NewEncoder(buff) + e.SetEscapeHTML(!opts.NoHTMLEscape) + e.SetIndent(opts.Prefix, opts.Indent) + if err = e.Encode(obj); err != nil { + return "", err + } + b = buff.Bytes() + // See https://github.com/golang/go/issues/37083 + // Hugo changed from MarshalIndent/Marshal. To make the output + // the same, we need to trim the trailing newline. + b = b[:len(b)-1] + return template.HTML(b), nil } + +type jsonifyOpts struct { + Prefix string + Indent string + NoHTMLEscape bool +} diff --git a/tpl/encoding/encoding_test.go b/tpl/encoding/encoding_test.go index e7c82e3be..8e6e2da48 100644 --- a/tpl/encoding/encoding_test.go +++ b/tpl/encoding/encoding_test.go @@ -82,7 +82,7 @@ func TestJsonify(t *testing.T) { c := qt.New(t) ns := New() - for _, test := range []struct { + for i, test := range []struct { opts any v any expect any @@ -91,6 +91,9 @@ func TestJsonify(t *testing.T) { {map[string]string{"indent": "<i>"}, []string{"a", "b"}, template.HTML("[\n<i>\"a\",\n<i>\"b\"\n]")}, {map[string]string{"prefix": "<p>"}, []string{"a", "b"}, template.HTML("[\n<p>\"a\",\n<p>\"b\"\n<p>]")}, {map[string]string{"prefix": "<p>", "indent": "<i>"}, []string{"a", "b"}, template.HTML("[\n<p><i>\"a\",\n<p><i>\"b\"\n<p>]")}, + {map[string]string{"indent": "<i>"}, []string{"a", "b"}, template.HTML("[\n<i>\"a\",\n<i>\"b\"\n]")}, + {map[string]any{"noHTMLEscape": false}, []string{"<a>", "<b>"}, template.HTML("[\"\\u003ca\\u003e\",\"\\u003cb\\u003e\"]")}, + {map[string]any{"noHTMLEscape": true}, []string{"<a>", "<b>"}, template.HTML("[\"<a>\",\"<b>\"]")}, {nil, tstNoStringer{}, template.HTML("{}")}, {nil, nil, template.HTML("null")}, // errors @@ -108,11 +111,11 @@ func TestJsonify(t *testing.T) { result, err := ns.Jsonify(args...) if b, ok := test.expect.(bool); ok && !b { - c.Assert(err, qt.Not(qt.IsNil)) + c.Assert(err, qt.Not(qt.IsNil), qt.Commentf("#%d", i)) continue } c.Assert(err, qt.IsNil) - c.Assert(result, qt.Equals, test.expect) + c.Assert(result, qt.Equals, test.expect, qt.Commentf("#%d", i)) } } |