mirror of
https://github.com/gohugoio/hugo.git
synced 2024-04-27 14:21:38 +00:00
Compare commits
8 commits
9e3c27ce4d
...
93331014c1
Author | SHA1 | Date | |
---|---|---|---|
93331014c1 | |||
74ce5dc841 | |||
54a8f0ce21 | |||
38e05bd3c7 | |||
ebfca61ac4 | |||
e1917740af | |||
e22a103565 | |||
70807eee38 |
|
@ -44,15 +44,9 @@ type Disqus struct {
|
|||
type GoogleAnalytics struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
|
||||
UseSessionStorage bool
|
||||
|
||||
// Enabling this will make the GA templates respect the
|
||||
// "Do Not Track" HTTP header. See https://www.paulfurley.com/google-analytics-dnt/.
|
||||
RespectDoNotTrack bool
|
||||
|
||||
// Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
|
||||
AnonymizeIP bool
|
||||
}
|
||||
|
||||
// Instagram holds the privacy configuration settings related to the Instagram shortcode.
|
||||
|
|
|
@ -33,8 +33,6 @@ disable = true
|
|||
[privacy.googleAnalytics]
|
||||
disable = true
|
||||
respectDoNotTrack = true
|
||||
anonymizeIP = true
|
||||
useSessionStorage = true
|
||||
[privacy.instagram]
|
||||
disable = true
|
||||
simple = true
|
||||
|
@ -60,8 +58,7 @@ simple = true
|
|||
|
||||
got := []bool{
|
||||
pc.Disqus.Disable, pc.GoogleAnalytics.Disable,
|
||||
pc.GoogleAnalytics.RespectDoNotTrack, pc.GoogleAnalytics.AnonymizeIP,
|
||||
pc.GoogleAnalytics.UseSessionStorage, pc.Instagram.Disable,
|
||||
pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable,
|
||||
pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT,
|
||||
pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple,
|
||||
pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable,
|
||||
|
|
|
@ -1557,10 +1557,8 @@ config:
|
|||
disqus:
|
||||
disable: false
|
||||
googleAnalytics:
|
||||
anonymizeIP: false
|
||||
disable: false
|
||||
respectDoNotTrack: false
|
||||
useSessionStorage: false
|
||||
instagram:
|
||||
disable: false
|
||||
simple: false
|
||||
|
|
|
@ -15,8 +15,6 @@ package hugolib
|
|||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestInternalTemplatesImage(t *testing.T) {
|
||||
|
@ -70,9 +68,9 @@ title: My Site
|
|||
<meta property="article:published_time" content="2021-02-26T18:02:00-01:00" />
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00-01:00" />
|
||||
<meta itemprop="name" content="My Bundle">
|
||||
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg" />
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00" />
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00" />
|
||||
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg">
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00">
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00">
|
||||
|
||||
`)
|
||||
b.AssertFileContent("public/mypage/index.html", `
|
||||
|
@ -83,57 +81,20 @@ title: My Site
|
|||
<meta property="og:image" content="https://example.org/mypage/sample.jpg" />
|
||||
<meta property="article:published_time" content="2021-02-26T18:02:00+01:00" />
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00+01:00" />
|
||||
<meta itemprop="image" content="https://example.org/pageimg1.jpg" />
|
||||
<meta itemprop="image" content="https://example.org/pageimg2.jpg" />
|
||||
<meta itemprop="image" content="https://example.local/logo.png" />
|
||||
<meta itemprop="image" content="https://example.org/mypage/sample.jpg" />
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00" />
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00" />
|
||||
<meta itemprop="image" content="https://example.org/pageimg1.jpg">
|
||||
<meta itemprop="image" content="https://example.org/pageimg2.jpg">
|
||||
<meta itemprop="image" content="https://example.local/logo.png">
|
||||
<meta itemprop="image" content="https://example.org/mypage/sample.jpg">
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00">
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00">
|
||||
`)
|
||||
b.AssertFileContent("public/mysite/index.html", `
|
||||
<meta name="twitter:image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta property="og:image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta itemprop="image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta itemprop="image" content="https://example.org/siteimg1.jpg">
|
||||
`)
|
||||
}
|
||||
|
||||
// Just some simple test of the embedded templates to avoid
|
||||
// https://github.com/gohugoio/hugo/issues/4757 and similar.
|
||||
func TestEmbeddedTemplates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := qt.New(t)
|
||||
c.Assert(true, qt.Equals, true)
|
||||
|
||||
home := []string{"index.html", `
|
||||
GA:
|
||||
{{ template "_internal/google_analytics.html" . }}
|
||||
|
||||
GA async:
|
||||
|
||||
{{ template "_internal/google_analytics_async.html" . }}
|
||||
|
||||
Disqus:
|
||||
|
||||
{{ template "_internal/disqus.html" . }}
|
||||
|
||||
`}
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithSimpleConfigFile().WithTemplatesAdded(home...)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
// Gheck GA regular and async
|
||||
b.AssertFileContent("public/index.html",
|
||||
"'anonymizeIp', true",
|
||||
"'script','https://www.google-analytics.com/analytics.js','ga');\n\tga('create', 'UA-ga_id', 'auto')",
|
||||
"<script async src='https://www.google-analytics.com/analytics.js'>")
|
||||
|
||||
// Disqus
|
||||
b.AssertFileContent("public/index.html", "\"disqus_shortname\" + '.disqus.com/embed.js';")
|
||||
}
|
||||
|
||||
func TestEmbeddedPaginationTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -449,7 +449,9 @@ func (s *Site) Params() maps.Params {
|
|||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Author() map[string]any {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
if len(s.conf.Author) != 0 {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
}
|
||||
return s.conf.Author
|
||||
}
|
||||
|
||||
|
|
|
@ -258,7 +258,6 @@ id = "UA-ga_id"
|
|||
disable = false
|
||||
[privacy.googleAnalytics]
|
||||
respectDoNotTrack = true
|
||||
anonymizeIP = true
|
||||
[privacy.instagram]
|
||||
simple = true
|
||||
[privacy.twitter]
|
||||
|
|
804
mage_output_file.go
Normal file
804
mage_output_file.go
Normal file
|
@ -0,0 +1,804 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Use local types and functions in order to avoid name conflicts with additional magefiles.
|
||||
type arguments struct {
|
||||
Verbose bool // print out log statements
|
||||
List bool // print out a list of targets
|
||||
Help bool // print out help for a specific target
|
||||
Timeout time.Duration // set a timeout to running the targets
|
||||
Args []string // args contain the non-flag command-line arguments
|
||||
}
|
||||
|
||||
parseBool := func(env string) bool {
|
||||
val := os.Getenv(env)
|
||||
if val == "" {
|
||||
return false
|
||||
}
|
||||
b, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
log.Printf("warning: environment variable %s is not a valid bool value: %v", env, val)
|
||||
return false
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
parseDuration := func(env string) time.Duration {
|
||||
val := os.Getenv(env)
|
||||
if val == "" {
|
||||
return 0
|
||||
}
|
||||
d, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
log.Printf("warning: environment variable %s is not a valid duration value: %v", env, val)
|
||||
return 0
|
||||
}
|
||||
return d
|
||||
}
|
||||
args := arguments{}
|
||||
fs := flag.FlagSet{}
|
||||
fs.SetOutput(os.Stdout)
|
||||
|
||||
// default flag set with ExitOnError and auto generated PrintDefaults should be sufficient
|
||||
fs.BoolVar(&args.Verbose, "v", parseBool("MAGEFILE_VERBOSE"), "show verbose output when running targets")
|
||||
fs.BoolVar(&args.List, "l", parseBool("MAGEFILE_LIST"), "list targets for this binary")
|
||||
fs.BoolVar(&args.Help, "h", parseBool("MAGEFILE_HELP"), "print out help for a specific target")
|
||||
fs.DurationVar(&args.Timeout, "t", parseDuration("MAGEFILE_TIMEOUT"), "timeout in duration parsable format (e.g. 5m30s)")
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintf(os.Stdout, `
|
||||
%s [options] [target]
|
||||
|
||||
Commands:
|
||||
-l list targets in this binary
|
||||
-h show this help
|
||||
|
||||
Options:
|
||||
-h show description of a target
|
||||
-t <string>
|
||||
timeout in duration parsable format (e.g. 5m30s)
|
||||
-v show verbose output when running targets
|
||||
`[1:], filepath.Base(os.Args[0]))
|
||||
}
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
// flag will have printed out an error already.
|
||||
return
|
||||
}
|
||||
args.Args = fs.Args()
|
||||
if args.Help && len(args.Args) == 0 {
|
||||
fs.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
// color is ANSI color type
|
||||
type color int
|
||||
|
||||
// If you add/change/remove any items in this constant,
|
||||
// you will need to run "stringer -type=color" in this directory again.
|
||||
// NOTE: Please keep the list in an alphabetical order.
|
||||
const (
|
||||
black color = iota
|
||||
red
|
||||
green
|
||||
yellow
|
||||
blue
|
||||
magenta
|
||||
cyan
|
||||
white
|
||||
brightblack
|
||||
brightred
|
||||
brightgreen
|
||||
brightyellow
|
||||
brightblue
|
||||
brightmagenta
|
||||
brightcyan
|
||||
brightwhite
|
||||
)
|
||||
|
||||
// AnsiColor are ANSI color codes for supported terminal colors.
|
||||
var ansiColor = map[color]string{
|
||||
black: "\u001b[30m",
|
||||
red: "\u001b[31m",
|
||||
green: "\u001b[32m",
|
||||
yellow: "\u001b[33m",
|
||||
blue: "\u001b[34m",
|
||||
magenta: "\u001b[35m",
|
||||
cyan: "\u001b[36m",
|
||||
white: "\u001b[37m",
|
||||
brightblack: "\u001b[30;1m",
|
||||
brightred: "\u001b[31;1m",
|
||||
brightgreen: "\u001b[32;1m",
|
||||
brightyellow: "\u001b[33;1m",
|
||||
brightblue: "\u001b[34;1m",
|
||||
brightmagenta: "\u001b[35;1m",
|
||||
brightcyan: "\u001b[36;1m",
|
||||
brightwhite: "\u001b[37;1m",
|
||||
}
|
||||
|
||||
const _color_name = "blackredgreenyellowbluemagentacyanwhitebrightblackbrightredbrightgreenbrightyellowbrightbluebrightmagentabrightcyanbrightwhite"
|
||||
|
||||
var _color_index = [...]uint8{0, 5, 8, 13, 19, 23, 30, 34, 39, 50, 59, 70, 82, 92, 105, 115, 126}
|
||||
|
||||
colorToLowerString := func (i color) string {
|
||||
if i < 0 || i >= color(len(_color_index)-1) {
|
||||
return "color(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _color_name[_color_index[i]:_color_index[i+1]]
|
||||
}
|
||||
|
||||
// ansiColorReset is an ANSI color code to reset the terminal color.
|
||||
const ansiColorReset = "\033[0m"
|
||||
|
||||
// defaultTargetAnsiColor is a default ANSI color for colorizing targets.
|
||||
// It is set to Cyan as an arbitrary color, because it has a neutral meaning
|
||||
var defaultTargetAnsiColor = ansiColor[cyan]
|
||||
|
||||
getAnsiColor := func(color string) (string, bool) {
|
||||
colorLower := strings.ToLower(color)
|
||||
for k, v := range ansiColor {
|
||||
colorConstLower := colorToLowerString(k)
|
||||
if colorConstLower == colorLower {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Terminals which don't support color:
|
||||
// TERM=vt100
|
||||
// TERM=cygwin
|
||||
// TERM=xterm-mono
|
||||
var noColorTerms = map[string]bool{
|
||||
"vt100": false,
|
||||
"cygwin": false,
|
||||
"xterm-mono": false,
|
||||
}
|
||||
|
||||
// terminalSupportsColor checks if the current console supports color output
|
||||
//
|
||||
// Supported:
|
||||
// linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe, pwsh.exe
|
||||
// Not supported:
|
||||
// windows cmd.exe, powerShell.exe
|
||||
terminalSupportsColor := func() bool {
|
||||
envTerm := os.Getenv("TERM")
|
||||
if _, ok := noColorTerms[envTerm]; ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// enableColor reports whether the user has requested to enable a color output.
|
||||
enableColor := func() bool {
|
||||
b, _ := strconv.ParseBool(os.Getenv("MAGEFILE_ENABLE_COLOR"))
|
||||
return b
|
||||
}
|
||||
|
||||
// targetColor returns the ANSI color which should be used to colorize targets.
|
||||
targetColor := func() string {
|
||||
s, exists := os.LookupEnv("MAGEFILE_TARGET_COLOR")
|
||||
if exists == true {
|
||||
if c, ok := getAnsiColor(s); ok == true {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return defaultTargetAnsiColor
|
||||
}
|
||||
|
||||
// store the color terminal variables, so that the detection isn't repeated for each target
|
||||
var enableColorValue = enableColor() && terminalSupportsColor()
|
||||
var targetColorValue = targetColor()
|
||||
|
||||
printName := func(str string) string {
|
||||
if enableColorValue {
|
||||
return fmt.Sprintf("%s%s%s", targetColorValue, str, ansiColorReset)
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
list := func() error {
|
||||
|
||||
targets := map[string]string{
|
||||
"check": "Run tests and linters",
|
||||
"docker": "Build hugo Docker container",
|
||||
"fmt": "Run gofmt linter",
|
||||
"genDocsHelper": "Generate docs helper",
|
||||
"generate": "autogen packages",
|
||||
"hugo": "Build hugo binary",
|
||||
"hugoNoGitInfo": "Build hugo without git info",
|
||||
"hugoRace": "Build hugo binary with race detector enabled",
|
||||
"install": "hugo binary",
|
||||
"lint": "Run golint linter",
|
||||
"test": "Run tests",
|
||||
"test386": "Run tests in 32-bit mode Note that we don't run with the extended tag.",
|
||||
"testCoverHTML": "Generate test coverage report",
|
||||
"testRace": "Run tests with race detector",
|
||||
"uninstall": "hugo binary",
|
||||
"vet": "Run go vet linter",
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(targets))
|
||||
for name := range targets {
|
||||
keys = append(keys, name)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
fmt.Println("Targets:")
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 4, 4, ' ', 0)
|
||||
for _, name := range keys {
|
||||
fmt.Fprintf(w, " %v\t%v\n", printName(name), targets[name])
|
||||
}
|
||||
err := w.Flush()
|
||||
return err
|
||||
}
|
||||
|
||||
var ctx context.Context
|
||||
var ctxCancel func()
|
||||
|
||||
getContext := func() (context.Context, func()) {
|
||||
if ctx != nil {
|
||||
return ctx, ctxCancel
|
||||
}
|
||||
|
||||
if args.Timeout != 0 {
|
||||
ctx, ctxCancel = context.WithTimeout(context.Background(), args.Timeout)
|
||||
} else {
|
||||
ctx = context.Background()
|
||||
ctxCancel = func() {}
|
||||
}
|
||||
return ctx, ctxCancel
|
||||
}
|
||||
|
||||
runTarget := func(fn func(context.Context) error) interface{} {
|
||||
var err interface{}
|
||||
ctx, cancel := getContext()
|
||||
d := make(chan interface{})
|
||||
go func() {
|
||||
defer func() {
|
||||
err := recover()
|
||||
d <- err
|
||||
}()
|
||||
err := fn(ctx)
|
||||
d <- err
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
e := ctx.Err()
|
||||
fmt.Printf("ctx err: %v\n", e)
|
||||
return e
|
||||
case err = <-d:
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
}
|
||||
// This is necessary in case there aren't any targets, to avoid an unused
|
||||
// variable error.
|
||||
_ = runTarget
|
||||
|
||||
handleError := func(logger *log.Logger, err interface{}) {
|
||||
if err != nil {
|
||||
logger.Printf("Error: %+v\n", err)
|
||||
type code interface {
|
||||
ExitStatus() int
|
||||
}
|
||||
if c, ok := err.(code); ok {
|
||||
os.Exit(c.ExitStatus())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
_ = handleError
|
||||
|
||||
// Set MAGEFILE_VERBOSE so mg.Verbose() reflects the flag value.
|
||||
if args.Verbose {
|
||||
os.Setenv("MAGEFILE_VERBOSE", "1")
|
||||
} else {
|
||||
os.Setenv("MAGEFILE_VERBOSE", "0")
|
||||
}
|
||||
|
||||
log.SetFlags(0)
|
||||
if !args.Verbose {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
logger := log.New(os.Stderr, "", 0)
|
||||
if args.List {
|
||||
if err := list(); err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if args.Help {
|
||||
if len(args.Args) < 1 {
|
||||
logger.Println("no target specified")
|
||||
os.Exit(2)
|
||||
}
|
||||
switch strings.ToLower(args.Args[0]) {
|
||||
case "check":
|
||||
fmt.Println("Run tests and linters")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage check\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "docker":
|
||||
fmt.Println("Build hugo Docker container")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage docker\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "fmt":
|
||||
fmt.Println("Run gofmt linter")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage fmt\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "gendocshelper":
|
||||
fmt.Println("Generate docs helper")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage gendocshelper\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "generate":
|
||||
fmt.Println("Generate autogen packages")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage generate\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "hugo":
|
||||
fmt.Println("Build hugo binary")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage hugo\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "hugonogitinfo":
|
||||
fmt.Println("Build hugo without git info")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage hugonogitinfo\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "hugorace":
|
||||
fmt.Println("Build hugo binary with race detector enabled")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage hugorace\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "install":
|
||||
fmt.Println("Install hugo binary")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage install\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "lint":
|
||||
fmt.Println("Run golint linter")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage lint\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "test":
|
||||
fmt.Println("Run tests")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage test\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "test386":
|
||||
fmt.Println("Run tests in 32-bit mode Note that we don't run with the extended tag. Currently not supported in 32 bit.")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage test386\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "testcoverhtml":
|
||||
fmt.Println("Generate test coverage report")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage testcoverhtml\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "testrace":
|
||||
fmt.Println("Run tests with race detector")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage testrace\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "uninstall":
|
||||
fmt.Println("Uninstall hugo binary")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage uninstall\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
case "vet":
|
||||
fmt.Println("Run go vet linter")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Usage:\n\n\tmage vet\n\n")
|
||||
var aliases []string
|
||||
if len(aliases) > 0 {
|
||||
fmt.Printf("Aliases: %s\n\n", strings.Join(aliases, ", "))
|
||||
}
|
||||
return
|
||||
|
||||
default:
|
||||
logger.Printf("Unknown target: %q\n", args.Args[0])
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
if len(args.Args) < 1 {
|
||||
if err := list(); err != nil {
|
||||
logger.Println("Error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
for x := 0; x < len(args.Args); {
|
||||
target := args.Args[x]
|
||||
x++
|
||||
|
||||
// resolve aliases
|
||||
switch strings.ToLower(target) {
|
||||
|
||||
}
|
||||
|
||||
switch strings.ToLower(target) {
|
||||
|
||||
case "check":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Check\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Check")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
Check()
|
||||
return nil
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "docker":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Docker\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Docker")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Docker()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "fmt":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Fmt\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Fmt")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Fmt()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "gendocshelper":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"GenDocsHelper\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "GenDocsHelper")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return GenDocsHelper()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "generate":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Generate\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Generate")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Generate()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "hugo":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Hugo\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Hugo")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Hugo()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "hugonogitinfo":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"HugoNoGitInfo\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "HugoNoGitInfo")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return HugoNoGitInfo()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "hugorace":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"HugoRace\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "HugoRace")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return HugoRace()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "install":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Install\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Install")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Install()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "lint":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Lint\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Lint")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Lint()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "test":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Test\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Test")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Test()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "test386":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Test386\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Test386")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Test386()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "testcoverhtml":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"TestCoverHTML\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "TestCoverHTML")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return TestCoverHTML()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "testrace":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"TestRace\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "TestRace")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return TestRace()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "uninstall":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Uninstall\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Uninstall")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Uninstall()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
case "vet":
|
||||
expected := x + 0
|
||||
if expected > len(args.Args) {
|
||||
// note that expected and args at this point include the arg for the target itself
|
||||
// so we subtract 1 here to show the number of args without the target.
|
||||
logger.Printf("not enough arguments for target \"Vet\", expected %v, got %v\n", expected-1, len(args.Args)-1)
|
||||
os.Exit(2)
|
||||
}
|
||||
if args.Verbose {
|
||||
logger.Println("Running target:", "Vet")
|
||||
}
|
||||
|
||||
wrapFn := func(ctx context.Context) error {
|
||||
return Vet()
|
||||
}
|
||||
ret := runTarget(wrapFn)
|
||||
handleError(logger, ret)
|
||||
|
||||
default:
|
||||
logger.Printf("Unknown target specified: %q\n", target)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ func New(rs *resources.Spec) *Client {
|
|||
|
||||
// Copy copies r to the new targetPath.
|
||||
func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource, error) {
|
||||
key := dynacache.CleanKey(targetPath)
|
||||
key := dynacache.CleanKey(targetPath) + "__copy"
|
||||
return c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
|
||||
return resources.Copy(r, targetPath), nil
|
||||
})
|
||||
|
@ -66,7 +66,7 @@ func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource
|
|||
// Get creates a new Resource by opening the given pathname in the assets filesystem.
|
||||
func (c *Client) Get(pathname string) (resource.Resource, error) {
|
||||
pathname = path.Clean(pathname)
|
||||
key := dynacache.CleanKey(pathname)
|
||||
key := dynacache.CleanKey(pathname) + "__get"
|
||||
|
||||
return c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
|
||||
// The resource file will not be read before it gets used (e.g. in .Content),
|
||||
|
|
|
@ -227,3 +227,50 @@ eventDate: 2023-11-01T07:00:00-08:00
|
|||
|
||||
b.AssertFileContent("public/index.html", "2023-11|p9|p8|p7|2023-10|p6|p5|p4|2023-09|p3|p2|p1|")
|
||||
}
|
||||
|
||||
// Issue 10412
|
||||
func TestImageTransformThenCopy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- assets/pixel.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- layouts/index.html --
|
||||
{{- with resources.Get "pixel.png" }}
|
||||
{{- with .Resize "200x" | resources.Copy "pixel.png" }}
|
||||
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}">|{{ .Key }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/pixel.png", true)
|
||||
b.AssertFileContent("public/index.html",
|
||||
`<img src="/pixel.png" width="200" height="200">|/pixel.png`,
|
||||
)
|
||||
}
|
||||
|
||||
// Issue 12310
|
||||
func TestUseDifferentCacheKeyForResourceCopy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
-- assets/a.txt --
|
||||
This was assets/a.txt
|
||||
-- layouts/index.html --
|
||||
{{ $nilResource := resources.Get "/p1/b.txt" }}
|
||||
{{ $r := resources.Get "a.txt" }}
|
||||
{{ $r = resources.Copy "/p1/b.txt" $r }}
|
||||
{{ $r.RelPermalink }}
|
||||
`
|
||||
|
||||
b, err := hugolib.TestE(t, files)
|
||||
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.AssertFileContent("public/p1/b.txt", "This was assets/a.txt")
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
package debug
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/sanity-io/litter"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/yuin/goldmark/util"
|
||||
|
||||
|
@ -108,7 +108,11 @@ type Namespace struct {
|
|||
// Also note that the output from Dump may change from Hugo version to the next,
|
||||
// so don't depend on a specific output.
|
||||
func (ns *Namespace) Dump(val any) string {
|
||||
return litter.Sdump(val)
|
||||
b, err := json.MarshalIndent(val, "", " ")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// VisualizeSpaces returns a string with spaces replaced by a visible string.
|
||||
|
|
|
@ -43,3 +43,33 @@ disableKinds = ["taxonomy", "term"]
|
|||
|
||||
b.AssertLogContains("timer: name foo count 5 duration")
|
||||
}
|
||||
|
||||
func TestDebugDumpPage(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableLiveReload = true
|
||||
[taxonomies]
|
||||
tag = "tags"
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "The Index"
|
||||
date: 2012-03-15
|
||||
---
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "The First"
|
||||
tags: ["a", "b"]
|
||||
---
|
||||
-- layouts/_default/list.html --
|
||||
Dump: {{ debug.Dump . | safeHTML }}
|
||||
Dump Site: {{ debug.Dump site }}
|
||||
Dum site.Taxonomies: {{ debug.Dump site.Taxonomies | safeHTML }}
|
||||
-- layouts/_default/single.html --
|
||||
Dump: {{ debug.Dump . | safeHTML }}
|
||||
|
||||
|
||||
`
|
||||
b := hugolib.TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "Dump: {\n \"Date\": \"2012-03-15T00:00:00Z\"")
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func init() {
|
|||
[][2]string{
|
||||
{`{{ $m := newScratch }}
|
||||
{{ $m.Set "Hugo" "Rocks!" }}
|
||||
{{ $m.Values | debug.Dump | safeHTML }}`, "map[string]interface {}{\n \"Hugo\": \"Rocks!\",\n}"},
|
||||
{{ $m.Values | debug.Dump | safeHTML }}`, "{\n \"Hugo\": \"Rocks!\"\n}"},
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -1,51 +1,22 @@
|
|||
{{- $pc := .Site.Config.Privacy.GoogleAnalytics -}}
|
||||
{{- if not $pc.Disable }}{{ with .Site.Config.Services.GoogleAnalytics.ID -}}
|
||||
{{ if hasPrefix . "G-"}}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ . }}"></script>
|
||||
<script>
|
||||
{{ template "__ga_js_set_doNotTrack" $ }}
|
||||
if (!doNotTrack) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '{{ . }}', { 'anonymize_ip': {{- $pc.AnonymizeIP -}} });
|
||||
}
|
||||
</script>
|
||||
{{ else if hasPrefix . "UA-" }}
|
||||
<script>
|
||||
{{ template "__ga_js_set_doNotTrack" $ }}
|
||||
if (!doNotTrack) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
{{- if $pc.UseSessionStorage }}
|
||||
if (window.sessionStorage) {
|
||||
var GA_SESSION_STORAGE_KEY = 'ga:clientId';
|
||||
ga('create', '{{ . }}', {
|
||||
'storage': 'none',
|
||||
'clientId': sessionStorage.getItem(GA_SESSION_STORAGE_KEY)
|
||||
});
|
||||
ga(function(tracker) {
|
||||
sessionStorage.setItem(GA_SESSION_STORAGE_KEY, tracker.get('clientId'));
|
||||
});
|
||||
}
|
||||
{{ else }}
|
||||
ga('create', '{{ . }}', 'auto');
|
||||
{{ end -}}
|
||||
{{ if $pc.AnonymizeIP }}ga('set', 'anonymizeIp', true);{{ end }}
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
{{- end -}}
|
||||
{{- end }}{{ end -}}
|
||||
|
||||
{{- define "__ga_js_set_doNotTrack" -}}{{/* This is also used in the async version. */}}
|
||||
{{- $pc := .Site.Config.Privacy.GoogleAnalytics -}}
|
||||
{{- if not $pc.RespectDoNotTrack -}}
|
||||
var doNotTrack = false;
|
||||
{{- else -}}
|
||||
var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);
|
||||
var doNotTrack = (dnt == "1" || dnt == "yes");
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ if not site.Config.Privacy.GoogleAnalytics.Disable }}
|
||||
{{ with site.Config.Services.GoogleAnalytics.ID }}
|
||||
{{ if strings.HasPrefix (lower .) "ua-" }}
|
||||
{{ warnf "Google Analytics 4 (GA4) replaced Google Universal Analytics (UA) effective 1 July 2023. See https://support.google.com/analytics/answer/11583528. Create a GA4 property and data stream, then replace the Google Analytics ID in your site configuration with the new value." }}
|
||||
{{ else }}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ . }}"></script>
|
||||
<script>
|
||||
var doNotTrack = false;
|
||||
if ({{ site.Config.Privacy.GoogleAnalytics.RespectDoNotTrack }}) {
|
||||
var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);
|
||||
var doNotTrack = (dnt == "1" || dnt == "yes");
|
||||
}
|
||||
if (!doNotTrack) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '{{ . }}');
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
{{ warnf "_internal/google_analytics_async.html is no longer supported by Google and will be removed in a future version of Hugo" }}
|
||||
{{- $pc := .Site.Config.Privacy.GoogleAnalytics -}}
|
||||
{{- if not $pc.Disable -}}
|
||||
{{ with .Site.Config.Services.GoogleAnalytics.ID }}
|
||||
<script>
|
||||
{{ template "__ga_js_set_doNotTrack" $ }}
|
||||
if (!doNotTrack) {
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
{{- if $pc.UseSessionStorage }}
|
||||
if (window.sessionStorage) {
|
||||
var GA_SESSION_STORAGE_KEY = 'ga:clientId';
|
||||
ga('create', '{{ . }}', {
|
||||
'storage': 'none',
|
||||
'clientId': sessionStorage.getItem(GA_SESSION_STORAGE_KEY)
|
||||
});
|
||||
ga(function(tracker) {
|
||||
sessionStorage.setItem(GA_SESSION_STORAGE_KEY, tracker.get('clientId'));
|
||||
});
|
||||
}
|
||||
{{ else }}
|
||||
ga('create', '{{ . }}', 'auto');
|
||||
{{ end -}}
|
||||
{{ if $pc.AnonymizeIP }}ga('set', 'anonymizeIp', true);{{ end }}
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
{{ end }}
|
||||
{{- end -}}
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
{{- with .Params.audio }}<meta property="og:audio" content="{{ . }}" />{{ end }}
|
||||
{{- with .Params.locale }}<meta property="og:locale" content="{{ . }}" />{{ end }}
|
||||
{{- with .Site.Params.title }}<meta property="og:site_name" content="{{ . }}" />{{ end }}
|
||||
{{- with .Site.Params.title }}<meta property="og:site_name" content="{{ . }}" />
|
||||
{{- else if .Site.Title }}<meta property="og:site_name" content="{{ .Site.Title }}" />{{ end }}
|
||||
{{- with .Params.videos }}{{- range . }}
|
||||
<meta property="og:video" content="{{ . | absURL }}" />
|
||||
{{ end }}{{ end }}
|
||||
|
|
|
@ -1,17 +1,56 @@
|
|||
<meta itemprop="name" content="{{ .Title }}">
|
||||
<meta itemprop="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}">
|
||||
{{- with or .Title site.Title }}
|
||||
<meta itemprop="name" content="{{ . }}">
|
||||
{{- end }}
|
||||
|
||||
{{- if .IsPage -}}
|
||||
{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}}
|
||||
{{ with .PublishDate }}<meta itemprop="datePublished" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end}}
|
||||
{{ with .Lastmod }}<meta itemprop="dateModified" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end}}
|
||||
<meta itemprop="wordCount" content="{{ .WordCount }}">
|
||||
{{- with or .Description .Summary site.Params.Description }}
|
||||
<meta itemprop="description" content="{{ . }}">
|
||||
{{- end }}
|
||||
|
||||
{{- $images := partial "_funcs/get-page-images" . -}}
|
||||
{{- range first 6 $images -}}
|
||||
<meta itemprop="image" content="{{ .Permalink }}" />
|
||||
{{- end -}}
|
||||
|
||||
<!-- Output all taxonomies as schema.org keywords -->
|
||||
<meta itemprop="keywords" content="{{ if .IsPage}}{{ range $index, $tag := .Params.tags }}{{ $tag }},{{ end }}{{ else }}{{ range $plural, $terms := .Site.Taxonomies }}{{ range $term, $val := $terms }}{{ printf "%s," $term }}{{ end }}{{ end }}{{ end }}" />
|
||||
{{- $ISO8601 := "2006-01-02T15:04:05-07:00" }}
|
||||
{{- with .PublishDate }}
|
||||
<meta itemprop="datePublished" {{ .Format $ISO8601 | printf "content=%q" | safeHTMLAttr }}>
|
||||
{{- end }}
|
||||
|
||||
{{- with .Lastmod }}
|
||||
<meta itemprop="dateModified" {{ .Format $ISO8601 | printf "content=%q" | safeHTMLAttr }}>
|
||||
{{- end }}
|
||||
|
||||
{{- with .WordCount }}
|
||||
<meta itemprop="wordCount" content="{{ . }}">
|
||||
{{- end }}
|
||||
|
||||
{{- $images := partial "_funcs/get-page-images" . }}
|
||||
{{- range first 6 $images }}
|
||||
<meta itemprop="image" content="{{ .Permalink }}">
|
||||
{{- end }}
|
||||
|
||||
{{- /*
|
||||
Keywords precedence:
|
||||
|
||||
1. Use "keywords" term page titles.
|
||||
2. Use "keywords" from front matter if "keywords" is not a taxonomy.
|
||||
3. Use "tags" term page titles.
|
||||
4. Use term page titles from all taxonomies.
|
||||
|
||||
*/}}
|
||||
{{- $keywords := slice }}
|
||||
{{- range .GetTerms "keywords" }}
|
||||
{{- $keywords = $keywords | append .Title }}
|
||||
{{- else }}
|
||||
{{- with .Keywords }}
|
||||
{{- $keywords = . }}
|
||||
{{- else }}
|
||||
{{- range .GetTerms "tags" }}
|
||||
{{- $keywords = $keywords | append .Title }}
|
||||
{{- else }}
|
||||
{{- range $taxonomy, $_ := site.Taxonomies }}
|
||||
{{- range $.GetTerms $taxonomy }}
|
||||
{{- $keywords = $keywords | append .Title }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with $keywords }}
|
||||
<meta itemprop="keywords" content="{{ delimit . `,` }}">
|
||||
{{- end -}}
|
||||
|
|
|
@ -210,3 +210,47 @@ var a = §§{{.Title }}§§;
|
|||
// This used to fail, but not in >= Hugo 0.121.0.
|
||||
b.Assert(err, qt.IsNil)
|
||||
}
|
||||
|
||||
func TestGoogleAnalyticsTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
[privacy.googleAnalytics]
|
||||
disable = false
|
||||
respectDoNotTrack = true
|
||||
[services.googleAnalytics]
|
||||
id = 'G-0123456789'
|
||||
-- layouts/index.html --
|
||||
{{ template "_internal/google_analytics.html" . }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
`<script async src="https://www.googletagmanager.com/gtag/js?id=G-0123456789"></script>`,
|
||||
`var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);`,
|
||||
)
|
||||
}
|
||||
|
||||
func TestDisqusTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
[services.disqus]
|
||||
shortname = 'foo'
|
||||
[privacy.disqus]
|
||||
disable = false
|
||||
-- layouts/index.html --
|
||||
{{ template "_internal/disqus.html" . }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
`s.src = '//' + "foo" + '.disqus.com/embed.js';`,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue