mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
parent
596e0e98e4
commit
2079a23dd8
21 changed files with 324 additions and 189 deletions
|
@ -38,16 +38,22 @@ and taxonomy pages will be rendered below `/en` in English, and below `/fr` in F
|
|||
|
||||
Only the obvious non-global options can be overridden per language. Examples of global options are `BaseURL`, `BuildDrafts`, etc.
|
||||
|
||||
Taxonomies configuration can also be set per language, example:
|
||||
Taxonomies and Blackfriday configuration can also be set per language, example:
|
||||
|
||||
```
|
||||
[Taxonomies]
|
||||
tag = "tags"
|
||||
|
||||
[blackfriday]
|
||||
angledQuotes = true
|
||||
hrefTargetBlank = true
|
||||
|
||||
[Languages]
|
||||
[Languages.en]
|
||||
weight = 1
|
||||
title = "English"
|
||||
[Languages.en.blackfriday]
|
||||
angledQuotes = false
|
||||
|
||||
[Languages.fr]
|
||||
weight = 2
|
||||
|
|
25
helpers/configProvider.go
Normal file
25
helpers/configProvider.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2016-present 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 helpers implements general utility functions that work with
|
||||
// and on content. The helper functions defined here lay down the
|
||||
// foundation of how Hugo works with files and filepaths, and perform
|
||||
// string operations on content.
|
||||
package helpers
|
||||
|
||||
// ConfigProvider provides the configuration settings for Hugo.
|
||||
type ConfigProvider interface {
|
||||
GetString(key string) string
|
||||
GetStringMap(key string) map[string]interface{}
|
||||
GetStringMapString(key string) map[string]string
|
||||
}
|
|
@ -59,7 +59,7 @@ type Blackfriday struct {
|
|||
}
|
||||
|
||||
// NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
|
||||
func NewBlackfriday() *Blackfriday {
|
||||
func NewBlackfriday(c ConfigProvider) *Blackfriday {
|
||||
combinedParam := map[string]interface{}{
|
||||
"smartypants": true,
|
||||
"angledQuotes": false,
|
||||
|
@ -72,7 +72,7 @@ func NewBlackfriday() *Blackfriday {
|
|||
"sourceRelativeLinksProjectFolder": "/docs/content",
|
||||
}
|
||||
|
||||
siteParam := viper.GetStringMap("blackfriday")
|
||||
siteParam := c.GetStringMap("blackfriday")
|
||||
if siteParam != nil {
|
||||
siteConfig := cast.ToStringMap(siteParam)
|
||||
|
||||
|
@ -341,20 +341,25 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
|
|||
// RenderingContext holds contextual information, like content and configuration,
|
||||
// for a given content rendering.
|
||||
type RenderingContext struct {
|
||||
Content []byte
|
||||
PageFmt string
|
||||
DocumentID string
|
||||
Config *Blackfriday
|
||||
RenderTOC bool
|
||||
FileResolver FileResolverFunc
|
||||
LinkResolver LinkResolverFunc
|
||||
configInit sync.Once
|
||||
Content []byte
|
||||
PageFmt string
|
||||
DocumentID string
|
||||
Config *Blackfriday
|
||||
RenderTOC bool
|
||||
FileResolver FileResolverFunc
|
||||
LinkResolver LinkResolverFunc
|
||||
ConfigProvider ConfigProvider
|
||||
configInit sync.Once
|
||||
}
|
||||
|
||||
func newViperProvidedRenderingContext() *RenderingContext {
|
||||
return &RenderingContext{ConfigProvider: viper.GetViper()}
|
||||
}
|
||||
|
||||
func (c *RenderingContext) getConfig() *Blackfriday {
|
||||
c.configInit.Do(func() {
|
||||
if c.Config == nil {
|
||||
c.Config = NewBlackfriday()
|
||||
c.Config = NewBlackfriday(c.ConfigProvider)
|
||||
}
|
||||
})
|
||||
return c.Config
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
// Renders a codeblock using Blackfriday
|
||||
func render(input string) string {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
render := getHTMLRenderer(0, ctx)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
@ -33,7 +33,7 @@ func render(input string) string {
|
|||
|
||||
// Renders a codeblock using Mmark
|
||||
func renderWithMmark(input string) string {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
render := getMmarkHTMLRenderer(0, ctx)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/miekg/mmark"
|
||||
"github.com/russross/blackfriday"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -124,7 +125,7 @@ func TestTruncateWordsByRune(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetHTMLRendererFlags(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
renderer := getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
|
||||
flags := renderer.GetFlags()
|
||||
if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
|
||||
|
@ -148,7 +149,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
|
|||
{blackfriday.HTML_SMARTYPANTS_LATEX_DASHES},
|
||||
}
|
||||
defaultFlags := blackfriday.HTML_USE_XHTML
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.AngledQuotes = true
|
||||
ctx.Config.Fractions = true
|
||||
|
@ -171,7 +172,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetHTMLRendererAnchors(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.DocumentID = "testid"
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.PlainIDAnchors = false
|
||||
|
@ -195,7 +196,7 @@ func TestGetHTMLRendererAnchors(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetMmarkHTMLRenderer(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.DocumentID = "testid"
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.PlainIDAnchors = false
|
||||
|
@ -219,7 +220,7 @@ func TestGetMmarkHTMLRenderer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"headerId"}
|
||||
ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"}
|
||||
|
@ -234,7 +235,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
|
|||
type data struct {
|
||||
testFlag int
|
||||
}
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{""}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
|
@ -266,7 +267,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"definitionLists"}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
|
@ -278,7 +279,7 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetMarkdownRenderer(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := markdownRender(ctx)
|
||||
|
@ -289,7 +290,7 @@ func TestGetMarkdownRenderer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetMarkdownRendererWithTOC(t *testing.T) {
|
||||
ctx := &RenderingContext{RenderTOC: true}
|
||||
ctx := &RenderingContext{RenderTOC: true, ConfigProvider: viper.GetViper()}
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := markdownRender(ctx)
|
||||
|
@ -304,7 +305,7 @@ func TestGetMmarkExtensions(t *testing.T) {
|
|||
type data struct {
|
||||
testFlag int
|
||||
}
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Config = ctx.getConfig()
|
||||
ctx.Config.Extensions = []string{"tables"}
|
||||
ctx.Config.ExtensionsMask = []string{""}
|
||||
|
@ -333,7 +334,7 @@ func TestGetMmarkExtensions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMmarkRender(t *testing.T) {
|
||||
ctx := &RenderingContext{}
|
||||
ctx := newViperProvidedRenderingContext()
|
||||
ctx.Content = []byte("testContent")
|
||||
ctx.Config = ctx.getConfig()
|
||||
actualRenderedMarkdown := mmarkRender(ctx)
|
||||
|
|
|
@ -94,7 +94,7 @@ func loadDefaultSettings() {
|
|||
viper.SetDefault("NewContentEditor", "")
|
||||
viper.SetDefault("Paginate", 10)
|
||||
viper.SetDefault("PaginatePath", "page")
|
||||
viper.SetDefault("Blackfriday", helpers.NewBlackfriday())
|
||||
viper.SetDefault("Blackfriday", helpers.NewBlackfriday(viper.GetViper()))
|
||||
viper.SetDefault("RSSUri", "index.xml")
|
||||
viper.SetDefault("SectionPagesMenu", "")
|
||||
viper.SetDefault("DisablePathToLower", false)
|
||||
|
|
|
@ -15,7 +15,6 @@ package hugolib
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -55,10 +54,9 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
|
|||
|
||||
templ := tpl.New()
|
||||
p, _ := pageFromString(simplePageWithURL, path)
|
||||
p.Node.Site = &SiteInfo{
|
||||
rawAllPages: &(Pages{p}),
|
||||
BaseURL: template.URL(helpers.SanitizeURLKeepTrailingSlash(baseURL)),
|
||||
}
|
||||
p.Node.Site = newSiteInfoDefaultLanguage(
|
||||
helpers.SanitizeURLKeepTrailingSlash(baseURL),
|
||||
p)
|
||||
|
||||
output, err := HandleShortcodes(in, p, templ)
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ type HugoSites struct {
|
|||
|
||||
// NewHugoSites creates a new collection of sites given the input sites, building
|
||||
// a language configuration based on those.
|
||||
func NewHugoSites(sites ...*Site) (*HugoSites, error) {
|
||||
func newHugoSites(sites ...*Site) (*HugoSites, error) {
|
||||
langConfig, err := newMultiLingualFromSites(sites...)
|
||||
|
||||
if err != nil {
|
||||
|
@ -55,7 +55,7 @@ func NewHugoSitesFromConfiguration() (*HugoSites, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewHugoSites(sites...)
|
||||
return newHugoSites(sites...)
|
||||
}
|
||||
|
||||
func createSitesFromConfig() ([]*Site, error) {
|
||||
|
@ -461,7 +461,7 @@ func buildAndRenderSite(s *Site, additionalTemplates ...string) error {
|
|||
|
||||
// Convenience func used in tests to build a single site/language.
|
||||
func doBuildSite(s *Site, render bool, additionalTemplates ...string) error {
|
||||
sites, err := NewHugoSites(s)
|
||||
sites, err := newHugoSites(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -490,7 +490,7 @@ func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages Lan
|
|||
Language: languages[0],
|
||||
}
|
||||
if len(languages) == 1 {
|
||||
return NewHugoSites(first)
|
||||
return newHugoSites(first)
|
||||
}
|
||||
|
||||
sites := make([]*Site, len(languages))
|
||||
|
@ -499,7 +499,7 @@ func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages Lan
|
|||
sites[i] = &Site{Language: languages[i]}
|
||||
}
|
||||
|
||||
return NewHugoSites(sites...)
|
||||
return newHugoSites(sites...)
|
||||
|
||||
}
|
||||
|
||||
|
@ -507,3 +507,7 @@ func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages Lan
|
|||
func newHugoSitesFromLanguages(languages Languages) (*HugoSites, error) {
|
||||
return newHugoSitesFromSourceAndLanguages(nil, languages)
|
||||
}
|
||||
|
||||
func newHugoSitesDefaultLanguage() (*HugoSites, error) {
|
||||
return newHugoSitesFromSourceAndLanguages(nil, Languages{newDefaultLanguage()})
|
||||
}
|
||||
|
|
|
@ -137,6 +137,12 @@ func TestMultiSitesBuild(t *testing.T) {
|
|||
require.NotNil(t, frTags["frtag1"])
|
||||
readDestination(t, "public/fr/plaques/frtag1/index.html")
|
||||
readDestination(t, "public/en/tags/tag1/index.html")
|
||||
|
||||
// Check Blackfriday config
|
||||
assert.True(t, strings.Contains(string(doc1fr.Content), "«"), string(doc1fr.Content))
|
||||
assert.False(t, strings.Contains(string(doc1en.Content), "«"), string(doc1en.Content))
|
||||
assert.True(t, strings.Contains(string(doc1en.Content), "“"), string(doc1en.Content))
|
||||
|
||||
}
|
||||
|
||||
func TestMultiSitesRebuild(t *testing.T) {
|
||||
|
@ -326,7 +332,6 @@ title = "Norsk"
|
|||
|
||||
// Watching does not work with in-memory fs, so we trigger a reload manually
|
||||
require.NoError(t, viper.ReadInConfig())
|
||||
|
||||
err = sites.Build(BuildCfg{CreateSitesFromConfig: true})
|
||||
|
||||
if err != nil {
|
||||
|
@ -370,7 +375,10 @@ paginate = 2
|
|||
DefaultContentLanguage = "fr"
|
||||
|
||||
[permalinks]
|
||||
other = "/somewhere/else/:filename"
|
||||
other = "/somewhere/else/:filename"
|
||||
|
||||
[blackfriday]
|
||||
angledQuotes = true
|
||||
|
||||
[Taxonomies]
|
||||
tag = "tags"
|
||||
|
@ -379,6 +387,8 @@ tag = "tags"
|
|||
[Languages.en]
|
||||
weight = 10
|
||||
title = "English"
|
||||
[Languages.en.blackfriday]
|
||||
angledQuotes = false
|
||||
|
||||
[Languages.fr]
|
||||
weight = 20
|
||||
|
@ -441,7 +451,7 @@ tags:
|
|||
publishdate: "2000-01-01"
|
||||
---
|
||||
# doc1
|
||||
*some content*
|
||||
*some "content"*
|
||||
NOTE: slug should be used as URL
|
||||
`)},
|
||||
{filepath.FromSlash("sect/doc1.fr.md"), []byte(`---
|
||||
|
@ -452,7 +462,7 @@ plaques:
|
|||
publishdate: "2000-01-04"
|
||||
---
|
||||
# doc1
|
||||
*quelque contenu*
|
||||
*quelque "contenu"*
|
||||
NOTE: should be in the 'en' Page's 'Translations' field.
|
||||
NOTE: date is after "doc3"
|
||||
`)},
|
||||
|
|
|
@ -25,9 +25,14 @@ func NewLanguage(lang string) *Language {
|
|||
return &Language{Lang: lang, params: make(map[string]interface{})}
|
||||
}
|
||||
|
||||
// TODO(bep) multilingo
|
||||
func newDefaultLanguage() *Language {
|
||||
return NewLanguage("en")
|
||||
defaultLang := viper.GetString("DefaultContentLanguage")
|
||||
|
||||
if defaultLang == "" {
|
||||
defaultLang = "en"
|
||||
}
|
||||
|
||||
return NewLanguage(defaultLang)
|
||||
}
|
||||
|
||||
type Languages []*Language
|
||||
|
@ -74,16 +79,18 @@ func newMultiLingualFromSites(sites ...*Site) (*Multilingual, error) {
|
|||
languages[i] = s.Language
|
||||
}
|
||||
|
||||
defaultLang := viper.GetString("DefaultContentLanguage")
|
||||
|
||||
if defaultLang == "" {
|
||||
defaultLang = "en"
|
||||
}
|
||||
|
||||
return &Multilingual{Languages: languages, DefaultLang: NewLanguage(defaultLang)}, nil
|
||||
return &Multilingual{Languages: languages, DefaultLang: newDefaultLanguage()}, nil
|
||||
|
||||
}
|
||||
|
||||
func newMultiLingualDefaultLanguage() *Multilingual {
|
||||
return newMultiLingualForLanguage(newDefaultLanguage())
|
||||
}
|
||||
|
||||
func newMultiLingualForLanguage(language *Language) *Multilingual {
|
||||
languages := Languages{language}
|
||||
return &Multilingual{Languages: languages, DefaultLang: language}
|
||||
}
|
||||
func (ml *Multilingual) enabled() bool {
|
||||
return len(ml.Languages) > 1
|
||||
}
|
||||
|
@ -92,6 +99,7 @@ func (l *Language) Params() map[string]interface{} {
|
|||
l.paramsInit.Do(func() {
|
||||
// Merge with global config.
|
||||
// TODO(bep) consider making this part of a constructor func.
|
||||
|
||||
globalParams := viper.GetStringMap("Params")
|
||||
for k, v := range globalParams {
|
||||
if _, ok := l.params[k]; !ok {
|
||||
|
@ -116,6 +124,9 @@ func (l *Language) GetStringMapString(key string) map[string]string {
|
|||
}
|
||||
|
||||
func (l *Language) Get(key string) interface{} {
|
||||
if l == nil {
|
||||
panic("language not set")
|
||||
}
|
||||
key = strings.ToLower(key)
|
||||
if v, ok := l.params[key]; ok {
|
||||
return v
|
||||
|
@ -159,7 +170,7 @@ func toSortedLanguages(l map[string]interface{}) (Languages, error) {
|
|||
}
|
||||
|
||||
// Put all into the Params map
|
||||
// TODO(bep) reconsile with the type handling etc. from other params handlers.
|
||||
// TODO(bep) ml reconsile with the type handling etc. from other params handlers.
|
||||
language.SetParam(loki, v)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"github.com/spf13/hugo/helpers"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
@ -45,8 +47,9 @@ type Node struct {
|
|||
paginatorInit sync.Once
|
||||
scratch *Scratch
|
||||
|
||||
language *Language
|
||||
lang string // TODO(bep) multilingo
|
||||
language *Language
|
||||
languageInit sync.Once
|
||||
lang string // TODO(bep) multilingo
|
||||
|
||||
translations Nodes
|
||||
translationsInit sync.Once
|
||||
|
@ -191,6 +194,7 @@ func (n *Node) Scratch() *Scratch {
|
|||
|
||||
// TODO(bep) multilingo consolidate. See Page.
|
||||
func (n *Node) Language() *Language {
|
||||
n.initLanguage()
|
||||
return n.language
|
||||
}
|
||||
|
||||
|
@ -204,6 +208,31 @@ func (n *Node) Lang() string {
|
|||
return n.lang
|
||||
}
|
||||
|
||||
func (n *Node) initLanguage() {
|
||||
n.languageInit.Do(func() {
|
||||
pageLang := n.lang
|
||||
ml := n.Site.multilingual
|
||||
if ml == nil {
|
||||
panic("Multilanguage not set")
|
||||
}
|
||||
if pageLang == "" {
|
||||
n.language = ml.DefaultLang
|
||||
return
|
||||
}
|
||||
|
||||
language := ml.Language(pageLang)
|
||||
|
||||
if language == nil {
|
||||
// TODO(bep) ml
|
||||
// This may or may not be serious. It can be a file named stefano.chiodino.md.
|
||||
jww.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
|
||||
language = ml.DefaultLang
|
||||
}
|
||||
|
||||
n.language = language
|
||||
})
|
||||
}
|
||||
|
||||
func (n *Node) LanguagePrefix() string {
|
||||
return n.Site.LanguagePrefix
|
||||
}
|
||||
|
@ -261,7 +290,7 @@ func (n *Node) addMultilingualWebPrefix(outfile string) string {
|
|||
hadSlashSuffix := strings.HasSuffix(outfile, "/")
|
||||
|
||||
lang := n.Lang()
|
||||
if lang == "" || !n.Site.Multilingual {
|
||||
if lang == "" || !n.Site.IsMultiLingual() {
|
||||
return outfile
|
||||
}
|
||||
outfile = "/" + path.Join(lang, outfile)
|
||||
|
@ -273,7 +302,7 @@ func (n *Node) addMultilingualWebPrefix(outfile string) string {
|
|||
|
||||
func (n *Node) addMultilingualFilesystemPrefix(outfile string) string {
|
||||
lang := n.Lang()
|
||||
if lang == "" || !n.Site.Multilingual {
|
||||
if lang == "" || !n.Site.IsMultiLingual() {
|
||||
return outfile
|
||||
}
|
||||
return string(filepath.Separator) + filepath.Join(lang, outfile)
|
||||
|
|
|
@ -319,7 +319,8 @@ func (p *Page) setAutoSummary() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Page) renderBytes(content []byte) []byte {
|
||||
// TODO(bep) ml not used???
|
||||
func (p *Page) _renderBytes(content []byte) []byte {
|
||||
var fn helpers.LinkResolverFunc
|
||||
var fileFn helpers.FileResolverFunc
|
||||
if p.getRenderingConfig().SourceRelativeLinksEval {
|
||||
|
@ -331,8 +332,10 @@ func (p *Page) renderBytes(content []byte) []byte {
|
|||
}
|
||||
}
|
||||
return helpers.RenderBytes(
|
||||
&helpers.RenderingContext{Content: content, PageFmt: p.determineMarkupType(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn})
|
||||
&helpers.RenderingContext{
|
||||
Content: content, PageFmt: p.determineMarkupType(),
|
||||
ConfigProvider: p.Language(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn})
|
||||
}
|
||||
|
||||
func (p *Page) renderContent(content []byte) []byte {
|
||||
|
@ -346,16 +349,20 @@ func (p *Page) renderContent(content []byte) []byte {
|
|||
return p.Node.Site.SourceRelativeLinkFile(ref, p)
|
||||
}
|
||||
}
|
||||
return helpers.RenderBytes(&helpers.RenderingContext{Content: content, RenderTOC: true, PageFmt: p.determineMarkupType(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn})
|
||||
return helpers.RenderBytes(&helpers.RenderingContext{
|
||||
Content: content, RenderTOC: true, PageFmt: p.determineMarkupType(),
|
||||
ConfigProvider: p.Language(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn})
|
||||
}
|
||||
|
||||
func (p *Page) getRenderingConfig() *helpers.Blackfriday {
|
||||
|
||||
p.renderingConfigInit.Do(func() {
|
||||
pageParam := cast.ToStringMap(p.GetParam("blackfriday"))
|
||||
|
||||
p.renderingConfig = helpers.NewBlackfriday()
|
||||
if p.Language() == nil {
|
||||
panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang))
|
||||
}
|
||||
p.renderingConfig = helpers.NewBlackfriday(p.Language())
|
||||
if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil {
|
||||
jww.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
|
||||
}
|
||||
|
|
|
@ -141,9 +141,7 @@ func createSortTestPages(num int) Pages {
|
|||
Section: "z",
|
||||
URL: fmt.Sprintf("http://base/x/y/p%d.html", i),
|
||||
},
|
||||
Site: &SiteInfo{
|
||||
BaseURL: "http://base/",
|
||||
},
|
||||
Site: newSiteInfoDefaultLanguage("http://base/"),
|
||||
},
|
||||
Source: Source{File: *source.NewFile(filepath.FromSlash(fmt.Sprintf("/x/y/p%d.md", i)))},
|
||||
}
|
||||
|
|
|
@ -69,9 +69,7 @@ func TestPermalink(t *testing.T) {
|
|||
Section: "z",
|
||||
URL: test.url,
|
||||
},
|
||||
Site: &SiteInfo{
|
||||
BaseURL: test.base,
|
||||
},
|
||||
Site: newSiteInfoDefaultLanguage(string(test.base)),
|
||||
},
|
||||
Source: Source{File: *source.NewFile(filepath.FromSlash(test.file))},
|
||||
}
|
||||
|
|
|
@ -1117,7 +1117,7 @@ func TestPagePaths(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
p, _ := NewPageFrom(strings.NewReader(test.content), filepath.FromSlash(test.path))
|
||||
p.Node.Site = &SiteInfo{}
|
||||
p.Node.Site = newSiteInfoDefaultLanguage("")
|
||||
|
||||
if test.hasPermalink {
|
||||
p.Node.Site.Permalinks = siteParmalinksSetting
|
||||
|
|
|
@ -460,9 +460,7 @@ func createTestPages(num int) Pages {
|
|||
Section: "z",
|
||||
URL: fmt.Sprintf("http://base/x/y/p%d.html", i),
|
||||
},
|
||||
Site: &SiteInfo{
|
||||
BaseURL: "http://base/",
|
||||
},
|
||||
Site: newSiteInfoDefaultLanguage("http://base/"),
|
||||
},
|
||||
Source: Source{File: *source.NewFile(filepath.FromSlash(fmt.Sprintf("/x/y/p%d.md", i)))},
|
||||
}
|
||||
|
|
|
@ -242,7 +242,8 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Tem
|
|||
if sc.doMarkup {
|
||||
newInner := helpers.RenderBytes(&helpers.RenderingContext{
|
||||
Content: []byte(inner), PageFmt: p.determineMarkupType(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig()})
|
||||
ConfigProvider: p.Language(),
|
||||
DocumentID: p.UniqueID(), Config: p.getRenderingConfig()})
|
||||
|
||||
// If the type is “unknown” or “markdown”, we assume the markdown
|
||||
// generation has been performed. Given the input: `a line`, markdown
|
||||
|
|
|
@ -28,29 +28,59 @@ import (
|
|||
"github.com/spf13/hugo/target"
|
||||
"github.com/spf13/hugo/tpl"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TODO(bep) remove
|
||||
func pageFromString(in, filename string) (*Page, error) {
|
||||
return NewPageFrom(strings.NewReader(in), filename)
|
||||
}
|
||||
|
||||
func CheckShortCodeMatch(t *testing.T, input, expected string, template tpl.Template) {
|
||||
CheckShortCodeMatchAndError(t, input, expected, template, false)
|
||||
func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
|
||||
CheckShortCodeMatchAndError(t, input, expected, withTemplate, false)
|
||||
}
|
||||
|
||||
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, template tpl.Template, expectError bool) {
|
||||
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error, expectError bool) {
|
||||
testCommonResetState()
|
||||
|
||||
p, _ := pageFromString(simplePage, "simple.md")
|
||||
output, err := HandleShortcodes(input, p, template)
|
||||
// Need some front matter, see https://github.com/spf13/hugo/issues/2337
|
||||
contentFile := `---
|
||||
title: "Title"
|
||||
---
|
||||
` + input
|
||||
|
||||
writeSource(t, "content/simple.md", contentFile)
|
||||
|
||||
h, err := newHugoSitesDefaultLanguage()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create sites: %s", err)
|
||||
}
|
||||
|
||||
cfg := BuildCfg{SkipRender: true, withTemplate: withTemplate}
|
||||
|
||||
err = h.Build(cfg)
|
||||
|
||||
if err != nil && !expectError {
|
||||
t.Fatalf("Shortcode rendered error %s. Expected: %q, Got: %q", err, expected, output)
|
||||
t.Fatalf("Shortcode rendered error %s.", err)
|
||||
}
|
||||
|
||||
if err == nil && expectError {
|
||||
t.Fatalf("No error from shortcode")
|
||||
}
|
||||
|
||||
require.Len(t, h.Sites[0].Pages, 1)
|
||||
|
||||
output := strings.TrimSpace(string(h.Sites[0].Pages[0].Content))
|
||||
if strings.HasPrefix(output, "<p>") {
|
||||
output = output[3:]
|
||||
}
|
||||
if strings.HasSuffix(output, "</p>") {
|
||||
output = output[:len(output)-4]
|
||||
}
|
||||
|
||||
expected = strings.TrimSpace(expected)
|
||||
|
||||
if output != expected {
|
||||
t.Fatalf("Shortcode render didn't match. got \n%q but expected \n%q", output, expected)
|
||||
}
|
||||
|
@ -86,115 +116,123 @@ func TestShortcodeGoFuzzReports(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNonSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
|
||||
// notice the syntax diff from 0.12, now comment delims must be added
|
||||
CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", tem)
|
||||
CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", nil)
|
||||
}
|
||||
|
||||
// Issue #929
|
||||
func TestHyphenatedSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
||||
CheckShortCodeMatch(t, "{{< hyphenated-video 47238zzb >}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{< hyphenated-video 47238zzb >}}", "Playing Video 47238zzb", wt)
|
||||
}
|
||||
|
||||
// Issue #1753
|
||||
func TestNoTrailingNewline(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
||||
CheckShortCodeMatch(t, "ab{{< a c >}}d", "abcd", tem)
|
||||
CheckShortCodeMatch(t, "ab{{< a c >}}d", "abcd", wt)
|
||||
}
|
||||
|
||||
func TestPositionalParamSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
|
||||
return nil
|
||||
}
|
||||
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb 132 >}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{<video 47238zzb>}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{<video 47238zzb >}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", tem)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", wt)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb 132 >}}", "Playing Video 47238zzb", wt)
|
||||
CheckShortCodeMatch(t, "{{<video 47238zzb>}}", "Playing Video 47238zzb", wt)
|
||||
CheckShortCodeMatch(t, "{{<video 47238zzb >}}", "Playing Video 47238zzb", wt)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", wt)
|
||||
}
|
||||
|
||||
func TestPositionalParamIndexOutOfBounds(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video error: index out of range for positional param at position 1", tem)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video error: index out of range for positional param at position 1", wt)
|
||||
}
|
||||
|
||||
// some repro issues for panics in Go Fuzz testing
|
||||
func TestShortcodeGoFuzzRepros(t *testing.T) {
|
||||
tt := tpl.New()
|
||||
tt.AddInternalShortcode("inner.html", `Shortcode... {{ with .Get 0 }}{{ . }}{{ end }}-- {{ with .Get 1 }}{{ . }}{{ end }}- {{ with .Inner }}{{ . }}{{ end }}`)
|
||||
// Issue #1337
|
||||
CheckShortCodeMatchAndError(t, "{{%inner\"\"\"\"=\"\"", "", tt, true)
|
||||
}
|
||||
|
||||
func TestNamedParamSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
|
||||
|
||||
CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< img class="aspen" >}}`, `<img class="aspen">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< img src= "one" >}}`, `<img src="one">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< img src ="one" >}}`, `<img src="one">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< img src = "one" >}}`, `<img src="one">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< img src = "one" class = "aspen grove" >}}`, `<img src="one" class="aspen grove">`, tem)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< img class="aspen" >}}`, `<img class="aspen">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< img src= "one" >}}`, `<img src="one">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< img src ="one" >}}`, `<img src="one">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< img src = "one" >}}`, `<img src="one">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< img src = "one" class = "aspen grove" >}}`, `<img src="one" class="aspen grove">`, wt)
|
||||
}
|
||||
|
||||
// Issue #2294
|
||||
func TestNestedNamedMissingParam(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("acc.html", `<div class="acc">{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("acc.html", `<div class="acc">{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t,
|
||||
`{{% acc %}}{{% div %}}d1{{% /div %}}{{% div2 %}}d2{{% /div2 %}}{{% /acc %}}`,
|
||||
"<div class=\"acc\"><div >d1</div><div >d2</div>\n</div>", tem)
|
||||
"<div class=\"acc\"><div >d1</div><div >d2</div>\n</div>", wt)
|
||||
}
|
||||
|
||||
func TestIsNamedParamsSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("byposition.html", `<div id="{{ .Get 0 }}">`)
|
||||
tem.AddInternalShortcode("byname.html", `<div id="{{ .Get "id" }}">`)
|
||||
tem.AddInternalShortcode("ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
|
||||
|
||||
CheckShortCodeMatch(t, `{{< ifnamedparams id="name" >}}`, `<div id="name">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< ifnamedparams position >}}`, `<div id="position">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< byname id="name" >}}`, `<div id="name">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< byname position >}}`, `<div id="error: cannot access positional params by string name">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< byposition position >}}`, `<div id="position">`, tem)
|
||||
CheckShortCodeMatch(t, `{{< byposition id="name" >}}`, `<div id="error: cannot access named params by position">`, tem)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("byposition.html", `<div id="{{ .Get 0 }}">`)
|
||||
tem.AddInternalShortcode("byname.html", `<div id="{{ .Get "id" }}">`)
|
||||
tem.AddInternalShortcode("ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{< ifnamedparams id="name" >}}`, `<div id="name">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< ifnamedparams position >}}`, `<div id="position">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< byname id="name" >}}`, `<div id="name">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< byname position >}}`, `<div id="error: cannot access positional params by string name">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< byposition position >}}`, `<div id="position">`, wt)
|
||||
CheckShortCodeMatch(t, `{{< byposition id="name" >}}`, `<div id="error: cannot access named params by position">`, wt)
|
||||
}
|
||||
|
||||
func TestInnerSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
|
||||
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, tem)
|
||||
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}More Here{{< /inside >}}`, "<div class=\"aspen\">More Here</div>", tem)
|
||||
CheckShortCodeMatch(t, `{{< inside >}}More Here{{< /inside >}}`, "<div>More Here</div>", tem)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, wt)
|
||||
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}More Here{{< /inside >}}`, "<div class=\"aspen\">More Here</div>", wt)
|
||||
CheckShortCodeMatch(t, `{{< inside >}}More Here{{< /inside >}}`, "<div>More Here</div>", wt)
|
||||
}
|
||||
|
||||
func TestInnerSCWithMarkdown(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{% inside %}}
|
||||
# More Here
|
||||
|
||||
[link](http://spf13.com) and text
|
||||
|
||||
{{% /inside %}}`, "<div><h1 id=\"more-here\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>", tem)
|
||||
{{% /inside %}}`, "<div><h1 id=\"more-here\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>", wt)
|
||||
}
|
||||
|
||||
func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{% inside %}}
|
||||
# More Here
|
||||
|
||||
|
@ -210,54 +248,55 @@ And then:
|
|||
This is **plain** text.
|
||||
|
||||
{{< /inside >}}
|
||||
`, "<div><h1 id=\"more-here\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>\n\nAnd then:\n\n<div>\n# More Here\n\nThis is **plain** text.\n\n</div>\n", tem)
|
||||
`, "<div><h1 id=\"more-here\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>\n\n<p>And then:</p>\n\n<p><div>\n# More Here\n\nThis is **plain** text.\n\n</div>", wt)
|
||||
}
|
||||
|
||||
func TestEmbeddedSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
CheckShortCodeMatch(t, "{{% test %}}", "This is a simple Test", tem)
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" />\n \n \n</figure>\n", tem)
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"This is a caption\" />\n \n \n <figcaption>\n <p>\n This is a caption\n \n \n \n </p> \n </figcaption>\n \n</figure>\n", tem)
|
||||
CheckShortCodeMatch(t, "{{% test %}}", "This is a simple Test", nil)
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" />\n \n \n</figure>\n", nil)
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"This is a caption\" />\n \n \n <figcaption>\n <p>\n This is a caption\n \n \n \n </p> \n </figcaption>\n \n</figure>\n", nil)
|
||||
}
|
||||
|
||||
func TestNestedSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
|
||||
tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{% scn1 %}}{{% scn2 %}}{{% /scn1 %}}`, "<div>Outer, inner is <div>SC2</div>\n</div>", wt)
|
||||
|
||||
CheckShortCodeMatch(t, `{{% scn1 %}}{{% scn2 %}}{{% /scn1 %}}`, "<div>Outer, inner is <div>SC2</div>\n</div>", tem)
|
||||
|
||||
CheckShortCodeMatch(t, `{{< scn1 >}}{{% scn2 %}}{{< /scn1 >}}`, "<div>Outer, inner is <div>SC2</div></div>", tem)
|
||||
CheckShortCodeMatch(t, `{{< scn1 >}}{{% scn2 %}}{{< /scn1 >}}`, "<div>Outer, inner is <div>SC2</div></div>", wt)
|
||||
}
|
||||
|
||||
func TestNestedComplexSC(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`)
|
||||
tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`)
|
||||
tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`)
|
||||
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`)
|
||||
tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`)
|
||||
tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{< row >}}1-s{{% column %}}2-**s**{{< aside >}}3-**s**{{< /aside >}}4-s{{% /column %}}5-s{{< /row >}}6-s`,
|
||||
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", tem)
|
||||
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", wt)
|
||||
|
||||
// turn around the markup flag
|
||||
CheckShortCodeMatch(t, `{{% row %}}1-s{{< column >}}2-**s**{{% aside %}}3-**s**{{% /aside %}}4-s{{< /column >}}5-s{{% /row %}}6-s`,
|
||||
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", tem)
|
||||
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", wt)
|
||||
}
|
||||
|
||||
func TestParentShortcode(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
tem.AddInternalShortcode("r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
|
||||
|
||||
wt := func(tem tpl.Template) error {
|
||||
tem.AddInternalShortcode("r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
|
||||
tem.AddInternalShortcode("r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
|
||||
return nil
|
||||
}
|
||||
CheckShortCodeMatch(t, `{{< r1 pr1="p1" >}}1: {{< r2 pr2="p2" >}}2: {{< r3 pr3="p3" >}}{{< /r3 >}}{{< /r2 >}}{{< /r1 >}}`,
|
||||
"1: p1 1: 2: p1p2 2: 3: p1p2p3 ", tem)
|
||||
"1: p1 1: 2: p1p2 2: 3: p1p2p3 ", wt)
|
||||
|
||||
}
|
||||
|
||||
func TestFigureImgWidth(t *testing.T) {
|
||||
tem := tpl.New()
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"apple\" width=\"100px\" />\n \n \n</figure>\n", tem)
|
||||
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"apple\" width=\"100px\" />\n \n \n</figure>\n", nil)
|
||||
}
|
||||
|
||||
func TestHighlight(t *testing.T) {
|
||||
|
@ -539,7 +578,7 @@ tags:
|
|||
|
||||
}
|
||||
|
||||
sites, err := NewHugoSites(s)
|
||||
sites, err := newHugoSites(s)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build site: %s", err)
|
||||
|
|
|
@ -107,7 +107,7 @@ func (s *Site) Reset() *Site {
|
|||
|
||||
// newSite creates a new site in the given language.
|
||||
func newSite(lang *Language) *Site {
|
||||
return &Site{Language: lang}
|
||||
return &Site{Language: lang, Info: SiteInfo{multilingual: newMultiLingualForLanguage(lang)}}
|
||||
}
|
||||
|
||||
// newSite creates a new site in the default language.
|
||||
|
@ -172,12 +172,23 @@ type SiteInfo struct {
|
|||
paginationPageCount uint64
|
||||
Data *map[string]interface{}
|
||||
|
||||
Multilingual bool
|
||||
multilingual *Multilingual
|
||||
Language *Language
|
||||
LanguagePrefix string
|
||||
Languages Languages
|
||||
}
|
||||
|
||||
// Used in tests.
|
||||
func newSiteInfoDefaultLanguage(baseURL string, pages ...*Page) *SiteInfo {
|
||||
ps := Pages(pages)
|
||||
|
||||
return &SiteInfo{
|
||||
BaseURL: template.URL(baseURL),
|
||||
rawAllPages: &ps,
|
||||
multilingual: newMultiLingualDefaultLanguage(),
|
||||
}
|
||||
}
|
||||
|
||||
// SiteSocial is a place to put social details on a site level. These are the
|
||||
// standard keys that themes will expect to have available, but can be
|
||||
// expanded to any others on a per site basis
|
||||
|
@ -218,6 +229,10 @@ func (s *SiteInfo) GetParam(key string) interface{} {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *SiteInfo) IsMultiLingual() bool {
|
||||
return len(s.Languages) > 1
|
||||
}
|
||||
|
||||
func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error) {
|
||||
var refURL *url.URL
|
||||
var err error
|
||||
|
@ -837,7 +852,7 @@ func (s *Site) initialize() (err error) {
|
|||
// HomeAbsURL is a convenience method giving the absolute URL to the home page.
|
||||
func (s *SiteInfo) HomeAbsURL() string {
|
||||
base := "/"
|
||||
if s.Multilingual {
|
||||
if s.IsMultiLingual() {
|
||||
base = s.Language.Lang
|
||||
}
|
||||
return helpers.AbsURL(base)
|
||||
|
@ -880,8 +895,8 @@ func (s *Site) initializeSiteInfo() {
|
|||
LanguageCode: lang.GetString("languagecode"),
|
||||
Copyright: lang.GetString("copyright"),
|
||||
DisqusShortname: lang.GetString("DisqusShortname"),
|
||||
// TODO(bep) multilang, consolidate the below (make into methods etc.)
|
||||
Multilingual: s.multilingualEnabled(),
|
||||
// TODO(bep) ml consolidate the below (make into methods etc.)
|
||||
multilingual: s.Multilingual,
|
||||
Language: lang,
|
||||
LanguagePrefix: languagePrefix,
|
||||
Languages: languages,
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
// Translations represent the other translations for a given page. The
|
||||
// string here is the language code, as affected by the `post.LANG.md`
|
||||
// filename.
|
||||
|
@ -38,17 +34,6 @@ func pagesToTranslationsMap(ml *Multilingual, pages []*Page) map[string]Translat
|
|||
continue
|
||||
}
|
||||
|
||||
language := ml.Language(pageLang)
|
||||
|
||||
if language == nil {
|
||||
// TODO(bep) ml
|
||||
// This may or may not be serious. It can be a file named stefano.chiodino.md.
|
||||
jww.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
|
||||
language = ml.DefaultLang
|
||||
}
|
||||
|
||||
page.language = language
|
||||
|
||||
pageTranslation[pageLang] = page
|
||||
out[base] = pageTranslation
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ import (
|
|||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/hugo/hugofs"
|
||||
|
||||
|
@ -1212,7 +1214,10 @@ func markdownify(in interface{}) (template.HTML, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m := helpers.RenderBytes(&helpers.RenderingContext{Content: []byte(text), PageFmt: "markdown"})
|
||||
// TODO(bep) ml language
|
||||
m := helpers.RenderBytes(&helpers.RenderingContext{
|
||||
ConfigProvider: viper.GetViper(),
|
||||
Content: []byte(text), PageFmt: "markdown"})
|
||||
m = bytes.TrimPrefix(m, markdownTrimPrefix)
|
||||
m = bytes.TrimSuffix(m, markdownTrimSuffix)
|
||||
return template.HTML(m), nil
|
||||
|
|
Loading…
Reference in a new issue