mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
ef12d169c8
commit
b4c5df42ff
6 changed files with 105 additions and 2 deletions
38
docs/content/en/functions/transform/XMLEscape.md
Normal file
38
docs/content/en/functions/transform/XMLEscape.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
title: transform.XMLEscape
|
||||||
|
description: Returns the given string, removing disallowed characters then escaping the result to its XML equivalent.
|
||||||
|
categories: []
|
||||||
|
keywords: []
|
||||||
|
action:
|
||||||
|
aliases: []
|
||||||
|
related: []
|
||||||
|
returnType: string
|
||||||
|
signatures: [transform.XMLEscape INPUT]
|
||||||
|
---
|
||||||
|
|
||||||
|
The `transform.XMLEscape` function removes [disallowed characters] as defined in the XML specification, then escapes the result by replacing the following characters with [HTML entities]:
|
||||||
|
|
||||||
|
- `"` → `"`
|
||||||
|
- `'` → `'`
|
||||||
|
- `&` → `&`
|
||||||
|
- `<` → `<`
|
||||||
|
- `>` → `>`
|
||||||
|
- `\t` → `	`
|
||||||
|
- `\n` → `
`
|
||||||
|
- `\r` → `
`
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```go-html-template
|
||||||
|
transform.XMLEscape "<p>abc</p>" → <p>abc</p>
|
||||||
|
```
|
||||||
|
|
||||||
|
When using `transform.XMLEscape` in a template rendered by Go's [html/template] package, declare the string to be safe HTML to avoid double escaping. For example, in an RSS template:
|
||||||
|
|
||||||
|
{{< code file="layouts/_default/rss.xml" >}}
|
||||||
|
<description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
|
||||||
|
{{< /code >}}
|
||||||
|
|
||||||
|
[disallowed characters]: https://www.w3.org/TR/xml/#charsets
|
||||||
|
[html entities]: https://developer.mozilla.org/en-us/docs/glossary/entity
|
||||||
|
[html/template]: https://pkg.go.dev/html/template
|
|
@ -64,7 +64,7 @@
|
||||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||||
{{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
|
{{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
|
||||||
<guid>{{ .Permalink }}</guid>
|
<guid>{{ .Permalink }}</guid>
|
||||||
<description>{{ .Summary | html }}</description>
|
<description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
|
||||||
</item>
|
</item>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</channel>
|
</channel>
|
||||||
|
|
|
@ -37,7 +37,7 @@ home=["HTML"]
|
||||||
-- files/README.txt --
|
-- files/README.txt --
|
||||||
Hugo Rocks!
|
Hugo Rocks!
|
||||||
-- content/blog/hugo-rocks.md --
|
-- content/blog/hugo-rocks.md --
|
||||||
---
|
---
|
||||||
title: "**BatMan**"
|
title: "**BatMan**"
|
||||||
---
|
---
|
||||||
`
|
`
|
||||||
|
@ -65,6 +65,10 @@ title: "**BatMan**"
|
||||||
// This will fail the build, so skip for now.
|
// This will fail the build, so skip for now.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if strings.Contains(example[0], "transform.XMLEscape") {
|
||||||
|
// This will fail the build, so skip for now.
|
||||||
|
continue
|
||||||
|
}
|
||||||
templates = append(templates, example[0])
|
templates = append(templates, example[0])
|
||||||
expected = append(expected, example[1])
|
expected = append(expected, example[1])
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,16 @@ func init() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ns.AddMethodMapping(ctx.XMLEscape,
|
||||||
|
nil,
|
||||||
|
[][2]string{
|
||||||
|
{
|
||||||
|
`{{ transform.XMLEscape "<p>abc</p>" }}`,
|
||||||
|
`<p>abc</p>`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,3 +65,23 @@ foo
|
||||||
b.AssertFileContent("public/p3/index.html", "_<h2 id=\"foo\">foo</h2>\n<p>bar</p>\n_")
|
b.AssertFileContent("public/p3/index.html", "_<h2 id=\"foo\">foo</h2>\n<p>bar</p>\n_")
|
||||||
b.AssertFileContent("public/p4/index.html", "_<p id=\"bar\">foo</p>\n_")
|
b.AssertFileContent("public/p4/index.html", "_<p id=\"bar\">foo</p>\n_")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestXMLEscape(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
disableKinds = ['section','sitemap','taxonomy','term']
|
||||||
|
-- content/p1.md --
|
||||||
|
---
|
||||||
|
title: p1
|
||||||
|
---
|
||||||
|
a **b** c
|
||||||
|
<!--more-->
|
||||||
|
`
|
||||||
|
b := hugolib.Test(t, files)
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.xml", `
|
||||||
|
<description><p>a <strong>b</strong> c</p></description>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
|
@ -15,9 +15,12 @@
|
||||||
package transform
|
package transform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/cache/namedmemcache"
|
"github.com/gohugoio/hugo/cache/namedmemcache"
|
||||||
"github.com/gohugoio/hugo/markup/converter/hooks"
|
"github.com/gohugoio/hugo/markup/converter/hooks"
|
||||||
|
@ -118,6 +121,34 @@ func (ns *Namespace) HTMLUnescape(s any) (string, error) {
|
||||||
return html.UnescapeString(ss), nil
|
return html.UnescapeString(ss), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XMLEscape returns the given string, removing disallowed characters then
|
||||||
|
// escaping the result to its XML equivalent.
|
||||||
|
func (ns *Namespace) XMLEscape(s any) (string, error) {
|
||||||
|
ss, err := cast.ToStringE(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/xml/#NT-Char
|
||||||
|
cleaned := strings.Map(func(r rune) rune {
|
||||||
|
if r == 0x9 || r == 0xA || r == 0xD ||
|
||||||
|
(r >= 0x20 && r <= 0xD7FF) ||
|
||||||
|
(r >= 0xE000 && r <= 0xFFFD) ||
|
||||||
|
(r >= 0x10000 && r <= 0x10FFFF) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}, ss)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = xml.EscapeText(&buf, []byte(cleaned))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Markdownify renders s from Markdown to HTML.
|
// Markdownify renders s from Markdown to HTML.
|
||||||
func (ns *Namespace) Markdownify(ctx context.Context, s any) (template.HTML, error) {
|
func (ns *Namespace) Markdownify(ctx context.Context, s any) (template.HTML, error) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue