mirror of
https://github.com/gohugoio/hugo.git
synced 2024-05-20 16:22:49 +00:00
Compare commits
18 commits
0fd5e274bd
...
ac303e8e32
Author | SHA1 | Date | |
---|---|---|---|
ac303e8e32 | |||
6b867972ec | |||
509ab08c1b | |||
2d75f539e1 | |||
15a4b9b337 | |||
10a8448eee | |||
722c486a34 | |||
f40f50ead0 | |||
fbbcca40c8 | |||
addc311e56 | |||
6c8f472712 | |||
c38633a0c4 | |||
35461c167f | |||
aaed290b02 | |||
f1b1e5f41b | |||
769439cc5b | |||
6de4f5cde5 | |||
d362fae970 |
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2022 The Hugo Authors.
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
Please report (suspected) security vulnerabilities to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. You will receive a response from us within 48 hours. If we can confirm the issue, we will release a patch as soon as possible depending on the complexity of the issue but historically within days.
|
Please report (suspected) security vulnerabilities to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. You will receive a response from us within 48 hours. If we can confirm the issue, we will release a patch as soon as possible depending on the complexity of the issue but historically within days.
|
||||||
|
|
||||||
Also see [Hugo's Security Model](https://gohugo.io/about/security-model/).
|
Also see [Hugo's Security Model](https://gohugo.io/about/security/).
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package hugolib
|
package hugolib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -241,3 +242,52 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
|
||||||
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
|
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderHooksDefaultEscape(t *testing.T) {
|
||||||
|
files := `
|
||||||
|
-- hugo.toml --
|
||||||
|
[markup.goldmark.renderHooks]
|
||||||
|
[markup.goldmark.renderHooks.image]
|
||||||
|
enableDefault = ENABLE
|
||||||
|
[markup.goldmark.renderHooks.link]
|
||||||
|
enableDefault = ENABLE
|
||||||
|
[markup.goldmark.parser]
|
||||||
|
wrapStandAloneImageWithinParagraph = false
|
||||||
|
[markup.goldmark.parser.attribute]
|
||||||
|
block = true
|
||||||
|
title = true
|
||||||
|
-- content/_index.md --
|
||||||
|
---
|
||||||
|
title: "Home"
|
||||||
|
---
|
||||||
|
Link: [text-"<>&](/destination-"<> 'title-"<>&')
|
||||||
|
|
||||||
|
Image: ![alt-"<>&](/destination-"<> 'title-"<>&')
|
||||||
|
{class="><script>alert()</script>" id="baz"}
|
||||||
|
|
||||||
|
-- layouts/index.html --
|
||||||
|
{{ .Content }}
|
||||||
|
`
|
||||||
|
|
||||||
|
for _, enabled := range []bool{true, false} {
|
||||||
|
enabled := enabled
|
||||||
|
t.Run(fmt.Sprint(enabled), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
b := Test(t, strings.ReplaceAll(files, "ENABLE", fmt.Sprint(enabled)))
|
||||||
|
|
||||||
|
// The escaping is slightly different between the two.
|
||||||
|
if enabled {
|
||||||
|
b.AssertFileContent("public/index.html",
|
||||||
|
"Link: <a href=\"/destination-%22%3C%3E\" title=\"title-"<>&\">text-"<>&</a>",
|
||||||
|
"img alt=\"alt-"<>&\" src=\"/destination-%22%3C%3E\" title=\"title-"<>&\">",
|
||||||
|
"><script>",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
b.AssertFileContent("public/index.html",
|
||||||
|
"Link: <a href=\"/destination-%22%3C%3E\" title=\"title-"<>&\">text-"<>&</a>",
|
||||||
|
"Image: <img src=\"/destination-%22%3C%3E\" alt=\"alt-"<>&\" title=\"title-"<>&\">",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -175,7 +175,7 @@ func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HT
|
||||||
// This content will be parsed and rendered by Goldmark.
|
// This content will be parsed and rendered by Goldmark.
|
||||||
// Wrap it in a special Hugo markup to assign the correct Page from
|
// Wrap it in a special Hugo markup to assign the correct Page from
|
||||||
// the stack.
|
// the stack.
|
||||||
c = hugocontext.Wrap(c, pco.po.p.pid)
|
return template.HTML(hugocontext.Wrap(c, pco.po.p.pid)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return helpers.BytesToHTML(c), nil
|
return helpers.BytesToHTML(c), nil
|
||||||
|
|
|
@ -34,7 +34,7 @@ func New() goldmark.Extender {
|
||||||
|
|
||||||
// Wrap wraps the given byte slice in a Hugo context that used to determine the correct Page
|
// Wrap wraps the given byte slice in a Hugo context that used to determine the correct Page
|
||||||
// in .RenderShortcodes.
|
// in .RenderShortcodes.
|
||||||
func Wrap(b []byte, pid uint64) []byte {
|
func Wrap(b []byte, pid uint64) string {
|
||||||
buf := bufferpool.GetBuffer()
|
buf := bufferpool.GetBuffer()
|
||||||
defer bufferpool.PutBuffer(buf)
|
defer bufferpool.PutBuffer(buf)
|
||||||
buf.Write(prefix)
|
buf.Write(prefix)
|
||||||
|
@ -45,7 +45,7 @@ func Wrap(b []byte, pid uint64) []byte {
|
||||||
buf.Write(b)
|
buf.Write(b)
|
||||||
buf.Write(prefix)
|
buf.Write(prefix)
|
||||||
buf.Write(closingDelimAndNewline)
|
buf.Write(closingDelimAndNewline)
|
||||||
return buf.Bytes()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
var kindHugoContext = ast.NewNodeKind("HugoContext")
|
var kindHugoContext = ast.NewNodeKind("HugoContext")
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TestWrap(t *testing.T) {
|
||||||
|
|
||||||
b := []byte("test")
|
b := []byte("test")
|
||||||
|
|
||||||
c.Assert(string(Wrap(b, 42)), qt.Equals, "{{__hugo_ctx pid=42}}\ntest{{__hugo_ctx/}}\n")
|
c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest{{__hugo_ctx/}}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkWrap(b *testing.B) {
|
func BenchmarkWrap(b *testing.B) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
|
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
|
||||||
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||||
"github.com/gohugoio/hugo/markup/highlight"
|
"github.com/gohugoio/hugo/markup/highlight"
|
||||||
|
"github.com/gohugoio/hugo/markup/pandoc/pandoc_config"
|
||||||
"github.com/gohugoio/hugo/markup/tableofcontents"
|
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
@ -39,6 +40,7 @@ type Config struct {
|
||||||
|
|
||||||
// Configuration for the Asciidoc external markdown engine.
|
// Configuration for the Asciidoc external markdown engine.
|
||||||
AsciidocExt asciidocext_config.Config
|
AsciidocExt asciidocext_config.Config
|
||||||
|
Pandoc pandoc_config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Decode(cfg config.Provider) (conf Config, err error) {
|
func Decode(cfg config.Provider) (conf Config, err error) {
|
||||||
|
@ -105,4 +107,5 @@ var Default = Config{
|
||||||
|
|
||||||
Goldmark: goldmark_config.Default,
|
Goldmark: goldmark_config.Default,
|
||||||
AsciidocExt: asciidocext_config.Default,
|
AsciidocExt: asciidocext_config.Default,
|
||||||
|
Pandoc: pandoc_config.Default,
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,16 @@
|
||||||
package pandoc
|
package pandoc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hexec"
|
"github.com/gohugoio/hugo/common/hexec"
|
||||||
"github.com/gohugoio/hugo/htesting"
|
"github.com/gohugoio/hugo/htesting"
|
||||||
"github.com/gohugoio/hugo/identity"
|
"github.com/gohugoio/hugo/identity"
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/markup/converter"
|
"github.com/gohugoio/hugo/markup/converter"
|
||||||
"github.com/gohugoio/hugo/markup/internal"
|
"github.com/gohugoio/hugo/markup/internal"
|
||||||
|
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Provider is the package entry point.
|
// Provider is the package entry point.
|
||||||
|
@ -37,17 +41,33 @@ func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error)
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pandocResult struct {
|
||||||
|
converter.ResultRender
|
||||||
|
toc *tableofcontents.Fragments
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r pandocResult) TableOfContents() *tableofcontents.Fragments {
|
||||||
|
return r.toc
|
||||||
|
}
|
||||||
|
|
||||||
type pandocConverter struct {
|
type pandocConverter struct {
|
||||||
ctx converter.DocumentContext
|
ctx converter.DocumentContext
|
||||||
cfg converter.ProviderConfig
|
cfg converter.ProviderConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pandocConverter) Convert(ctx converter.RenderContext) (converter.ResultRender, error) {
|
func (c *pandocConverter) Convert(ctx converter.RenderContext) (converter.ResultRender, error) {
|
||||||
b, err := c.getPandocContent(ctx.Src, c.ctx)
|
contentWithToc, err := c.getPandocContent(ctx.Src, c.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return converter.Bytes(b), nil
|
content, toc, err := c.extractTOC(contentWithToc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pandocResult{
|
||||||
|
ResultRender: converter.Bytes(content),
|
||||||
|
toc: toc,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pandocConverter) Supports(feature identity.Identity) bool {
|
func (c *pandocConverter) Supports(feature identity.Identity) bool {
|
||||||
|
@ -63,7 +83,7 @@ func (c *pandocConverter) getPandocContent(src []byte, ctx converter.DocumentCon
|
||||||
" Leaving pandoc content unrendered.")
|
" Leaving pandoc content unrendered.")
|
||||||
return src, nil
|
return src, nil
|
||||||
}
|
}
|
||||||
args := []string{"--mathjax"}
|
args := []string{"--mathjax", "--toc", "-s", "--metadata", "title=dummy"}
|
||||||
return internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
|
return internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +96,164 @@ func getPandocBinaryName() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractTOC extracts the toc from the given src html.
|
||||||
|
// It returns the html without the TOC, and the TOC data
|
||||||
|
func (a *pandocConverter) extractTOC(src []byte) ([]byte, *tableofcontents.Fragments, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.Write(src)
|
||||||
|
node, err := html.Parse(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
f func(*html.Node) bool
|
||||||
|
body *html.Node
|
||||||
|
toc *tableofcontents.Fragments
|
||||||
|
toVisit []*html.Node
|
||||||
|
)
|
||||||
|
|
||||||
|
// find body
|
||||||
|
f = func(n *html.Node) bool {
|
||||||
|
if n.Type == html.ElementNode && n.Data == "body" {
|
||||||
|
body = n
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if n.FirstChild != nil {
|
||||||
|
toVisit = append(toVisit, n.FirstChild)
|
||||||
|
}
|
||||||
|
if n.NextSibling != nil && f(n.NextSibling) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for len(toVisit) > 0 {
|
||||||
|
nv := toVisit[0]
|
||||||
|
toVisit = toVisit[1:]
|
||||||
|
if f(nv) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !f(node) {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove by pandoc generated title
|
||||||
|
f = func(n *html.Node) bool {
|
||||||
|
if n.Type == html.ElementNode && n.Data == "header" && attr(n, "id") == "title-block-header" {
|
||||||
|
n.Parent.RemoveChild(n)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if n.FirstChild != nil {
|
||||||
|
toVisit = append(toVisit, n.FirstChild)
|
||||||
|
}
|
||||||
|
if n.NextSibling != nil && f(n.NextSibling) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for len(toVisit) > 0 {
|
||||||
|
nv := toVisit[0]
|
||||||
|
toVisit = toVisit[1:]
|
||||||
|
if f(nv) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f(body)
|
||||||
|
|
||||||
|
// find toc
|
||||||
|
f = func(n *html.Node) bool {
|
||||||
|
if n.Type == html.ElementNode && n.Data == "nav" && attr(n, "id") == "TOC" {
|
||||||
|
toc = parseTOC(n)
|
||||||
|
if !a.cfg.MarkupConfig().Pandoc.PreserveTOC {
|
||||||
|
n.Parent.RemoveChild(n)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if n.FirstChild != nil {
|
||||||
|
toVisit = append(toVisit, n.FirstChild)
|
||||||
|
}
|
||||||
|
if n.NextSibling != nil && f(n.NextSibling) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for len(toVisit) > 0 {
|
||||||
|
nv := toVisit[0]
|
||||||
|
toVisit = toVisit[1:]
|
||||||
|
if f(nv) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
err = html.Render(&buf, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// ltrim <html><head></head><body>\n\n and rtrim \n\n</body></html> which are added by html.Render
|
||||||
|
res := buf.Bytes()[8:]
|
||||||
|
res = res[:len(res)-9]
|
||||||
|
return res, toc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTOC returns a TOC root from the given toc Node
|
||||||
|
func parseTOC(doc *html.Node) *tableofcontents.Fragments {
|
||||||
|
var (
|
||||||
|
toc tableofcontents.Builder
|
||||||
|
f func(*html.Node, int, int)
|
||||||
|
)
|
||||||
|
f = func(n *html.Node, row, level int) {
|
||||||
|
if n.Type == html.ElementNode {
|
||||||
|
switch n.Data {
|
||||||
|
case "ul":
|
||||||
|
if level == 0 {
|
||||||
|
row++
|
||||||
|
}
|
||||||
|
level++
|
||||||
|
f(n.FirstChild, row, level)
|
||||||
|
case "li":
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
if c.Type != html.ElementNode || c.Data != "a" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
href := attr(c, "href")[1:]
|
||||||
|
toc.AddAt(&tableofcontents.Heading{
|
||||||
|
Title: nodeContent(c),
|
||||||
|
ID: href,
|
||||||
|
}, row, level)
|
||||||
|
}
|
||||||
|
f(n.FirstChild, row, level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.NextSibling != nil {
|
||||||
|
f(n.NextSibling, row, level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(doc.FirstChild, -1, 0)
|
||||||
|
return toc.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
func attr(node *html.Node, key string) string {
|
||||||
|
for _, a := range node.Attr {
|
||||||
|
if a.Key == key {
|
||||||
|
return a.Val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeContent(node *html.Node) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
html.Render(&buf, c)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Supports returns whether Pandoc is installed on this computer.
|
// Supports returns whether Pandoc is installed on this computer.
|
||||||
func Supports() bool {
|
func Supports() bool {
|
||||||
hasBin := getPandocBinaryName() != ""
|
hasBin := getPandocBinaryName() != ""
|
||||||
|
|
85
markup/pandoc/integration_test.go
Normal file
85
markup/pandoc/integration_test.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2021 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package pandoc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBasicConversion(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
-- content/p1.md --
|
||||||
|
testContent
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
{{ .Content }}
|
||||||
|
`
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
NeedsOsFS: true,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html", `<p>testContent</p>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConversionWithHeader(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
-- content/p1.md --
|
||||||
|
# testContent
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
{{ .Content }}
|
||||||
|
`
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
NeedsOsFS: true,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html", `<h1 id="testcontent">testContent</h1>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConversionWithExtractedToc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
-- content/p1.md --
|
||||||
|
# title 1
|
||||||
|
## title 2
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
{{ .TableOfContents }}
|
||||||
|
{{ .Content }}
|
||||||
|
`
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
NeedsOsFS: true,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html", "<nav id=\"TableOfContents\">\n <ul>\n <li><a href=\"#title-2\">title 2</a></li>\n </ul>\n</nav>\n<h1 id=\"title-1\">title 1</h1>\n<h2 id=\"title-2\">title 2</h2>")
|
||||||
|
}
|
27
markup/pandoc/pandoc_config/config.go
Normal file
27
markup/pandoc/pandoc_config/config.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package pandocdoc_config holds pandoc related configuration.
|
||||||
|
package pandoc_config
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Default holds Hugo's default pandoc configuration.
|
||||||
|
Default = Config{
|
||||||
|
PreserveTOC: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config configures pandoc.
|
||||||
|
type Config struct {
|
||||||
|
PreserveTOC bool
|
||||||
|
}
|
|
@ -261,7 +261,10 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import) (*moduleAdapt
|
||||||
// This will select the latest release-version (not beta etc.).
|
// This will select the latest release-version (not beta etc.).
|
||||||
versionQuery = "upgrade"
|
versionQuery = "upgrade"
|
||||||
}
|
}
|
||||||
if err := c.Get(fmt.Sprintf("%s@%s", modulePath, versionQuery)); err != nil {
|
|
||||||
|
// Note that we cannot use c.Get for this, as that may
|
||||||
|
// trigger a new module collection and potentially create a infinite loop.
|
||||||
|
if err := c.get(fmt.Sprintf("%s@%s", modulePath, versionQuery)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := c.loadModules(); err != nil {
|
if err := c.loadModules(); err != nil {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{{- $src = .RelPermalink -}}
|
{{- $src = .RelPermalink -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" .Title) -}}
|
{{- $attributes := merge .Attributes (dict "alt" .Text "src" $src "title" (.Title | transform.HTMLEscape)) -}}
|
||||||
<img
|
<img
|
||||||
{{- range $k, $v := $attributes -}}
|
{{- range $k, $v := $attributes -}}
|
||||||
{{- if $v -}}
|
{{- if $v -}}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- $attributes := dict "href" $href "title" .Title -}}
|
{{- $attributes := dict "href" $href "title" (.Title | transform.HTMLEscape) -}}
|
||||||
<a
|
<a
|
||||||
{{- range $k, $v := $attributes -}}
|
{{- range $k, $v := $attributes -}}
|
||||||
{{- if $v -}}
|
{{- if $v -}}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||||
<link>{{ .Permalink }}</link>
|
<link>{{ .Permalink }}</link>
|
||||||
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||||
<generator>Hugo {{ hugo.Version }}</generator>
|
<generator>Hugo</generator>
|
||||||
<language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
|
<language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
|
||||||
<managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
|
<managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
|
||||||
<webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster>{{ end }}{{ with .Site.Copyright }}
|
<webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster>{{ end }}{{ with .Site.Copyright }}
|
||||||
|
|
|
@ -8,10 +8,10 @@ Renders an embedded YouTube video.
|
||||||
@param {int} [end] The time, measured in seconds from the start of the video, when the player should stop playing the video.
|
@param {int} [end] The time, measured in seconds from the start of the video, when the player should stop playing the video.
|
||||||
@param {string} [id] The video id. Optional if the id is provided as first positional argument.
|
@param {string} [id] The video id. Optional if the id is provided as first positional argument.
|
||||||
@param {string} [loading=eager] The loading attribute of the iframe element.
|
@param {string} [loading=eager] The loading attribute of the iframe element.
|
||||||
@param {bool} [loop=false] Whether to indefinitely repeat the video.
|
@param {bool} [loop=false] Whether to indefinitely repeat the video. Ignores the start and end arguments after the first play.
|
||||||
@param {bool} [mute=false] Whether to mute the video. Always true when autoplay is true.
|
@param {bool} [mute=false] Whether to mute the video. Always true when autoplay is true.
|
||||||
@param {int} [start] The time, measured in seconds from the start of the video, when the player should start playing the video.
|
@param {int} [start] The time, measured in seconds from the start of the video, when the player should start playing the video.
|
||||||
@param {string} [title] The title attribute of the iframe element. Defaults to the title returned by YouTube oEmbed API.
|
@param {string} [title] The title attribute of the iframe element. Defaults to "YouTube video".
|
||||||
|
|
||||||
@returns {template.HTML}
|
@returns {template.HTML}
|
||||||
|
|
||||||
|
@ -26,20 +26,6 @@ Renders an embedded YouTube video.
|
||||||
{{- if not $pc.Disable }}
|
{{- if not $pc.Disable }}
|
||||||
{{- with $id := or (.Get "id") (.Get 0) }}
|
{{- with $id := or (.Get "id") (.Get 0) }}
|
||||||
|
|
||||||
{{- /* Get data from the YouTube oEmbed API. */}}
|
|
||||||
{{- $q := querify "url" (printf "https://www.youtube.com/watch?v=%s" $id) "format" "json" }}
|
|
||||||
{{- $url := printf "https://www.youtube.com/oembed?%s" $q }}
|
|
||||||
{{- $data := dict }}
|
|
||||||
{{- with resources.GetRemote $url }}
|
|
||||||
{{- with .Err }}
|
|
||||||
{{- erroridf $remoteErrID "The %q shortcode was unable to get remote resource %q. %s. See %s" $.Name $url . $.Position }}
|
|
||||||
{{- else }}
|
|
||||||
{{- $data = .Content | transform.Unmarshal }}
|
|
||||||
{{- end }}
|
|
||||||
{{- else }}
|
|
||||||
{{- erroridf $remoteErrID "The %q shortcode was unable to get remote resource %q. See %s" $.Name $url $.Position }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/* Set defaults. */}}
|
{{/* Set defaults. */}}
|
||||||
{{- $allowFullScreen := "allowfullscreen" }}
|
{{- $allowFullScreen := "allowfullscreen" }}
|
||||||
{{- $autoplay := 0 }}
|
{{- $autoplay := 0 }}
|
||||||
|
@ -50,7 +36,7 @@ Renders an embedded YouTube video.
|
||||||
{{- $loop := 0 }}
|
{{- $loop := 0 }}
|
||||||
{{- $mute := 0 }}
|
{{- $mute := 0 }}
|
||||||
{{- $start := 0 }}
|
{{- $start := 0 }}
|
||||||
{{- $title := $data.title }}
|
{{- $title := "YouTube video" }}
|
||||||
|
|
||||||
{{- /* Get arguments. */}}
|
{{- /* Get arguments. */}}
|
||||||
{{- if in (slice "false" false 0) ($.Get "allowFullScreen") }}
|
{{- if in (slice "false" false 0) ($.Get "allowFullScreen") }}
|
||||||
|
|
Loading…
Reference in a new issue