From: Bjørn Erik Pedersen Date: Sat, 26 Mar 2022 09:04:57 +0000 (+0200) Subject: Merge commit 'd276e901b36d2576ef8350ed96b17f66254eac1b' X-Git-Tag: v0.96.0~4 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=d7497b28c17fe2f402accde45c75642ed7f09d08;p=brevno-suite%2Fhugo Merge commit 'd276e901b36d2576ef8350ed96b17f66254eac1b' --- d7497b28c17fe2f402accde45c75642ed7f09d08 diff --cc docs/.cspell.json index 70811e51,00000000..2cac73c0 mode 100644,000000..100644 --- a/docs/.cspell.json +++ b/docs/.cspell.json @@@ -1,155 -1,0 +1,276 @@@ +{ + "version": "0.2", + "words": [ ++ "aaabaab", + "aabb", + "aabba", + "aabbaa", + "aabbaabb", + "abourget", ++ "absurl", + "adoc", + "algolia", ++ "allowfullscreen", ++ "ananke", + "anchorize", + "anthonyfok", + "asciidoctor", + "attrlink", ++ "azblob", ++ "baseof", ++ "beevelop", ++ "bibtex", + "Bjørn", + "blackfriday", + "blogue", + "bogem", ++ "Bootcamp", ++ "brlink", + "Brotli", ++ "Browsersync", + "canonify", ++ "Catmull", + "Catwoman", + "Cheatsheet", ++ "choco", + "chromastyles", + "clockoon", + "Cloudinary", + "CNAME", + "Codecademy's", + "CODEOWNERS", ++ "Coen", + "Commento", + "Cond", + "Contentful", ++ "copyrighthtml", ++ "corejs", + "countrunes", + "countwords", + "crossreferences", ++ "datatable", ++ "DATOCMS", ++ "debugconfig", ++ "DELIM", ++ "dhersam", + "digitalcraftsman", + "Disqus", ++ "Dmdh", ++ "doas", + "dokuwiki", ++ "dpkg", + "DRING", + "Emojify", + "Enwrite", ++ "eopkg", + "eparis", + "errorf", ++ "erroridf", ++ "Evernote", ++ "exitwp", ++ "Feminella", + "firstpost", ++ "Formspree", ++ "fpath", + "Francia", + "freenode", ++ "frontmatter", + "funcs", + "funcsig", + "Garen", ++ "gcloud", + "Getenv", ++ "getjson", ++ "getpage", ++ "Gmfc", ++ "Goel", + "Gohugo", + "gohugoio", + "goldenbridge", + "Goldmark", + "gomodules", + "GOPATH", + "govendor", + "Gowans", + "Grayscale", + "Gruber", + "gtag", ++ "hidecaption", + "Hokus", ++ "hola", ++ "hügó", ++ "hugodeps", + "hugodoc", ++ "Hugofy", + "hugolang", + "hugoversion", ++ "Hyas", + "Hyvor", + "iframes", ++ "ifttt", ++ "iife", + "imgproc", ++ "importr", ++ "IMWQ", + "indice", ++ "innershortcode", + "Intelli", + "interdoc", + "IPTC", ++ "ismenucurrent", + "Isset", + "Isso", ++ "Jaco", ++ "johnpatitucci", + "Joomla", ++ "JRBR", + "jsonify", + "katex", ++ "keycdn", ++ "KEYVALS", + "kubernetes", ++ "Lanczos", ++ "langformatnumber", + "lastmod", ++ "libwebp", + "linktitle", ++ "Lipi", ++ "lrwxr", ++ "maingo", + "markdownified", + "markdownify", + "mathjax", ++ "mdhender", ++ "mdshortcode", + "mercredi", + "Mittwoch", ++ "mkdir", + "mmark", + "monokai", + "Morling", ++ "mspowerpoint", ++ "Multihost", + "Muut", ++ "myclass", ++ "mydeployment", ++ "myindex", ++ "mylayout", ++ "mypage", + "mypartials", + "mypost", ++ "mysite", ++ "myspa", ++ "mystyle", ++ "mytheme", ++ "NDJSON", + "needsexample", ++ "Netravali", ++ "newparam", ++ "Nikhil", ++ "Njjy", ++ "nlist", + "nobr", + "nocopy", + "Norsk", ++ "nosniff", ++ "NOSQL", ++ "notoc", + "novembre", ++ "NUMWORKERMULTIPLIER", ++ "Obhu", ++ "octohug", + "Octopress", ++ "oldparam", ++ "onrender", + "opengraph", + "OWASP", + "Pandoc", ++ "partialcached", ++ "Pastorius", ++ "Patitucci", ++ "PCRE", + "peaceiris", + "Pedersen", ++ "permalinkable", + "plainify", ++ "POSIX", ++ "postprocess", + "println", + "publishdate", + "Pygments", + "querify", ++ "QVOMC", ++ "rdwatters", + "readfile", ++ "rebinded", + "REDIR", + "reftext", + "relatedfuncs", + "relref", ++ "relurl", + "remarkjs", + "rgba", + "rlimit", ++ "roboto", ++ "rssxml", ++ "rwxrwxrwx", ++ "safehtml", + "safejs", + "Samsa", ++ "Shekhar", + "Shortcode", + "Shortcodes", + "Sindre", ++ "sitemapindex", ++ "sitemapxml", + "Smartcrop", ++ "Sprintf", ++ "Startseite", ++ "strconv", ++ "stringifier", + "struct", ++ "structs", ++ "subdir", + "Talkyard", + "taxo", + "tbody", + "tdewolff", + "testshortcodes", + "thead", + "Thinkful", ++ "TLDR", + "TMPDIR", + "tojson", + "Torikian", + "totoml", + "toyaml", ++ "twitteruser", ++ "Unmarshal", + "Unmarshal", + "urlize", ++ "urlset", + "vimrc", + "wanghc", + "Wappalyzer", + "warnf", + "webp", ++ "Wercker", + "wibble", ++ "wordcount", + "workson", ++ "xvzf", ++ "yoyoyo", ++ "Zgotmpl", + "zzbbaabb" + ], + "language": "en,en-GB,en-US,de,fr", + "files": [ + "**/*.md" + ], + "ignorePaths": [ + ".cspell.json", + "**/node_modules/**", + "*.min.*" + ], + "useGitignore": true +} diff --cc docs/content/en/about/security-model/index.md index c7e9d35c,00000000..461c7fe7 mode 100644,000000..100644 --- a/docs/content/en/about/security-model/index.md +++ b/docs/content/en/about/security-model/index.md @@@ -1,69 -1,0 +1,69 @@@ +--- +title: Hugo's Security Model +description: A summary of Hugo's security model. +date: 2019-10-01 +layout: single +keywords: ["Security", "Privacy"] +menu: + docs: + parent: "about" + weight: 4 +weight: 5 +sections_weight: 5 +draft: false +aliases: [/security/] +toc: true +--- + +## Runtime Security + +Hugo produces static output, so once built, the runtime is the browser (assuming the output is HTML) and any server (API) that you integrate with. + +But when developing and building your site, the runtime is the `hugo` executable. Securing a runtime can be [a real challenge](https://blog.logrocket.com/how-to-protect-your-node-js-applications-from-malicious-dependencies-5f2e60ea08f9/). + +**Hugo's main approach is that of sandboxing and a security policy with strict defaults:** + +* Hugo has a virtual file system and only the main project (not third-party components) is allowed to mount directories or files outside the project root. +* Only the main project can walk symbolic links. +* User-defined components have read-only access to the filesystem. +* We shell out to some external binaries to support [Asciidoctor](/content-management/formats/#list-of-content-formats) and similar, but those binaries and their flags are predefined and disabled by default (see [Security Policy](#security-policy)). General functions to run arbitrary external OS commands have been [discussed](https://github.com/gohugoio/hugo/issues/796), but not implemented because of security concerns. + + +## Security Policy + +{{< new-in "0.91.0" >}} + +Hugo has a built-in security policy that restricts access to [os/exec](https://pkg.go.dev/os/exec), remote communication and similar. + +The default configuration is listed below. Any build using features not in the allow list of the security policy will fail with a detailed message about what needs to be done. Most of these settings are allow lists (string or slice, [Regular Expressions](https://pkg.go.dev/regexp) or `none` which matches nothing). + +{{< code-toggle config="security" />}} + +Note that these and other config settings in Hugo can be overridden by the OS environment. If you want to block all remote HTTP fetching of data: + +``` - HUGO_SECURITY_HTTP_URLS=none hugo ++HUGO_SECURITY_HTTP_URLS=none hugo +``` + +## Dependency Security + +Hugo is built as a static binary using [Go Modules](https://github.com/golang/go/wiki/Modules) to manage its dependencies. Go Modules have several safeguards, one of them being the `go.sum` file. This is a database of the expected cryptographic checksums of all of your dependencies, including transitive dependencies. + +[Hugo Modules](/hugo-modules/) is a feature built on top of the functionality of Go Modules. Like Go Modules, a Hugo project using Hugo Modules will have a `go.sum` file. We recommend that you commit this file to your version control system. The Hugo build will fail if there is a checksum mismatch, which would be an indication of [dependency tampering](https://julienrenaux.fr/2019/12/20/github-actions-security-risk/). + +## Web Application Security + +These are the security threats as defined by [OWASP](https://en.wikipedia.org/wiki/OWASP). + +For HTML output, this is the core security model: + +https://golang.org/pkg/html/template/#hdr-Security_Model + +In short: + +Templates authors (you) are trusted, but the data you send in is not. +This is why you sometimes need to use the _safe_ functions, such as `safeHTML`, to avoid escaping of data you know is safe. +There is one exception to the above, as noted in the documentation: If you enable inline shortcodes, you also say that the shortcodes and data handling in content files are trusted, as those macros are treated as pure text. +It may be worth adding that Hugo is a static site generator with no concept of dynamic user input. + +For content, the default Markdown renderer is [configured](/getting-started/configuration-markup) to remove or escape potentially unsafe content. This behavior can be reconfigured if you trust your content. diff --cc docs/content/en/content-management/diagrams.md index cfc0b364,00000000..243a70fd mode 100644,000000..100644 --- a/docs/content/en/content-management/diagrams.md +++ b/docs/content/en/content-management/diagrams.md @@@ -1,277 -1,0 +1,277 @@@ +--- +title: Diagrams +date: 2022-02-20 +categories: [content management] +keywords: [diagrams,drawing] +menu: + docs: + parent: "content-management" + weight: 22 +weight: 22 +toc: true +--- + + +{{< new-in "0.93.0" >}} + + +## GoAT Diagrams (Ascii) + - Hugo supports [GoAT](https://github.com/bep/goat) natively. This means that this code block: ++Hugo! supports [GoAT](https://github.com/bep/goat) natively. This means that this code block: + +```` +```goat + . . . .--- 1 .-- 1 / 1 + / \ | | .---+ .-+ + + / \ .---+---. .--+--. | '--- 2 | '-- 2 / \ 2 + + + | | | | ---+ ---+ + + / \ / \ .-+-. .-+-. .+. .+. | .--- 3 | .-- 3 \ / 3 + / \ / \ | | | | | | | | '---+ '-+ + + 1 2 3 4 1 2 3 4 1 2 3 4 '--- 4 '-- 4 \ 4 + +``` +```` + +Will be rendered as: + +```goat + + . . . .--- 1 .-- 1 / 1 + / \ | | .---+ .-+ + + / \ .---+---. .--+--. | '--- 2 | '-- 2 / \ 2 + + + | | | | ---+ ---+ + + / \ / \ .-+-. .-+-. .+. .+. | .--- 3 | .-- 3 \ / 3 + / \ / \ | | | | | | | | '---+ '-+ + + 1 2 3 4 1 2 3 4 1 2 3 4 '--- 4 '-- 4 \ 4 +``` + + + + + +## Mermaid Diagrams + +Hugo currently does not provide default templates for Mermaid diagrams. But you can easily add your own. One way to do it would be to create ` layouts/_default/_markup/render-codeblock-mermaid.html`: + + +```go-html-template +
+ {{- .Inner | safeHTML }} +
+{{ .Page.Store.Set "hasMermaid" true }} +``` + +And then include this snippet at the bottom of the content template (below `.Content`): + +```go-html-template +{{ if .Page.Store.Get "hasMermaid" }} + + +{{ end }} +``` + +With that you can use the `mermaid` language in Markdown code blocks: + +```mermaid +sequenceDiagram + participant Alice + participant Bob + Alice->>John: Hello John, how are you? + loop Healthcheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts
prevail! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! +``` + + + +## Goat Ascii Diagram Examples + +### Graphics + +```goat + . + 0 3 P * Eye / ^ / + *-------* +y \ +) \ / Reflection + 1 /| 2 /| ^ \ \ \ v + *-------* | | v0 \ v3 --------*-------- + | |4 | |7 | *----\-----* + | *-----|-* +-----> +x / v X \ .-.<-------- o + |/ |/ / / o \ | / | Refraction / \ + *-------* v / \ +-' / \ + 5 6 +z v1 *------------------* v2 | o-----o + v + +``` + +### Complex + +```goat ++-------------------+ ^ .---. +| A Box |__.--.__ __.--> | .-. | | +| | '--' v | * |<--- | | ++-------------------+ '-' | | + Round *---(-. | + .-----------------. .-------. .----------. .-------. | | | + | Mixed Rounded | | | / Diagonals \ | | | | | | + | & Square Corners | '--. .--' / \ |---+---| '-)-' .--------. + '--+------------+-' .--. | '-------+--------' | | | | / Search / + | | | | '---. | '-------' | '-+------' + |<---------->| | | | v Interior | ^ + ' <---' '----' .-----------. ---. .--- v | + .------------------. Diag line | .-------. +---. \ / . | + | if (a > b) +---. .--->| | | | | Curved line \ / / \ | + | obj->fcn() | \ / | '-------' |<--' + / \ | + '------------------' '--' '--+--------' .--. .--. | .-. +Done?+-' + .---+-----. | ^ |\ | | /| .--+ | | \ / + | | | Join \|/ | | Curved | \| |/ | | \ | \ / + | | +----> o --o-- '-' Vertical '--' '--' '-- '--' + .---. + <--+---+-----' | /|\ | | 3 | + v not:line 'quotes' .-' '---' + .-. .---+--------. / A || B *bold* | ^ + | | | Not a dot | <---+---<-- A dash--is not a line v | + '-' '---------+--' / Nor/is this. --- + +``` + +### Process + +```goat + . + .---------. / \ + | START | / \ .-+-------+-. ___________ + '----+----' .-------. A / \ B | |COMPLEX| | / \ .-. + | | END |<-----+CHOICE +----->| | | +--->+ PREPARATION +--->| X | + v '-------' \ / | |PROCESS| | \___________/ '-' + .---------. \ / '-+---+---+-' + / INPUT / \ / + '-----+---' ' + | ^ + v | + .-----------. .-----+-----. .-. + | PROCESS +---------------->| PROCESS |<------+ X | + '-----------' '-----------' '-' +``` + +### File tree + +Created from https://arthursonzogni.com/Diagon/#Tree + +```goat { width=300 color="orange" } +───Linux─┬─Android + ├─Debian─┬─Ubuntu─┬─Lubuntu + │ │ ├─Kubuntu + │ │ ├─Xubuntu + │ │ └─Xubuntu + │ └─Mint + ├─Centos + └─Fedora +``` + + +### Sequence Diagram + +https://arthursonzogni.com/Diagon/#Sequence + +```goat { class="w-40" } +┌─────┐ ┌───┐ +│Alice│ │Bob│ +└──┬──┘ └─┬─┘ + │ │ + │ Hello Bob! │ + │───────────>│ + │ │ + │Hello Alice!│ + │<───────────│ +┌──┴──┐ ┌─┴─┐ +│Alice│ │Bob│ +└─────┘ └───┘ + +``` + + +### Flowchart + +https://arthursonzogni.com/Diagon/#Flowchart + +```goat + _________________ + ╱ ╲ ┌─────┐ + ╱ DO YOU UNDERSTAND ╲____________________________________________________│GOOD!│ + ╲ FLOW CHARTS? ╱yes └──┬──┘ + ╲_________________╱ │ + │no │ + _________▽_________ ______________________ │ + ╱ ╲ ╱ ╲ ┌────┐ │ +╱ OKAY, YOU SEE THE ╲________________╱ ... AND YOU CAN SEE ╲___│GOOD│ │ +╲ LINE LABELED 'YES'? ╱yes ╲ THE ONES LABELED 'NO'? ╱yes└──┬─┘ │ + ╲___________________╱ ╲______________________╱ │ │ + │no │no │ │ + ________▽_________ _________▽__________ │ │ + ╱ ╲ ┌───────────┐ ╱ ╲ │ │ + ╱ BUT YOU SEE THE ╲___│WAIT, WHAT?│ ╱ BUT YOU JUST ╲___ │ │ + ╲ ONES LABELED 'NO'? ╱yes└───────────┘ ╲ FOLLOWED THEM TWICE? ╱yes│ │ │ + ╲__________________╱ ╲____________________╱ │ │ │ + │no │no │ │ │ + ┌───▽───┐ │ │ │ │ + │LISTEN.│ └───────┬───────┘ │ │ + └───┬───┘ ┌──────▽─────┐ │ │ + ┌─────▽────┐ │(THAT WASN'T│ │ │ + │I HATE YOU│ │A QUESTION) │ │ │ + └──────────┘ └──────┬─────┘ │ │ + ┌────▽───┐ │ │ + │SCREW IT│ │ │ + └────┬───┘ │ │ + └─────┬─────┘ │ + │ │ + └─────┬─────┘ + ┌───────▽──────┐ + │LET'S GO DRING│ + └───────┬──────┘ + ┌─────────▽─────────┐ + │HEY, I SHOULD TRY │ + │INSTALLING FREEBSD!│ + └───────────────────┘ + +``` + + +### Table + +https://arthursonzogni.com/Diagon/#Table + +```goat { class="w-80 dark-blue" } +┌────────────────────────────────────────────────┐ +│ │ +├────────────────────────────────────────────────┤ +│SYNTAX = { PRODUCTION } . │ +├────────────────────────────────────────────────┤ +│PRODUCTION = IDENTIFIER "=" EXPRESSION "." . │ +├────────────────────────────────────────────────┤ +│EXPRESSION = TERM { "|" TERM } . │ +├────────────────────────────────────────────────┤ +│TERM = FACTOR { FACTOR } . │ +├────────────────────────────────────────────────┤ +│FACTOR = IDENTIFIER │ +├────────────────────────────────────────────────┤ +│ | LITERAL │ +├────────────────────────────────────────────────┤ +│ | "[" EXPRESSION "]" │ +├────────────────────────────────────────────────┤ +│ | "(" EXPRESSION ")" │ +├────────────────────────────────────────────────┤ +│ | "{" EXPRESSION "}" . │ +├────────────────────────────────────────────────┤ +│IDENTIFIER = letter { letter } . │ +├────────────────────────────────────────────────┤ +│LITERAL = """" character { character } """" .│ +└────────────────────────────────────────────────┘ +``` + + + diff --cc docs/content/en/content-management/formats.md index 1ae20ba5,00000000..3c3edfdd mode 100644,000000..100644 --- a/docs/content/en/content-management/formats.md +++ b/docs/content/en/content-management/formats.md @@@ -1,157 -1,0 +1,154 @@@ +--- +title: Content Formats +linktitle: Content Formats +description: Both HTML and Markdown are supported content formats. +date: 2017-01-10 +publishdate: 2017-01-10 - lastmod: 2017-04-06 +categories: [content management] +keywords: [markdown,asciidoc,mmark,pandoc,content format] +menu: + docs: + parent: "content-management" + weight: 20 +weight: 20 #rem +draft: false +aliases: [/content/markdown-extras/,/content/supported-formats/,/doc/supported-formats/] +toc: true +--- + +You can put any file type into your `/content` directories, but Hugo uses the `markup` front matter value if set or the file extension (see `Markup identifiers` in the table below) to determine if the markup needs to be processed, e.g.: + +* Markdown converted to HTML +* [Shortcodes](/content-management/shortcodes/) processed +* Layout applied + +## List of content formats + +The current list of content formats in Hugo: + - | Name | Markup identifiers | Comment | ++| Name | Markup identifiers | Comment | +| ------------- | ------------- |-------------| +| Goldmark | md, markdown, goldmark |Note that you can set the default handler of `md` and `markdown` to something else, see [Configure Markup](/getting-started/configuration-markup/).{{< new-in "0.60.0" >}} | +| Blackfriday | blackfriday |Blackfriday will eventually be deprecated.| +|MMark|mmark|Mmark is deprecated and will be removed in a future release.| +|Emacs Org-Mode|org|See [go-org](https://github.com/niklasfasching/go-org).| +|AsciiDoc|asciidocext, adoc, ad|Needs [Asciidoctor][ascii] installed.| - |RST|rst|Needs [RST](http://docutils.sourceforge.net/rst.html) installed.| ++|RST|rst|Needs [RST](https://docutils.sourceforge.net/rst.html) installed.| +|Pandoc|pandoc, pdc|Needs [Pandoc](https://www.pandoc.org/) installed.| +|HTML|html, htm|To be treated as a content file, with layout, shortcodes etc., it must have front matter. If not, it will be copied as-is.| + +The `markup identifier` is fetched from either the `markup` variable in front matter or from the file extension. For markup-related configuration, see [Configure Markup](/getting-started/configuration-markup/). + - +## External Helpers + +Some of the formats in the table above need external helpers installed on your PC. For example, for AsciiDoc files, +Hugo will try to call the `asciidoctor` command. This means that you will have to install the associated +tool on your machine to be able to use these formats. + +Hugo passes reasonable default arguments to these external helpers by default: + +- `asciidoctor`: `--no-header-footer -` +- `rst2html`: `--leave-comments --initial-header-level=2` +- `pandoc`: `--mathjax` + +{{% warning "Performance of External Helpers" %}} +Because additional formats are external commands, generation performance will rely heavily on the performance of the external tool you are using. As this feature is still in its infancy, feedback is welcome. +{{% /warning %}} + +### External Helper AsciiDoc + +[AsciiDoc](https://github.com/asciidoc/asciidoc) implementation EOLs in Jan 2020 and is no longer supported. +AsciiDoc development is being continued under [Asciidoctor](https://github.com/asciidoctor). The format AsciiDoc +remains of course. Please continue with the implementation Asciidoctor. + +### External Helper Asciidoctor + +The Asciidoctor community offers a wide set of tools for the AsciiDoc format that can be installed additionally to Hugo. +[See the Asciidoctor docs for installation instructions](https://asciidoctor.org/docs/install-toolchain/). Make sure that also all +optional extensions like `asciidoctor-diagram` or `asciidoctor-html5s` are installed if required. + +{{% note %}} +External `asciidoctor` command requires Hugo rendering to _disk_ to a specific destination directory. It is required to run Hugo with the command option `--destination`. +{{% /note %}} + +Some [Asciidoctor](https://asciidoctor.org/man/asciidoctor/) parameters can be customized in Hugo: + +Parameter | Comment +--- | --- +backend | Don't change this unless you know what you are doing. +doctype | Currently, the only document type supported in Hugo is `article`. +extensions | Possible extensions are `asciidoctor-html5s`, `asciidoctor-bibtex`, `asciidoctor-diagram`, `asciidoctor-interdoc-reftext`, `asciidoctor-katex`, `asciidoctor-latex`, `asciidoctor-mathematical`, `asciidoctor-question`, `asciidoctor-rouge`. +attributes | Variables to be referenced in your AsciiDoc file. This is a list of variable name/value maps. See [Asciidoctor's attributes](https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#attributes-and-substitutions). +noHeaderOrFooter | Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. Don't change this unless you know what you are doing. +safeMode | Safe mode level `unsafe`, `safe`, `server` or `secure`. Don't change this unless you know what you are doing. +sectionNumbers | Auto-number section titles. +verbose | Verbosely print processing information and configuration file checks to stderr. +trace | Include backtrace information on errors. +failureLevel | The minimum logging level that triggers a non-zero exit code (failure). + +Hugo provides additional settings that don't map directly to Asciidoctor's CLI options: + +workingFolderCurrent +: Sets the working directory to be the same as that of the AsciiDoc file being processed, so that [include](https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#include-files) will work with relative paths. This setting uses the `asciidoctor` cli parameter `--base-dir` and attribute `outdir=`. For rendering diagrams with [asciidoctor-diagram](https://asciidoctor.org/docs/asciidoctor-diagram/), `workingFolderCurrent` must be set to `true`. + +preserveTOC +: By default, Hugo removes the table of contents generated by Asciidoctor and provides it through the built-in variable [`.TableOfContents`](/content-management/toc/) to enable further customization and better integration with the various Hugo themes. This option can be set to `true` to preserve Asciidoctor's TOC in the generated page. + +Below are all the AsciiDoc related settings in Hugo with their default values: + +{{< code-toggle config="markup.asciidocExt" />}} + +Notice that for security concerns only extensions that do not have path separators (either `\`, `/` or `.`) are allowed. That means that extensions can only be invoked if they are in one's ruby's `$LOAD_PATH` (ie. most likely, the extension has been installed by the user). Any extension declared relative to the website's path will not be accepted. + +Example of how to set extensions and attributes: + +``` +[markup.asciidocExt] + extensions = ["asciidoctor-html5s", "asciidoctor-diagram"] + workingFolderCurrent = true + [markup.asciidocExt.attributes] + my-base-url = "https://example.com/" + my-attribute-name = "my value" +``` + +In a complex Asciidoctor environment it is sometimes helpful to debug the exact call to your external helper with all +parameters. Run Hugo with `-v`. You will get an output like + +``` +INFO 2019/12/22 09:08:48 Rendering book-as-pdf.adoc with C:\Ruby26-x64\bin\asciidoctor.bat using asciidoc args [--no-header-footer -r asciidoctor-html5s -b html5s -r asciidoctor-diagram --base-dir D:\prototypes\hugo_asciidoc_ddd\docs -a outdir=D:\prototypes\hugo_asciidoc_ddd\build -] ... +``` + +## Learn Markdown + +Markdown syntax is simple enough to learn in a single sitting. The following are excellent resources to get you up and running: + +* [Daring Fireball: Markdown, John Gruber (Creator of Markdown)][fireball] +* [Markdown Cheatsheet, Adam Pritchard][mdcheatsheet] +* [Markdown Tutorial (Interactive), Garen Torikian][mdtutorial] +* [The Markdown Guide, Matt Cone][mdguide] + +[`emojify` function]: /functions/emojify/ +[ascii]: https://asciidoctor.org/ +[bfconfig]: /getting-started/configuration/#configuring-blackfriday-rendering +[blackfriday]: https://github.com/russross/blackfriday +[mmark]: https://github.com/miekg/mmark +[config]: /getting-started/configuration/ +[developer tools]: /tools/ +[emojis]: https://www.webpagefx.com/tools/emoji-cheat-sheet/ +[fireball]: https://daringfireball.net/projects/markdown/ +[gfmtasks]: https://guides.github.com/features/mastering-markdown/#syntax +[helperssource]: https://github.com/gohugoio/hugo/blob/77c60a3440806067109347d04eb5368b65ea0fe8/helpers/general.go#L65 +[hl]: /content-management/syntax-highlighting/ +[hlsc]: /content-management/shortcodes/#highlight +[hugocss]: /css/style.css +[ietf]: https://tools.ietf.org/html/ +[mathjaxdocs]: https://docs.mathjax.org/en/latest/ +[mdcheatsheet]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet +[mdguide]: https://www.markdownguide.org/ +[mdtutorial]: https://www.markdowntutorial.com/ +[Miek Gieben's website]: https://miek.nl/2016/march/05/mmark-syntax-document/ - [mmark]: https://github.com/mmarkdown/mmark +[org]: https://orgmode.org/ +[pandoc]: https://www.pandoc.org/ +[rest]: https://docutils.sourceforge.io/rst.html +[sc]: /content-management/shortcodes/ +[sct]: /templates/shortcode-templates/ diff --cc docs/content/en/content-management/image-processing/index.md index 5b829367,00000000..710c260c mode 100644,000000..100644 --- a/docs/content/en/content-management/image-processing/index.md +++ b/docs/content/en/content-management/image-processing/index.md @@@ -1,358 -1,0 +1,462 @@@ +--- +title: "Image Processing" - description: "Image Page resources can be resized and cropped." ++description: "Resize, crop, rotate, filter, and convert images." +date: 2018-01-24T13:10:00-05:00 - linktitle: "Image Processing" +categories: ["content management"] +keywords: [resources, images] +weight: 4004 +draft: false +toc: true +menu: + docs: + parent: "content-management" + weight: 32 +--- ++## Image Resources + - ## The Image Page Resource ++To process an image, you must access the image as either a page resource or a global resource. + - The `image` is a [Page Resource]({{< relref "/content-management/page-resources" >}}), and the processing methods listed below do not work on images inside your `/static` folder. ++### Page Resources + - To print all images paths in a [Page Bundle]({{< relref "/content-management/organization#page-bundles" >}}): ++A page resource is a file within a [page bundle]. A page bundle is a directory with an `index.md` or `_index.md` file at its root. ++ ++```text ++content/ ++└── posts/ ++ └── post-1/ <-- page bundle ++ ├── index.md ++ └── sunset.jpg <-- page resource ++``` ++ ++To access an image as a page resource: + +```go-html-template - {{ with .Resources.ByType "image" }} - {{ range . }} - {{ .RelPermalink }} - {{ end }} - {{ end }} ++{{ $image := .Resources.GetMatch "sunset.jpg" }} ++``` ++ ++### Global Resources ++ ++A global resource is a file: ++ ++- Within the `assets` directory, or ++- Within any directory [mounted] to the `assets` directory, or ++- Located on a remote server accessible via `http` or `https` ++ ++```text ++assets/ ++└── images/ ++ └── sunset.jpg <-- global resource ++``` ++ ++To access a local image as a global resource: + ++```go-html-template ++{{ $image := resources.Get "images/sunset.jpg" }} ++``` ++ ++To access a remote image as a global resource: ++ ++```go-html-template ++{{ $image := resources.GetRemote "https://gohugo.io/img/hugo-logo.png" }} ++``` ++ ++## Image Rendering ++ ++Once you have accessed an image as either a page resource or a global resource, render it in your templates using the `Permalink`, `RelPermalink`, `Width`, and `Height` properties. ++ ++Example 1: Throws an error if the resource is not found. ++ ++```go-html-template ++{{ $image := .Resources.GetMatch "sunset.jpg" }} ++ +``` + - ## The Image Resource ++Example 2: Skips image rendering if the resource is not found. ++ ++```go-html-template ++{{ $image := .Resources.GetMatch "sunset.jpg" }} ++{{ with $image }} ++ ++{{ end }} ++``` + - The `image` resource can also be retrieved from a [global resource]({{< relref "/hugo-pipes/introduction#from-file-to-resource" >}}) ++Example 3: A more concise way to skip image rendering if the resource is not found. + +```go-html-template - {{- $image := resources.Get "images/logo.jpg" -}} ++{{ with .Resources.GetMatch "sunset.jpg" }} ++ ++{{ end }} +``` + +## Image Processing Methods + - The `image` resource implements the `Resize`, `Fit`, `Fill`, `Crop`, and `Filter` methods, each returning a transformed image using the specified dimensions and processing options. ++The `image` resource implements the `Resize`, `Fit`, `Fill`, `Crop`, `Filter`, and `Exif` methods. + +{{% note %}} - Metadata (EXIF, IPTC, XMP, etc.) is not preserved during image transformation. Use the [`Exif`](#exif) method with the _original_ image to extract EXIF metadata from JPEG or TIFF images. ++Metadata (Exif, IPTC, XMP, etc.) is not preserved during image transformation. Use the`Exif` method with the _original_ image to extract Exif metadata from JPEG or TIFF images. +{{% /note %}} + +### Resize + - Resizes the image to the specified width and height. ++Resize an image to the specified width and/or height. ++ ++If you specify both width and height, the resulting image will be disproportionally scaled unless the original image has the same aspect ratio. + - ```go - // Resize to a width of 600px and preserve ratio - {{ $image := $resource.Resize "600x" }} ++```go-html-template ++{{/* Resize to a width of 600px and preserve aspect ratio */}} ++{{ $image := $image.Resize "600x" }} + - // Resize to a height of 400px and preserve ratio - {{ $image := $resource.Resize "x400" }} ++{{/* Resize to a height of 400px and preserve aspect ratio */}} ++{{ $image := $image.Resize "x400" }} + - // Resize to a width 600px and a height of 400px - {{ $image := $resource.Resize "600x400" }} ++{{/* Resize to a width of 600px and a height of 400px */}} ++{{ $image := $image.Resize "600x400" }} +``` + +### Fit + - Scale down the image to fit the given dimensions while maintaining aspect ratio. Both height and width are required. ++Downscale an image to fit the given dimensions while maintaining aspect ratio. You must provide both width and height. + - ```go - {{ $image := $resource.Fit "600x400" }} ++```go-html-template ++{{ $image := $image.Fit "600x400" }} +``` + +### Fill + - Crop and resize the image to match the given dimensions. Both height and width are required. ++Crop and resize an image to match the given dimensions. You must provide both width and height. Use the [`anchor`] option to change the crop box anchor point. + - ```go - {{ $image := $resource.Fill "600x400" }} ++```go-html-template ++{{ $image := $image.Fill "600x400" }} +``` + +### Crop + - Crop the image to match the given dimensions without resizing. Both height and width are required. ++Crop an image to match the given dimensions without resizing. You must provide both width and height. Use the [`anchor`] option to change the crop box anchor point. + - ```go - {{ $image := $resource.Crop "400x400" }} ++```go-html-template ++{{ $image := $image.Crop "600x400" }} +``` + +### Filter + - Apply one or more filters to your image. See [Image Filters](/functions/images/#image-filters) for a full list. ++Apply one or more [filters] to an image. + +```go-html-template - {{ $img = $img.Filter (images.GaussianBlur 6) (images.Pixelate 8) }} ++{{ $image := $image.Filter (images.GaussianBlur 6) (images.Pixelate 8) }} +``` + - The above can also be written in a more functional style using pipes: ++Write this in a more functional style using pipes. Hugo applies the filters in the order given. + +```go-html-template - {{ $img = $img | images.Filter (images.GaussianBlur 6) (images.Pixelate 8) }} ++{{ $image := $image | images.Filter (images.GaussianBlur 6) (images.Pixelate 8) }} +``` + - The filters will be applied in the given order. - - Sometimes it can be useful to create the filter chain once and then reuse it: ++Sometimes it can be useful to create the filter chain once and then reuse it. + +```go-html-template +{{ $filters := slice (images.GaussianBlur 6) (images.Pixelate 8) }} - {{ $img1 = $img1.Filter $filters }} - {{ $img2 = $img2.Filter $filters }} ++{{ $image1 := $image1.Filter $filters }} ++{{ $image2 := $image2.Filter $filters }} +``` + +### Exif + - Provides an [Exif](https://en.wikipedia.org/wiki/Exif) object with metadata about the image. ++Provides an [Exif] object containing image metadata. + - Note that this is only supported for JPEG and TIFF images, so it's recommended to wrap the access with a `with`, e.g.: ++You may access Exif data in JPEG and TIFF images. To prevent errors when processing images without Exif data, wrap the access in a `with` statement. + +```go-html-template - {{ with $img.Exif }} - Date: {{ .Date }} - Lat/Long: {{ .Lat}}/{{ .Long }} - Tags: - {{ range $k, $v := .Tags }} - TAG: {{ $k }}: {{ $v }} - {{ end }} ++{{ with $image.Exif }} ++ Date: {{ .Date }} ++ Lat/Long: {{ .Lat }}/{{ .Long }} ++ Tags: ++ {{ range $k, $v := .Tags }} ++ TAG: {{ $k }}: {{ $v }} ++ {{ end }} +{{ end }} +``` + - Or individually access EXIF data with dot access, e.g.: ++You may also access Exif fields individually, using the [`lang.FormatNumber`] function to format the fields as needed. + +```go-html-template - {{ with $src.Exif }} ++{{ with $image.Exif }} + +{{ end }} +``` + - Some fields may need to be formatted with [`lang.FormatNumberCustom`]({{< relref "functions/lang" >}}) function to prevent display like `Aperture: 2.278934289` instead of `Aperture: 2.28`. - - #### Exif fields ++#### Exif Variables + - Date - : "photo taken" date/time ++.Date ++: Image creation date/time. Format with the [time.Format] function. + - Lat - : "photo taken where", GPS latitude ++.Lat ++: GPS latitude in degrees. + - Long - : "photo taken where", GPS longitude ++.Long ++: GPS longitude in degrees. + - See [Image Processing Config](#image-processing-config) for how to configure what gets included in Exif. ++.Tags ++: A collection of the available Exif tags for this image. You may include or exclude specific tags from this collection in the [site configuration](#exif-data). + +## Image Processing Options + - In addition to the dimensions (e.g. `600x400`), Hugo supports a set of additional image options. ++The `Resize`, `Fit`, `Fill`, and `Crop` methods accept a space-separated, case-insensitive list of options. The order of the options within the list is irrelevant. + - ### Background Color - - The background color to fill into the transparency layer. This is mostly useful when converting to a format that does not support transparency, e.g. `JPEG`. ++### Dimensions + - You can set the background color to use with a 3 or 6 digit hex code starting with `#`. ++With the `Resize` method you must specify width, height, or both. The `Fit`, `Fill`, and `Crop` methods require both width and height. All dimensions are in pixels. + - ```go - {{ $image.Resize "600x jpg #b31280" }} ++```go-html-template ++{{ $image := $image.Resize "600x" }} ++{{ $image := $image.Resize "x400" }} ++{{ $image := $image.Resize "600x400" }} ++{{ $image := $image.Fit "600x400" }} ++{{ $image := $image.Fill "600x400" }} ++{{ $image := $image.Crop "600x400" }} +``` + - For color codes, see https://www.google.com/search?q=color+picker ++### Rotation ++ ++Rotates an image counter-clockwise by the given angle. Hugo performs rotation _before_ scaling. For example, if the original image is 600x400 and you wish to rotate the image 90 degrees counter-clockwise while scaling it by 50%: + - **Note** that you also set a default background color to use, see [Image Processing Config](#image-processing-config). ++```go-html-template ++{{ $image = $image.Resize "200x r90" }} ++``` + - ### JPEG and WebP Quality ++In the example above, the width represents the desired width _after_ rotation. + - Only relevant for JPEG and WebP images, values 1 to 100 inclusive, higher is better. Default is 75. ++To rotate an image without scaling, use the dimensions of the original image: + - ```go - {{ $image.Resize "600x q50" }} ++```go-html-template ++{{ with .Resources.GetMatch "sunset.jpg" }} ++ {{ with .Resize (printf "%dx%d r90" .Height .Width) }} ++ ++ {{ end }} ++{{ end }} +``` + - {{< new-in "0.83.0" >}} WebP support was added in Hugo 0.83.0. ++In the example above, on the second line, we have reversed width and height to reflect the desired dimensions _after_ rotation. + - ### Hint ++### Anchor + - {{< new-in "0.83.0" >}} ++When using the `Crop` or `Fill` method, the _anchor_ determines the placement of the crop box. You may specify `TopLeft`, `Top`, `TopRight`, `Left`, `Center`,`Right`, `BottomLeft`, `Bottom`, `BottomRight`, or `Smart`. + - {{< new-in "0.83.0" >}} ++The default value is `Smart`, which uses [Smartcrop] image analysis to determine the optimal placement of the crop box. You may override the default value in the [site configuration](#processing-options). + - Hint about what type of image this is. Currently only used when encoding to WebP. ++For example, if you have a 400x200 image with a bird in the upper left quadrant, you can create a 200x100 thumbnail containing the bird: + - Default value is `photo`. ++```go-html-template ++{{ $image.Crop "200x100 TopLeft" }} ++``` ++ ++If you apply rotation when using the `Crop` or `Fill` method, specify the anchor relative to the rotated image. ++ ++### Target Format ++ ++By default, Hugo encodes the image in the source format. You may convert the image to another format by specifying `bmp`, `gif`, `jpeg`, `jpg`, `png`, `tif`, `tiff`, or `webp`. ++ ++```go-html-template ++{{ $image.Resize "600x webp" }} ++``` + - Valid values are `picture`, `photo`, `drawing`, `icon`, or `text`. ++To convert an image without scaling, use the dimensions of the original image: + - ```go - {{ $image.Resize "600x webp drawing" }} ++```go-html-template ++{{ with .Resources.GetMatch "sunset.jpg" }} ++ {{ with .Resize (printf "%dx%d webp" .Width .Height) }} ++ ++ {{ end }} ++{{ end }} +``` + - ### Rotate ++### Quality ++ ++Applicable to JPEG and WebP images, the `q` value determines the quality of the converted image. Higher values produce better quality images, while lower values produce smaller files. Set this value to a whole number between 1 and 100, inclusive. + - Rotates an image by the given angle counter-clockwise. The rotation will be performed first to get the dimensions correct. The main use of this is to be able to manually correct for [EXIF orientation](https://github.com/golang/go/issues/4341) of JPEG images. ++The default value is 75. You may override the default value in the [site configuration](#processing-options). + - ```go - {{ $image.Resize "600x r90" }} ++```go-html-template ++{{ $image.Resize "600x webp q50" }} +``` + - ### Anchor ++### Hint ++ ++ + - Only relevant for the `Crop` and `Fill` methods. This is useful for thumbnail generation where the main motive is located in, say, the left corner. ++Applicable to WebP images, this option corresponds to a set of pre-defined encoding parameters. + - Valid values are `Smart`, `Center`, `TopLeft`, `Top`, `TopRight`, `Left`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`. ++Value|Example ++:--|:-- ++`drawing`|Hand or line drawing with high-contrast details ++`icon`|Small colorful image ++`photo`|Outdoor photograph with natural lighting ++`picture`|Indoor photograph such as a portrait ++`text`|Image that is primarily text + - Default value is `Smart`, which uses [Smartcrop](https://github.com/muesli/smartcrop) to determine the best crop. ++The default value is `photo`. You may override the default value in the [site configuration](#processing-options). + - ```go - {{ $image.Fill "300x200 BottomLeft" }} ++```go-html-template ++{{ $image.Resize "600x webp picture" }} +``` + - ### Resample Filter ++### Background Color + - Filter used in resizing. Default is `Box`, a simple and fast resampling filter appropriate for downscaling. ++When converting an image from a format that supports transparency (e.g., PNG) to a format that does _not_ support transparency (e.g., JPEG), you may specify the background color of the resulting image. + - Examples are: `Box`, `NearestNeighbor`, `Linear`, `Gaussian`. ++Use either a 3-digit or a 6-digit hexadecimal color code (e.g., `#00f` or `#0000ff`). + - See https://github.com/disintegration/imaging for more. If you want to trade quality for faster processing, this may be a option to test. ++The default value is `#ffffff` (white). You may override the default value in the [site configuration](#processing-options). + - ```go - {{ $image.Resize "600x400 Gaussian" }} ++```go-html-template ++{{ $image.Resize "600x jpg #b31280" }} +``` + - ### Target Format ++### Resampling Filter ++ ++You may specify the resampling filter used when resizing an image. Commonly used resampling filters include: + - By default the images is encoded in the source format, but you can set the target format as an option. ++Filter|Description ++:--|:-- ++`Box`|Simple and fast averaging filter appropriate for downscaling ++`Lanczos`|High-quality resampling filter for photographic images yielding sharp results ++`CatmullRom`|Sharp cubic filter that is faster than the Lanczos filter while providing similar results ++`MitchellNetravali`|Cubic filter that produces smoother results with less ringing artifacts than CatmullRom ++`Linear`|Bilinear resampling filter, produces smooth output, faster than cubic filters ++`NearestNeighbor`|Fastest resampling filter, no antialiasing + - Valid values are `bmp`, `gif`, `jpeg`, `jpg`, `png`, `tif`, `tiff`, and `webp`. ++The default value is `Box`. You may override the default value in the [site configuration](#processing-options). + - ```go - {{ $image.Resize "600x jpg" }} ++```go-html-template ++{{ $image.Resize "600x400 Lanczos" }} +``` + - {{< new-in "0.83.0" >}} WebP support was added in Hugo 0.83.0. ++See [github.com/disintegration/imaging] for the complete list of resampling filters. If you wish to improve image quality at the expense of performance, you may wish to experiment with the alternative filters. + +## Image Processing Examples + +_The photo of the sunset used in the examples below is Copyright [Bjørn Erik Pedersen](https://commons.wikimedia.org/wiki/User:Bep) (Creative Commons Attribution-Share Alike 4.0 International license)_ + +{{< imgproc sunset Resize "300x" />}} + +{{< imgproc sunset Fill "90x120 left" />}} + +{{< imgproc sunset Fill "90x120 right" />}} + +{{< imgproc sunset Fit "90x90" />}} + +{{< imgproc sunset Crop "250x250 center" />}} + +{{< imgproc sunset Resize "300x q10" />}} + - This is the shortcode used in the examples above: ++This is the shortcode used to generate the examples above: + +{{< code file="layouts/shortcodes/imgproc.html" >}} +{{< readfile file="layouts/shortcodes/imgproc.html" >}} +{{< /code >}} + - And it is used like this: ++Call the shortcode from your markdown like this: + +```go-html-template +{{}} +``` + +{{% note %}} - **Tip:** Note the self-closing shortcode syntax above. The `imgproc` shortcode can be called both with and without **inner content**. ++Note the self-closing shortcode syntax above. You may call the `imgproc` shortcode with or without **inner content**. +{{% /note %}} + - ## Image Processing Config ++## Imaging Configuration + - You can configure an `imaging` section in `config.toml` with default image processing options: ++### Processing Options + - ```toml - [imaging] - # Default resample filter used for resizing. Default is Box, - # a simple and fast averaging filter appropriate for downscaling. - # See https://github.com/disintegration/imaging - resampleFilter = "box" ++Define an `imaging` section in your site configuration to set the default [image processing options](#image-processing-options). + - # Default JPEG or WebP quality setting. Default is 75. ++{{< code-toggle file="config" copy=true >}} ++[imaging] ++resampleFilter = "Box" +quality = 75 - - # Default hint about what type of image. Currently only used for WebP encoding. - # Default is "photo". - # Valid values are "picture", "photo", "drawing", "icon", or "text". +hint = "photo" - - # Anchor used when cropping pictures with either .Fill or .Crop - # Default is "smart" which does Smart Cropping, using https://github.com/muesli/smartcrop - # Smart Cropping is content aware and tries to find the best crop for each image. - # Valid values are Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight - anchor = "smart" - - # Default background color. - # Hugo will preserve transparency for target formats that supports it, - # but will fall back to this color for JPEG. - # Expects a standard HEX color string with 3 or 6 digits. - # See https://www.google.com/search?q=color+picker ++anchor = "Smart" +bgColor = "#ffffff" ++{{< /code-toggle >}} ++ ++anchor ++: See image processing options: [anchor](#anchor). ++ ++bgColor ++: See image processing options: [background color](#background-color). ++ ++hint ++: See image processing options: [hint](#hint). ++ ++quality ++: See image processing options: [quality](#quality). + ++resampleFilter ++: See image processing options: [resampling filter](#resampling-filter). ++ ++### Exif Data ++ ++Define an `imaging.exif` section in your site configuration to control the availability of Exif data. ++ ++{{< code-toggle file="config" copy=true >}} +[imaging.exif] - # Regexp matching the fields you want to Exclude from the (massive) set of Exif info - # available. As we cache this info to disk, this is for performance and - # disk space reasons more than anything. - # If you want it all, put ".*" in this config setting. - # Note that if neither this or ExcludeFields is set, Hugo will return a small - # default set: GPS|Exif|Exposure[M|P|B]|Contrast|Resolution|Sharp|JPEG|Metering|Sensing|Saturation|ColorSpace|Flash|WhiteBalance +includeFields = "" - - # Regexp matching the Exif fields you want to exclude. This may be easier to use - # than IncludeFields above, depending on what you want. +excludeFields = "" - - # Hugo extracts the "photo taken" date/time into .Date by default. - # Set this to true to turn it off. +disableDate = false - - # Hugo extracts the "photo taken where" (GPS latitude and longitude) into - # .Long and .Lat. Set this to true to turn it off. +disableLatLong = false - ``` ++{{< /code-toggle >}} ++ ++disableDate ++: Hugo extracts the image creation date/time into `.Date`. Set this to `true` to disable. Default is `false`. ++ ++disableLatLong ++: Hugo extracts the GPS latitude and longitude into `.Lat` and `.Long`. Set this to `true` to disable. Default is `false`. ++ ++excludeFields ++: Regular expression matching the Exif tags to exclude from the `.Tags` collection. Default is `""`. ++ ++includeFields ++: Regular expression matching the Exif tags to include in the `.Tags` collection. Default is `""`. To include all available tags, set this value to `".*"`. ++ ++{{% note %}} ++To improve performance and decrease cache size, if you set neither `excludeFields` nor `includeFields`, Hugo excludes the following tags: `ColorSpace`, `Contrast`, `Exif`, `Exposure[M|P|B]`, `Flash`, `GPS`, `JPEG`, `Metering`, `Resolution`, `Saturation`, `Sensing`, `Sharp`, and `WhiteBalance`. ++{{% /note %}} + +## Smart Cropping of Images + - By default, Hugo will use [Smartcrop](https://github.com/muesli/smartcrop), a library created by [muesli](https://github.com/muesli), when cropping images with `.Fill` or `.Crop`. You can set the anchor point manually, but in most cases the smart option will make a good choice. And we will work with the library author to improve this in the future. ++By default, Hugo uses the [Smartcrop] library when cropping images with the `Crop` or`Fill` methods. You can set the anchor point manually, but in most cases the `Smart` option will make a good choice. + +Examples using the sunset image from above: + +{{< imgproc sunset Fill "200x200 smart" />}} + +{{< imgproc sunset Crop "200x200 smart" />}} + +## Image Processing Performance Consideration + - Processed images are stored below `/resources` (can be set with `resourceDir` config setting). This folder is deliberately placed in the project, as it is recommended to check these into source control as part of the project. These images are not "Hugo fast" to generate, but once generated they can be reused. - - If you change your image settings (e.g. size), remove or rename images etc., you will end up with unused images taking up space and cluttering your project. ++Hugo caches processed images in the `resources` directory. If you include this directory in source control, Hugo will not have to regenerate the images in a CI/CD workflow (e.g., GitHub Pages, GitLab Pages, Netlify, etc.). This results in faster builds. + - To clean up, run: ++If you change image processing methods or options, or if you rename or remove images, the `resources` directory will contain unused images. To remove the unused images, perform garbage collection with: + +```bash +hugo --gc +``` + - {{% note %}} - **GC** is short for **Garbage Collection**. - {{% /note %}} ++[`anchor`]: {{< relref "content-management/image-processing#anchor" >}} ++[`lang.FormatNumber`]: {{< relref "functions/lang#langformatnumber" >}} ++[Exif]: ++[filters]: {{< relref "functions/images" >}} ++[github.com/disintegration/imaging]: ++[mounted]: {{< relref "hugo-modules/configuration#module-config-mounts">}} ++[page bundle]: {{< relref "content-management/page-bundles" >}} ++[Smartcrop]: ++[time.Format]: {{< relref "functions/dateformat" >}} diff --cc docs/content/en/content-management/related.md index a42cec32,00000000..9ede1525 mode 100644,000000..100644 --- a/docs/content/en/content-management/related.md +++ b/docs/content/en/content-management/related.md @@@ -1,137 -1,0 +1,137 @@@ +--- +title: Related Content +description: List related content in "See Also" sections. +date: 2017-09-05 +categories: [content management] +keywords: [content] +menu: + docs: + parent: "content-management" + weight: 40 +weight: 30 +draft: false +aliases: [/content/related/,/related/] +toc: true +--- + + +Hugo uses a set of factors to identify a page's related content based on Front Matter parameters. This can be tuned to the desired set of indices and parameters or left to Hugo's default [Related Content configuration](#configure-related-content). + +## List Related Content + + +To list up to 5 related pages (which share the same _date_ or _keyword_ parameters) is as simple as including something similar to this partial in your single page template: + +{{< code file="layouts/partials/related.html" >}} +{{ $related := .Site.RegularPages.Related . | first 5 }} +{{ with $related }} +

See Also

+ +{{ end }} +{{< /code >}} + +### Methods + +Here is the list of "Related" methods available on a page collection such `.RegularPages`. + +#### .Related PAGE +Returns a collection of pages related the given one. + +``` - {{ $related := .Site.RegularPages.Related . }} ++{{ $related := site.RegularPages.Related . }} +``` + +#### .RelatedIndices PAGE INDICE1 [INDICE2 ...] +Returns a collection of pages related to a given one restricted to a list of indices. + +``` - {{ $related := .Site.RegularPages.RelatedIndices . "tags" "date" }} ++{{ $related := site.RegularPages.RelatedIndices . "tags" "date" }} +``` + +#### .RelatedTo KEYVALS [KEYVALS2 ...] +Returns a collection of pages related together by a set of indices and their match. + +In order to build those set and pass them as argument, one must use the `keyVals` function where the first argument would be the `indice` and the consecutive ones its potential `matches`. + +``` - {{ $related := .Site.RegularPages.RelatedTo ( keyVals "tags" "hugo" "rocks") ( keyVals "date" .Date ) }} ++{{ $related := site.RegularPages.RelatedTo ( keyVals "tags" "hugo" "rocks") ( keyVals "date" .Date ) }} +``` + +{{% note %}} +Read [this blog article](https://regisphilibert.com/blog/2018/04/hugo-optmized-relashionships-with-related-content/) for a great explanation of more advanced usage of this feature. +{{% /note %}} + +## Configure Related Content +Hugo provides a sensible default configuration of Related Content, but you can fine-tune this in your configuration, on the global or language level if needed. + +### Default configuration + +Without any `related` configuration set on the project, Hugo's Related Content methods will use the following. + +{{< code-toggle file="config" >}} +related: + threshold: 80 + includeNewer: false + toLower: false + indices: + - name: keywords + weight: 100 + - name: date + weight: 10 +{{< /code-toggle >}} + +Note that if you have configured `tags` as a taxonomy, `tags` will also be added to the default configuration above with the weight of `80`. + +Custom configuration should be set using the same syntax. + +{{% note %}} +If you add a `related` config section, you need to add a complete configuration. It is not possible to just set, say, `includeNewer` and use the rest from the Hugo defaults. +{{% /note %}} + +### Top Level Config Options + +threshold +: A value between 0-100. Lower value will give more, but maybe not so relevant, matches. + +includeNewer +: Set to true to include **pages newer than the current page** in the related content listing. This will mean that the output for older posts may change as new related content gets added. + +toLower +: Set to true to lower case keywords in both the indexes and the queries. This may give more accurate results at a slight performance penalty. Note that this can also be set per index. + +### Config Options per Index + +name +: The index name. This value maps directly to a page param. Hugo supports string values (`author` in the example) and lists (`tags`, `keywords` etc.) and time and date objects. + +weight +: An integer weight that indicates _how important_ this parameter is relative to the other parameters. It can be 0, which has the effect of turning this index off, or even negative. Test with different values to see what fits your content best. + +pattern +: This is currently only relevant for dates. When listing related content, we may want to list content that is also close in time. Setting "2006" (default value for date indexes) as the pattern for a date index will add weight to pages published in the same year. For busier blogs, "200601" (year and month) may be a better default. + +toLower +: See above. + +## Performance Considerations + +**Fast is Hugo's middle name** and we would not have released this feature had it not been blistering fast. + +This feature has been in the back log and requested by many for a long time. The development got this recent kick start from this Twitter thread: + +{{< tweet user="scott_lowe" id="898398437527363585" >}} + +Scott S. Lowe removed the "Related Content" section built using the `intersect` template function on tags, and the build time dropped from 30 seconds to less than 2 seconds on his 1700 content page sized blog. + +He should now be able to add an improved version of that "Related Content" section without giving up the fast live-reloads. But it's worth noting that: + +* If you don't use any of the `Related` methods, you will not use the Relate Content feature, and performance will be the same as before. +* Calling `.RegularPages.Related` etc. will create one inverted index, also sometimes named posting list, that will be reused for any lookups in that same page collection. Doing that in addition to, as an example, calling `.Pages.Related` will work as expected, but will create one additional inverted index. This should still be very fast, but worth having in mind, especially for bigger sites. + +{{% note %}} +We currently do not index **Page content**. We thought we would release something that will make most people happy before we start solving [Sherlock's last case](https://github.com/joearms/sherlock). +{{% /note %}} diff --cc docs/content/en/content-management/urls.md index c0eb59d6,00000000..25d0f5d7 mode 100644,000000..100644 --- a/docs/content/en/content-management/urls.md +++ b/docs/content/en/content-management/urls.md @@@ -1,313 -1,0 +1,314 @@@ +--- +title: URL Management +linktitle: URL Management +description: Hugo supports permalinks, aliases, link canonicalization, and multiple options for handling relative vs absolute URLs. +date: 2017-02-01 +publishdate: 2017-02-01 +lastmod: 2017-03-09 +keywords: [aliases,redirects,permalinks,urls] +categories: [content management] +menu: + docs: + parent: "content-management" + weight: 110 +weight: 110 #rem +draft: false +aliases: [/extras/permalinks/,/extras/aliases/,/extras/urls/,/doc/redirects/,/doc/alias/,/doc/aliases/] +toc: true +--- + +## Permalinks + +The default Hugo target directory for your built website is `public/`. However, you can change this value by specifying a different `publishDir` in your [site configuration][config]. The directories created at build time for a section reflect the position of the content's directory within the `content` folder and namespace matching its layout within the `contentdir` hierarchy. + +The `permalinks` option in your [site configuration][config] allows you to adjust the directory paths (i.e., the URLs) on a per-section basis. This will change where the files are written to and will change the page's internal "canonical" location, such that template references to `.RelPermalink` will honor the adjustments made as a result of the mappings in this option. + +{{% note "Default Publish and Content Folders" %}} +These examples use the default values for `publishDir` and `contentDir`; i.e., `public` and `content`, respectively. You can override the default values in your [site's `config` file](/getting-started/configuration/). +{{% /note %}} + +For example, if one of your [sections][] is called `posts` and you want to adjust the canonical path to be hierarchical based on the year, month, and post title, you could set up the following configurations in YAML and TOML, respectively. + +### Permalinks Configuration Example + +{{< code-toggle file="config" copy="false" >}} +permalinks: + posts: /:year/:month/:title/ +{{< /code-toggle >}} + +Only the content under `posts/` will have the new URL structure. For example, the file `content/posts/sample-entry.md` with `date: 2017-02-27T19:20:00-05:00` in its front matter will render to `public/2017/02/sample-entry/index.html` at build time and therefore be reachable at `https://example.com/2017/02/sample-entry/`. + +To configure the `permalinks` option for pages in the "root" section, use **/** as the key: + +{{< code-toggle file="config" copy="false" >}} +permalinks: + /: /:year/:month/:filename/ +{{< /code-toggle >}} + +If the standard date-based permalink configuration does not meet your needs, you can also format URL segments using [Go time formatting directives](https://golang.org/pkg/time/#Time.Format). For example, a URL structure with two digit years and month and day digits without zero padding can be accomplished with: + +{{< code-toggle file="config" copy="false" >}} +permalinks: + posts: /:06/:1/:2/:title/ +{{< /code-toggle >}} + +You can also configure permalinks of taxonomies with the same syntax, by using the plural form of the taxonomy instead of the section. You will probably only want to use the configuration values `:slug` or `:title`. + +### Permalink Configuration Values + +The following is a list of values that can be used in a `permalink` definition in your site `config` file. All references to time are dependent on the content's date. + +`:year` +: the 4-digit year + +`:month` +: the 2-digit month + +`:monthname` +: the name of the month + +`:day` +: the 2-digit day + +`:weekday` +: the 1-digit day of the week (Sunday = 0) + +`:weekdayname` +: the name of the day of the week + +`:yearday` +: the 1- to 3-digit day of the year + +`:section` +: the content's section + +`:sections` +: the content's sections hierarchy. {{< new-in "0.83.0" >}} Since Hugo 0.83 you can use a selection of the sections using _slice syntax_: `:sections[1:]` includes all but the first, `:sections[:last]` includes all but the last, `:sections[last]` includes only the last, `:sections[1:2]` includes section 2 and 3. Note that this slice access will not throw any out-of-bounds errors, so you don't have to be exact. + +`:title` +: the content's title + +`:slug` +: the content's slug (or title if no slug is provided in the front matter) + +`:filename` +: the content's filename (without extension) + +Additionally, a Go time format string prefixed with `:` may be used. + +## Aliases + +Aliases can be used to create redirects to your page from other URLs. + +Aliases comes in two forms: + +1. Starting with a `/` meaning they are relative to the `BaseURL`, e.g. `/posts/my-blogpost/` +2. They are relative to the `Page` they're defined in, e.g. `my-blogpost` or even something like `../blog/my-blogpost` (new in Hugo 0.55). + +### Example: Aliases + +Let's assume you create a new piece of content at `content/posts/my-awesome-blog-post.md`. The content is a revision of your previous post at `content/posts/my-original-url.md`. You can create an `aliases` field in the front matter of your new `my-awesome-blog-post.md` where you can add previous paths. The following examples show how to create this field in TOML and YAML front matter, respectively. + +#### TOML Front Matter + +{{< code file="content/posts/my-awesome-post.md" copy="false" >}} ++++ +aliases = [ + "/posts/my-original-url/", + "/2010/01/01/even-earlier-url.html" +] ++++ +{{< /code >}} + +#### YAML Front Matter + +{{< code file="content/posts/my-awesome-post.md" copy="false" >}} +--- +aliases: + - /posts/my-original-url/ + - /2010/01/01/even-earlier-url.html +--- +{{< /code >}} + +Now when you visit any of the locations specified in aliases---i.e., *assuming the same site domain*---you'll be redirected to the page they are specified on. For example, a visitor to `example.com/posts/my-original-url/` will be immediately redirected to `example.com/posts/my-awesome-post/`. + +### Example: Aliases in Multilingual + +On [multilingual sites][multilingual], each translation of a post can have unique aliases. To use the same alias across multiple languages, prefix it with the language code. + +In `/posts/my-new-post.es.md`: + +``` +--- +aliases: + - /es/posts/my-original-post/ +--- +``` + +From Hugo 0.55 you can also have page-relative aliases, so ` /es/posts/my-original-post/` can be simplified to the more portable `my-original-post/` + +### How Hugo Aliases Work + +When aliases are specified, Hugo creates a directory to match the alias entry. Inside the directory, Hugo creates an `.html` file specifying the canonical URL for the page and the new redirect target. + +For example, a content file at `posts/my-intended-url.md` with the following in the front matter: + +``` +--- +title: My New post +aliases: [/posts/my-old-url/] +--- +``` + +Assuming a `baseURL` of `example.com`, the contents of the auto-generated alias `.html` found at `https://example.com/posts/my-old-url/` will contain the following: + +``` + + + + https://example.com/posts/my-intended-url + + + + + + +``` + +The `http-equiv="refresh"` line is what performs the redirect, in 0 seconds in this case. If an end user of your website goes to `https://example.com/posts/my-old-url`, they will now be automatically redirected to the newer, correct URL. The addition of `` lets search engine bots know that they should not crawl and index your new alias page. + +### Customize ++ +You may customize this alias page by creating an `alias.html` template in the +layouts folder of your site (i.e., `layouts/alias.html`). In this case, the data passed to the template is + +`Permalink` +: the link to the page being aliased + +`Page` +: the Page data for the page being aliased + +### Important Behaviors of Aliases + +1. Hugo makes no assumptions about aliases. They also do not change based +on your UglyURLs setting. You need to provide absolute paths to your web root +and the complete filename or directory. +2. Aliases are rendered *before* any content are rendered and therefore will be overwritten by any content with the same location. + +## Pretty URLs + +Hugo's default behavior is to render your content with "pretty" URLs. No non-standard server-side configuration is required for these pretty URLs to work. + +The following demonstrates the concept: + +``` +content/posts/_index.md +=> example.com/posts/ +content/posts/post-1.md +=> example.com/posts/post-1/ +``` + +## Ugly URLs + +If you would like to have what are often referred to as "ugly URLs" (e.g., example.com/urls.html), set `uglyurls = true` or `uglyurls: true` in your site's `config.toml` or `config.yaml`, respectively. You can also set the `HUGO_UGLYURLS` environment variable to `true` when running `hugo` or `hugo server`. + +If you want a specific piece of content to have an exact URL, you can specify this in the [front matter][] under the `url` key. The following are examples of the same content directory and what the eventual URL structure will be when Hugo runs with its default behavior. + +See [Content Organization][contentorg] for more details on paths. + +``` +. +└── content + └── about + | └── _index.md // <- https://example.com/about/ + ├── posts + | ├── firstpost.md // <- https://example.com/posts/firstpost/ + | ├── happy + | | └── ness.md // <- https://example.com/posts/happy/ness/ + | └── secondpost.md // <- https://example.com/posts/secondpost/ + └── quote + ├── first.md // <- https://example.com/quote/first/ + └── second.md // <- https://example.com/quote/second/ +``` + +Here's the same organization run with `hugo --uglyURLs`: + +``` +. +└── content + └── about + | └── _index.md // <- https://example.com/about.html + ├── posts + | ├── firstpost.md // <- https://example.com/posts/firstpost.html + | ├── happy + | | └── ness.md // <- https://example.com/posts/happy/ness.html + | └── secondpost.md // <- https://example.com/posts/secondpost.html + └── quote + ├── first.md // <- https://example.com/quote/first.html + └── second.md // <- https://example.com/quote/second.html +``` + + +## Canonicalization + +By default, all relative URLs encountered in the input are left unmodified, e.g. `/css/foo.css` would stay as `/css/foo.css`. The `canonifyURLs` field in your site `config` has a default value of `false`. + +By setting `canonifyURLs` to `true`, all relative URLs would instead be *canonicalized* using `baseURL`. For example, assuming you have `baseURL = https://example.com/`, the relative URL `/css/foo.css` would be turned into the absolute URL `https://example.com/css/foo.css`. + +Benefits of canonicalization include fixing all URLs to be absolute, which may aid with some parsing tasks. Note, however, that all modern browsers handle this on the client without issue. + +Benefits of non-canonicalization include being able to have scheme-relative resource inclusion; e.g., so that `http` vs `https` can be decided according to how the page was retrieved. + +{{% note "`canonifyURLs` default change" %}} +In the May 2014 release of Hugo v0.11, the default value of `canonifyURLs` was switched from `true` to `false`, which we think is the better default and should continue to be the case going forward. Please verify and adjust your website accordingly if you are upgrading from v0.10 or older versions. +{{% /note %}} + +To find out the current value of `canonifyURLs` for your website, you may use the handy `hugo config` command added in v0.13. + +``` +hugo config | grep -i canon +``` + +Or, if you are on Windows and do not have `grep` installed: + +``` +hugo config | FINDSTR /I canon +``` + +## Set URL in Front Matter + +In addition to specifying permalink values in your site configuration for different content sections, Hugo provides even more granular control for individual pieces of content. + +Both `slug` and `url` can be defined in individual front matter. For more information on content destinations at build time, see [Content Organization][contentorg]. + +From Hugo 0.55, you can use URLs relative to the current site context (the language), which makes it simpler to maintain. For a Japanese translation, both of the following examples would get the same URL: + +```markdown +--- +title: "Custom URL!" +url: "/jp/custom/foo" +--- +``` + +```markdown +--- +title: "Custom URL!" +url: "custom/foo" +--- +``` + + +## Relative URLs + +By default, all relative URLs are left unchanged by Hugo, which can be problematic when you want to make your site browsable from a local file system. + +Setting `relativeURLs` to `true` in your [site configuration][config] will cause Hugo to rewrite all relative URLs to be relative to the current content. + +For example, if your `/posts/first/` page contains a link to `/about/`, Hugo will rewrite the URL to `../../about/`. + +[config]: /getting-started/configuration/ +[contentorg]: /content-management/organization/ +[front matter]: /content-management/front-matter/ +[multilingual]: /content-management/multilingual/ +[sections]: /content-management/sections/ +[usage]: /getting-started/usage/ diff --cc docs/content/en/contribute/development.md index 27df5c6f,00000000..dd92ec0a mode 100644,000000..100644 --- a/docs/content/en/contribute/development.md +++ b/docs/content/en/contribute/development.md @@@ -1,436 -1,0 +1,436 @@@ +--- +title: Contribute to Hugo Development +linktitle: Development +description: Hugo relies heavily on contributions from the open source community. +date: 2017-02-01 +publishdate: 2017-02-01 +lastmod: 2017-02-01 +categories: [contribute] +keywords: [dev,open source] +authors: [digitalcraftsman] +menu: + docs: + parent: "contribute" + weight: 10 +weight: 10 +sections_weight: 10 +draft: false +toc: true +--- + +## Introduction + +Hugo is an open-source project and lives by the work of its [contributors][]. There are plenty of [open issues][issues], and we need your help to make Hugo even more awesome. You don't need to be a Go guru to contribute to the project's development. + +## Assumptions + +This contribution guide takes a step-by-step approach in hopes of helping newcomers. Therefore, we only assume the following: + +* You are new to Git or open-source projects in general +* You are a fan of Hugo and enthusiastic about contributing to the project + +{{% note "Additional Questions?" %}} +If you're struggling at any point in this contribution guide, reach out to the Hugo community in [Hugo's Discussion forum](https://discourse.gohugo.io). +{{% /note %}} + +## Install Go + +The installation of Go should take only a few minutes. You have more than one option to get Go up and running on your machine. + +If you are having trouble following the installation guides for Go, check out [Go Bootcamp, which contains setups for every platform][gobootcamp] or reach out to the Hugo community in the [Hugo Discussion Forums][forums]. + +### Install Go From Source + +[Download the latest stable version of Go][godl] and follow the official [Go installation guide][goinstall]. + +Once you're finished installing Go, let's confirm everything is working correctly. Open a terminal---or command line under Windows--and type the following: + +``` +go version +``` + +You should see something similar to the following written to the console. Note that the version here reflects the most recent version of Go as of the last update for this page: + +``` +go version go1.12 darwin/amd64 +``` + +Next, make sure that you set up your `GOPATH` [as described in the installation guide][setupgopath]. + +You can print the `GOPATH` with `echo $GOPATH`. You should see a non-empty string containing a valid path to your Go workspace; for example: + +``` +/Users//Code/go +``` + +### Install Go with Homebrew + +If you are a MacOS user and have [Homebrew](https://brew.sh/) installed on your machine, installing Go is as simple as the following command: + +{{< code file="install-go.sh" >}} +brew install go +{{< /code >}} + +### Install Go via GVM + +More experienced users can use the [Go Version Manager][gvm] (GVM). GVM allows you to switch between different Go versions *on the same machine*. If you're a beginner, you probably don't need this feature. However, GVM makes it easy to upgrade to a new released Go version with just a few commands. + +GVM comes in especially handy if you follow the development of Hugo over a longer period of time. Future versions of Hugo will usually be compiled with the latest version of Go. Sooner or later, you will have to upgrade if you want to keep up. + +## Create a GitHub Account + +If you're going to contribute code, you'll need to have an account on GitHub. Go to [www.github.com/join](https://github.com/join) and set up a personal account. + +## Install Git on Your System + +You will need to have Git installed on your computer to contribute to Hugo development. Teaching Git is outside the scope of the Hugo docs, but if you're looking for an excellent reference to learn the basics of Git, we recommend the [Git book][gitbook] if you are not sure where to begin. We will include short explanations of the Git commands in this document. + +Git is a [version control system](https://en.wikipedia.org/wiki/Version_control) to track the changes of source code. Hugo depends on smaller third-party packages that are used to extend the functionality. We use them because we don't want to reinvent the wheel. + +Go ships with a sub-command called `get` that will download these packages for us when we setup our working environment. The source code of the packages is tracked with Git. `get` will interact with the Git servers of the package hosters in order to fetch all dependencies. + +Move back to the terminal and check if Git is already installed. Type in `git version` and press enter. You can skip the rest of this section if the command returned a version number. Otherwise [download](https://git-scm.com/downloads) the latest version of Git and follow this [installation guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). + +Finally, check again with `git version` if Git was installed successfully. + +### Git Graphical Front Ends + +There are several [GUI clients](https://git-scm.com/downloads/guis) that help you to operate Git. Not all are available for all operating systems and maybe differ in their usage. Because of this we will document how to use the command line, since the commands are the same everywhere. + +### Install Hub on Your System (Optional) + +Hub is a great tool for working with GitHub. The main site for it is [hub.github.com](https://hub.github.com/). Feel free to install this little Git wrapper. + +On a Mac, you can install [Hub](https://github.com/github/hub) using [Homebrew](https://brew.sh): + +``` +brew install hub +``` + +Now we'll create an [alias in Bash](https://tldp.org/LDP/abs/html/aliases.html) so that typing `git` actually runs `Hub`: + +``` +echo "alias git='hub'" >> ~/.bash_profile +``` + +Confirm the installation: + +``` +git version 2.21.0 +hub version 2.10.0 +``` + +## Set up your working copy + +You set up the working copy of the repository locally on your computer. Your local copy of the files is what you'll edit, compile, and end up pushing back to GitHub. The main steps are cloning the repository and creating your fork as a remote. + +### Clone the repository + +We assume that you've set up your `GOPATH` (see the section above if you're unsure about this). You should now copy the Hugo repository down to your computer. You'll hear this called "clone the repo". GitHub's [help pages](https://help.github.com/articles/cloning-a-repository/) give us a short explanation: + +> When you create a repository on GitHub, it exists as a remote repository. You can create a local clone of your repository on your computer and sync between the two locations. + +We're going to clone the [master Hugo repository](https://github.com/gohugoio/hugo). That seems counter-intuitive, since you won't have commit rights on it. But it's required for the Go workflow. You'll work on a copy of the master and push your changes to your own repository on GitHub. + +So, let's make a new directory and clone that master repository: + +``` +mkdir $HOME/src +cd $HOME/src +git clone https://github.com/gohugoio/hugo.git +``` + +> Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. +> The easiest is to clone Hugo in a directory outside of GOPATH + +And then, install dependencies of Hugo by running the following in the cloned directory: + +``` +cd $HOME/src/hugo +go install +``` + + +Hugo relies on [mage](https://github.com/magefile/mage) for some convenient build and test targets. If you don't already have it, get it: + +``` +go install github.com/magefile/mage@latest +``` + +### Fork the repository + +If you're not familiar with this term, GitHub's [help pages](https://help.github.com/articles/fork-a-repo/) provide again a simple explanation: + +> A fork is a copy of a repository. Forking a repository allows you to freely experiment with changes without affecting the original project. + +#### Fork by hand + +Open the [Hugo repository](https://github.com/gohugoio/hugo) on GitHub and click on the "Fork" button in the top right. + +![Fork button](/images/contribute/development/forking-a-repository.png) + +Now open your fork repository on GitHub and copy the remote url of your fork. You can choose between HTTPS and SSH as protocol that Git should use for the following operations. HTTPS works always [if you're not sure](https://help.github.com/articles/which-remote-url-should-i-use/). + +![Copy remote url](/images/contribute/development/copy-remote-url.png) + +Switch back to the terminal and move into the directory of the cloned master repository from the last step. + +``` +cd $HOME/src/hugo +``` + +Now Git needs to know that our fork exists by adding the copied remote url: + +``` +git remote add +``` + +#### Fork with Hub + +Alternatively, you can use the Git wrapper Hub. Hub makes forking a repository easy: + +``` +git fork +``` + +That command will log in to GitHub using your account, create a fork of the repository that you're currently working in, and add it as a remote to your working copy. + +#### Trust, but verify + +Let's check if everything went right by listing all known remotes: + +``` +git remote -v +``` + +The output should look similar: + +``` +digitalcraftsman git@github.com:digitalcraftsman/hugo.git (fetch) +digitalcraftsman git@github.com:digitalcraftsman/hugo.git (push) +origin https://github.com/gohugoio/hugo (fetch) +origin https://github.com/gohugoio/hugo (push) +``` + +## The Hugo Git Contribution Workflow + +### Create a new branch + +You should never develop against the "master" branch. The development team will not accept a pull request against that branch. Instead, create a descriptive named branch and work on it. + +First, you should always pull the latest changes from the master repository: + +``` +git checkout master +git pull +``` + +Now we can create a new branch for your additions: + +``` +git checkout -b +``` + +You can check on which branch you are with `git branch`. You should see a list of all local branches. The current branch is indicated with a little asterisk. + +### Contribute to Documentation + +Perhaps you want to start contributing to the Hugo docs. If so, you can ignore most of the following steps and focus on the `/docs` directory within your newly cloned repository. You can change directories into the Hugo docs using `cd docs`. + +You can start Hugo's built-in server via `hugo server`. Browse the documentation by entering [http://localhost:1313](http://localhost:1313) in the address bar of your browser. The server automatically updates the page whenever you change content. + +We have developed a [separate Hugo documentation contribution guide][docscontrib] for more information on how the Hugo docs are built, organized, and improved by the generosity of people like you. + +### Build Hugo + +While making changes in the codebase it's a good idea to build the binary to test them: + +``` +mage hugo +``` + +This command generates the binary file at the root of the repository. + +If you want to install the binary in `$GOPATH/bin`, run + +``` +mage install +``` + +### Test +Sometimes changes on the codebase can cause unintended side effects. Or they don't work as expected. Most functions have their own test cases. You can find them in files ending with `_test.go`. + +Make sure the commands + +``` +mage -v check +``` + +passes. + +### Formatting +The Go code styleguide maybe is opinionated but it ensures that the codebase looks the same, regardless who wrote the code. Go comes with its own formatting tool. Let's apply the styleguide to our additions: + +``` +mage fmt +``` + +Once you made your additions commit your changes. Make sure that you follow our [code contribution guidelines](https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md): + +``` +# Add all changed files +git add --all +git commit --message "YOUR COMMIT MESSAGE" +``` + +The commit message should describe what the commit does (e.g. add feature XYZ), not how it is done. + +### Modify commits + +You noticed some commit messages don't fulfill the code contribution guidelines or you just forget something to add some files? No problem. Git provides the necessary tools to fix such problems. The next two methods cover all common cases. + +If you are unsure what a command does leave the commit as it is. We can fix your commits later in the pull request. + +#### Modify the last commit + +Let's say you want to modify the last commit message. Run the following command and replace the current message: + +``` +git commit --amend -m"YOUR NEW COMMIT MESSAGE" +``` + +Take a look at the commit log to see the change: + +``` +git log +# Exit with q +``` + +After making the last commit you may have forgot something. There is no need to create a new commit. Just add the latest changes and merge them into the intended commit: + +``` +git add --all +git commit --amend +``` + +#### Modify multiple commits + +{{% warning "Be Careful Modifying Multiple Commits"%}} +Modifications such as those described in this section can have serious unintended consequences. Skip this section if you're not sure! +{{% /warning %}} + +This is a bit more advanced. Git allows you to [rebase](https://git-scm.com/docs/git-rebase) commits interactively. In other words: it allows you to rewrite the commit history. + +``` +git rebase --interactive @~6 +``` + +The `6` at the end of the command represents the number of commits that should be modified. An editor should open and present a list of last six commit messages: + +``` +pick 80d02a1 tpl: Add hasPrefix to the template funcs' "smoke test" +pick aaee038 tpl: Sort the smoke tests +pick f0dbf2c tpl: Add the other test case for hasPrefix +pick 911c35b Add "How to contribute to Hugo" tutorial +pick 33c8973 Begin workflow +pick 3502f2e Refactoring and typo fixes +``` + +In the case above we should merge the last two commits in the commit of this tutorial (`Add "How to contribute to Hugo" tutorial`). You can "squash" commits, i.e. merge two or more commits into a single one. + +All operations are written before the commit message. Replace "pick" with an operation. In this case `squash` or `s` for short: + +``` +pick 80d02a1 tpl: Add hasPrefix to the template funcs' "smoke test" +pick aaee038 tpl: Sort the smoke tests +pick f0dbf2c tpl: Add the other test case for hasPrefix +pick 911c35b Add "How to contribute to Hugo" tutorial +squash 33c8973 Begin workflow +squash 3502f2e Refactoring and typo fixes +``` + +We also want to rewrite the commits message of the third last commit. We forgot "docs:" as prefix according to the code contribution guidelines. The operation to rewrite a commit is called `reword` (or `r` as shortcut). + +You should end up with a similar setup: + +``` +pick 80d02a1 tpl: Add hasPrefix to the template funcs' "smoke test" +pick aaee038 tpl: Sort the smoke tests +pick f0dbf2c tpl: Add the other test case for hasPrefix +reword 911c35b Add "How to contribute to Hugo" tutorial +squash 33c8973 Begin workflow +squash 3502f2e Refactoring and typo fixes +``` + +Close the editor. It should open again with a new tab. A text is instructing you to define a new commit message for the last two commits that should be merged (aka "squashed"). Save the file with CTRL+S and close the editor again. + +A last time a new tab opens. Enter a new commit message and save again. Your terminal should contain a status message. Hopefully this one: + +``` +Successfully rebased and updated refs/heads/. +``` + +Check the commit log if everything looks as expected. Should an error occur you can abort this rebase with `git rebase --abort`. + +### Push commits + +To push our commits to the fork on GitHub we need to specify a destination. A destination is defined by the remote and a branch name. Earlier, the defined that the remote url of our fork is the same as our GitHub handle, in my case `digitalcraftsman`. The branch should have the same as our local one. This makes it easy to identify corresponding branches. + +``` +git push --set-upstream +``` + +Now Git knows the destination. Next time when you to push commits you just need to enter `git push`. + +If you modified your commit history in the last step GitHub will reject your try to push. This is a safety-feature because the commit history isn't the same and new commits can't be appended as usual. You can enforce this push explicitly with `git push --force`. + +## Open a pull request + +We made a lot of progress. Good work. In this step we finally open a pull request to submit our additions. Open the [Hugo master repository](https://github.com/gohugoio/hugo/) on GitHub in your browser. + +You should find a green button labeled with "New pull request". But GitHub is clever and probably suggests you a pull request like in the beige box below: + +![Open a pull request](/images/contribute/development/open-pull-request.png) + +The new page summaries the most important information of your pull request. Scroll down and you find the additions of all your commits. Make sure everything looks as expected and click on "Create pull request". + +### Accept the contributor license agreement + +Last but not least you should accept the contributor license agreement (CLA). A new comment should be added automatically to your pull request. Click on the yellow badge, accept the agreement and authenticate yourself with your GitHub account. It just takes a few clicks and only needs to be done once. + +![Accept the CLA](/images/contribute/development/accept-cla.png) + +### Automatic builds + +We use a GitHub Actions workflow to build and test. This is a matrix build across combinations of operating system (masOS, Windows, and Ubuntu) and Go versions. The workflow is triggered by the submission of a pull request. If you are a first-time contributor, the workflow requires approval from a project maintainer. + +## Where to start? + +Thank you for reading through this contribution guide. Hopefully, we will see you again soon on GitHub. There are plenty of [open issues][issues] for you to help with. + +Feel free to [open an issue][newissue] if you think you found a bug or you have a new idea to improve Hugo. We are happy to hear from you. + +## Additional References for Learning Git and Go + +* [Codecademy's Free "Learn Git" Course][codecademy] (Free) +* [Code School and GitHub's "Try Git" Tutorial][trygit] (Free) +* [The Git Book][gitbook] (Free) +* [Go Bootcamp][gobootcamp] +* [GitHub Pull Request Tutorial, Thinkful][thinkful] + + +[codecademy]: https://www.codecademy.com/learn/learn-git +[contributors]: https://github.com/gohugoio/hugo/graphs/contributors +[docscontrib]: /contribute/documentation/ +[forums]: https://discourse.gohugo.io +[gitbook]: https://git-scm.com/ - [gobootcamp]: http://www.golangbootcamp.com/book/get_setup ++[gobootcamp]: https://www.golangbootcamp.com/book/get_setup +[godl]: https://golang.org/dl/ +[goinstall]: https://golang.org/doc/install +[gvm]: https://github.com/moovweb/gvm +[issues]: https://github.com/gohugoio/hugo/issues +[newissue]: https://github.com/gohugoio/hugo/issues/new +[releases]: /getting-started/ +[setupgopath]: https://golang.org/doc/code.html#Workspaces +[thinkful]: https://www.thinkful.com/learn/github-pull-request-tutorial/ +[trygit]: https://try.github.io/levels/1/challenges/1 diff --cc docs/content/en/functions/anchorize.md index 87aea235,00000000..b5bd22e0 mode 100644,000000..100644 --- a/docs/content/en/functions/anchorize.md +++ b/docs/content/en/functions/anchorize.md @@@ -1,27 -1,0 +1,27 @@@ +--- +title: anchorize +description: Takes a string and sanitizes it the same way as the [`defaultMarkdownHandler`](https://gohugo.io/getting-started/configuration-markup#configure-markup) does for markdown headers. +date: 2018-10-13 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [markdown,strings] +signature: ["anchorize INPUT"] +hugoversion: "0.39" +workson: [] +relatedfuncs: [humanize] +--- + +If [Goldmark](https://gohugo.io/getting-started/configuration-markup#goldmark) is set as `defaultMarkdownHandler`, the sanitizing logic adheres to the setting [`markup.goldmark.parser.autoHeadingIDType`](https://gohugo.io/getting-started/configuration-markup#goldmark). If [Blackfriday](https://gohugo.io/getting-started/configuration-markup#blackfriday) is set as `defaultMarkdownHandler`, this template function uses the [`SanitizedAnchorName` logic from Blackfriday](https://github.com/russross/blackfriday#sanitized-anchor-names) (the same applies when `markup.goldmark.parser.autoHeadingIDType` is set to `blackfriday`). + +Since the `defaultMarkdownHandler` and this template function use the same sanitizing logic, you can use the latter to determine the ID of a header for linking with anchor tags. + - ``` - {{anchorize "This is a header"}} → "this-is-a-header" - {{anchorize "This is also a header"}} → "this-is-also-a-header" - {{anchorize "main.go"}} → "main-go" - {{anchorize "Article 123"}} → "article-123" - {{anchorize "<- Let's try this, shall we?"}} → "let-s-try-this-shall-we" - {{anchorize "Hello, 世界"}} → "hello-世界" ++```go-html-template ++{{ anchorize "This is a header" }} --> "this-is-a-header" ++{{ anchorize "This is also a header" }} --> "this-is-also----a-header" ++{{ anchorize "main.go" }} --> "maingo" ++{{ anchorize "Article 123" }} --> "article-123" ++{{ anchorize "<- Let's try this, shall we?" }} --> "--lets-try-this-shall-we" ++{{ anchorize "Hello, 世界" }} --> "hello-世界" +``` diff --cc docs/content/en/functions/append.md index 732ffead,00000000..3e2fc13c mode 100644,000000..100644 --- a/docs/content/en/functions/append.md +++ b/docs/content/en/functions/append.md @@@ -1,38 -1,0 +1,33 @@@ +--- +title: append +description: "`append` appends one or more values to a slice and returns the resulting slice." +date: 2018-09-14 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [collections] +signature: ["COLLECTION | append VALUE [VALUE]...", "COLLECTION | append COLLECTION"] +workson: [] +hugoversion: "0.49" +relatedfuncs: [last,first,where,slice] +aliases: [] +--- + +An example appending single values: + +```go-html-template +{{ $s := slice "a" "b" "c" }} +{{ $s = $s | append "d" "e" }} +{{/* $s now contains a []string with elements "a", "b", "c", "d", and "e" */}} + +``` + +The same example appending a slice to a slice: + - +```go-html-template +{{ $s := slice "a" "b" "c" }} +{{ $s = $s | append (slice "d" "e") }} +``` + +The `append` function works for all types, including `Pages`. - - - - diff --cc docs/content/en/functions/apply.md index 3a507c7f,00000000..7550069a mode 100644,000000..100644 --- a/docs/content/en/functions/apply.md +++ b/docs/content/en/functions/apply.md @@@ -1,118 -1,0 +1,118 @@@ +--- +title: apply +description: Given a map, array, or slice, `apply` returns a new slice with a function applied over it. +date: 2017-02-01 +publishdate: 2017-02-01 +lastmod: 2017-02-01 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [advanced] +signature: ["apply COLLECTION FUNCTION [PARAM...]"] +workson: [] +hugoversion: +relatedfuncs: [] +deprecated: false +draft: false +aliases: [] +--- + +{{< todo >}} +POTENTIAL NEW CONTENT: see apply/sequence discussion: https://discourse.gohugo.io/t/apply-printf-on-a-sequence/5722; +{{< /todo >}} + +`apply` expects at least three parameters, depending on the function being applied. + +1. The first parameter is the sequence to operate on. +2. The second parameter is the name of the function as a string, which must be the name of a valid [Hugo function][functions]. +3. After that, the parameters to the applied function are provided, with the string `"."` standing in for each element of the sequence the function is to be applied against. + +Here is an example of a content file with `names:` as a front matter field: + +``` ++++ +names: [ "Derek Perkins", "Joe Bergevin", "Tanner Linsley" ] ++++ +``` + +You can then use `apply` as follows: + +``` +{{ apply .Params.names "urlize" "." }} +``` + +Which will result in the following: + +``` +"derek-perkins", "joe-bergevin", "tanner-linsley" +``` + +This is *roughly* equivalent to using the following with [range][]: + +``` +{{ range .Params.names }}{{ . | urlize }}{{ end }} +``` + +However, it is not possible to provide the output of a range to the [`delimit` function][delimit], so you need to `apply` it. + +If you have `post-tag-list.html` and `post-tag-link.html` as [partials][], you *could* use the following snippets, respectively: + +{{< code file="layouts/partials/post-tag-list.html" copy="false" >}} +{{ with .Params.tags }} +
+ Tags: + {{ $len := len . }} + {{ if eq $len 1 }} - {{ partial "post-tag-link" (index . 0) }} ++ {{ partial "post-tag-link.html" (index . 0) }} + {{ else }} + {{ $last := sub $len 1 }} + {{ range first $last . }} - {{ partial "post-tag-link" . }}, ++ {{ partial "post-tag-link.html" . }}, + {{ end }} - {{ partial "post-tag-link" (index . $last) }} ++ {{ partial "post-tag-link.html" (index . $last) }} + {{ end }} +
+{{ end }} +{{< /code >}} + +{{< code file="layouts/partials/post-tag-link.html" copy="false" >}} + +{{< /code >}} + +This works, but the complexity of `post-tag-list.html` is fairly high. The Hugo template needs to perform special behavior for the case where there’s only one tag, and it has to treat the last tag as special. Additionally, the tag list will be rendered something like `Tags: tag1 , tag2 , tag3` because of the way that the HTML is generated and then interpreted by a browser. + +This first version of `layouts/partials/post-tag-list.html` separates all of the operations for ease of reading. The combined and DRYer version is shown next: + +``` +{{ with .Params.tags }} +
+ Tags: + {{ $sort := sort . }} - {{ $links := apply $sort "partial" "post-tag-link" "." }} ++ {{ $links := apply $sort "partial" "post-tag-link.html" "." }} + {{ $clean := apply $links "chomp" "." }} + {{ delimit $clean ", " }} +
+{{ end }} +``` + +Now in the completed version, you can sort the tags, convert the tags to links with `layouts/partials/post-tag-link.html`, [chomp][] off stray newlines, and join the tags together in a delimited list for presentation. Here is an even DRYer version of the preceding example: + +{{< code file="layouts/partials/post-tag-list.html" download="post-tag-list.html" >}} + {{ with .Params.tags }} +
+ Tags: - {{ delimit (apply (apply (sort .) "partial" "post-tag-link" ".") "chomp" ".") ", " }} ++ {{ delimit (apply (apply (sort .) "partial" "post-tag-link.html" ".") "chomp" ".") ", " }} +
+ {{ end }} +{{< /code >}} + +{{% note %}} +`apply` does not work when receiving the sequence as an argument through a pipeline. +{{% /note %}} + +[chomp]: /functions/chomp/ "See documentation for the chomp function" +[delimit]: /functions/delimit/ "See documentation for the delimit function" +[functions]: /functions/ "See the full list of Hugo functions to see what can be passed as an argument to the apply function." +[partials]: /templates/partials/ +[range]: /functions/range/ "Learn the importance of the range function, a fundamental keyword in both Hugo templates and the Go programming language." diff --cc docs/content/en/functions/group.md index e1a22ef5,00000000..203498cb mode 100644,000000..100644 --- a/docs/content/en/functions/group.md +++ b/docs/content/en/functions/group.md @@@ -1,37 -1,0 +1,31 @@@ +--- +title: group +description: "`group` groups a list of pages." +date: 2018-09-14 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [collections] +signature: ["PAGES | group KEY"] +hugoversion: "0.49" +--- + +{{< code file="layouts/partials/groups.html" >}} +{{ $new := .Site.RegularPages | first 10 | group "New" }} +{{ $old := .Site.RegularPages | last 10 | group "Old" }} +{{ $groups := slice $new $old }} +{{ range $groups }} +

{{ .Key }}{{/* Prints "New", "Old" */}}

+
    + {{ range .Pages }} +
  • + {{ .Title }} +
    {{ .Date.Format "Mon, Jan 2, 2006" }}
    +
  • + {{ end }} +
+{{ end }} +{{< /code >}} + - - +The page group you get from `group` is of the same type you get from the built-in [group methods](/templates/lists#group-content) in Hugo. The above example can even be [paginated](/templates/pagination/#list-paginator-pages). - - - - diff --cc docs/content/en/functions/hugo.md index 7602a2e3,00000000..a495eae0 mode 100644,000000..100644 --- a/docs/content/en/functions/hugo.md +++ b/docs/content/en/functions/hugo.md @@@ -1,116 -1,0 +1,115 @@@ +--- +title: hugo +linktitle: hugo +description: The `hugo` function provides easy access to Hugo-related data. +date: 2019-01-31 +publishdate: 2019-01-31 - lastmod: 2019-01-31 +keywords: [] +categories: [functions] +menu: + docs: + parent: "functions" +toc: +signature: ["hugo"] +workson: [] +hugoversion: +relatedfuncs: [] +deprecated: false +draft: false +aliases: [] +--- + +`hugo` returns an instance that contains the following functions: + +hugo.Generator +: `` tag for the version of Hugo that generated the site. `hugo.Generator` outputs a *complete* HTML tag; e.g. `` + +hugo.Version +: the current version of the Hugo binary you are using e.g. `0.63.2` + + +`hugo` returns an instance that contains the following functions: + +hugo.Environment +: the current running environment as defined through the `--environment` cli tag + +hugo.CommitHash +: the git commit hash of the current Hugo binary e.g. `0e8bed9ccffba0df554728b46c5bbf6d78ae5247` + +hugo.BuildDate +: the compile date of the current Hugo binary formatted with RFC 3339 e.g. `2002-10-02T10:00:00-05:00` + +hugo.IsExtended {{< new-in "0.83.0" >}} +: whether this is the extended Hugo binary. + +hugo.IsProduction +: returns true if `hugo.Environment` is set to the production environment + +{{% note "Use the Hugo Generator Tag" %}} +We highly recommend using `hugo.Generator` in your website's ``. `hugo.Generator` is included by default in all themes hosted on [themes.gohugo.io](https://themes.gohugo.io). The generator tag allows the Hugo team to track the usage and popularity of Hugo. +{{% /note %}} + +hugo.Deps +: See [hugo.Deps](#hugodeps) + + +## hugo.Deps + +{{< new-in "0.92.0" >}} + +`hugo.Deps` returns a list of dependencies for a project (either Hugo Modules or local theme components). + - Eeach dependency contains: ++Each dependency contains: + +Path (string) +: Returns the path to this module. This will either be the module path, e.g. "github.com/gohugoio/myshortcodes", or the path below your /theme folder, e.g. "mytheme". + +Version (string) +: The module version. - ++ +Vendor (bool) +: Whether this dependency is vendored. - ++ +Time (time.Time) +: Time version was created. + +Owner +: In the dependency tree, this is the first module that defines this module as a dependency. + +Replace (*Dependency) +: Replaced by this dependency. + +An example table listing the dependencies: + +```html +

Dependencies

+ + + + + + + + + + + + + {{ range $index, $element := hugo.Deps }} + + + + + + + + + {{ end }} + +
#OwnerPathVersionTimeVendor
{{ add $index 1 }}{{ with $element.Owner }}{{.Path }}{{ end }} + {{ $element.Path }} + {{ with $element.Replace}} + => {{ .Path }} + {{ end }} + {{ $element.Version }}{{ with $element.Time }}{{ . }}{{ end }}{{ $element.Vendor }}
+``` diff --cc docs/content/en/functions/relurl.md index 71eefb01,00000000..af98c44f mode 100644,000000..100644 --- a/docs/content/en/functions/relurl.md +++ b/docs/content/en/functions/relurl.md @@@ -1,50 -1,0 +1,49 @@@ +--- +title: relURL +description: Creates a baseURL-relative URL. +date: 2017-02-01 +publishdate: 2017-02-01 - lastmod: 2017-02-01 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [urls] +signature: ["relURL INPUT"] +workson: [] +hugoversion: +relatedfuncs: [absURL] +deprecated: false +aliases: [] +--- + +Both `absURL` and `relURL` consider the configured value of `baseURL` in your site's [`config` file][configuration]. Given a `baseURL` set to `https://example.com/hugo/`: + +``` +{{ "mystyle.css" | absURL }} → "https://example.com/hugo/mystyle.css" +{{ "mystyle.css" | relURL }} → "/hugo/mystyle.css" +{{ "http://gohugo.io/" | relURL }} → "http://gohugo.io/" +{{ "http://gohugo.io/" | absURL }} → "http://gohugo.io/" +``` + +The last two examples may look strange but can be very useful. For example, the following shows how to use `absURL` in [JSON-LD structured data for SEO][jsonld] where some of your images for a piece of content may or may not be hosted locally: + +{{< code file="layouts/partials/schemaorg-metadata.html" download="schemaorg-metadata.html" >}} + +{{< /code >}} + +The above uses the [apply function][] and also exposes how the Go template parser JSON-encodes objects inside `